Skip to content

Commit

Permalink
FIX #1
Browse files Browse the repository at this point in the history
  • Loading branch information
LolaLollipop committed Jan 4, 2024
1 parent 9570d7b commit da78773
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 74 deletions.
4 changes: 2 additions & 2 deletions RueI/RueI/CharacterLengths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
using System.Runtime.CompilerServices;

/// <summary>
/// Provides a variety of constant values.
/// Provides lengths for characters in hints.
/// </summary>
/// <remarks>This class is mosty designed for internal use within RueI. However, they can still be useful for external use.</remarks>
/// <remarks>This class is mosty designed for internal use within RueI. However, it can still be useful for external use.</remarks>
public static class CharacterLengths
{
/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions RueI/RueI/Displays/AutoGiving/AutoGivers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
using RueI.Elements;

/// <summary>
/// Manages and automatically assigns elements to <see cref="ReferenceHub"/>s meeting a criteria.
/// Manages and automatically assigns elements to <see cref="DisplayCore"/> instances meeting a criteria.
/// </summary>
public class AutoElement
{
private record PeriodicUpdate(TimeSpan time, int priority, JobToken token);

private const int AUTOUPDATEPRIORITY = 5;

private static readonly List<AutoElement> AutoGivers = new();

private readonly Element? element;
Expand Down Expand Up @@ -161,7 +163,7 @@ private static void OnRoleChanged(ReferenceHub hub, PlayerRoleBase prevRole, Pla
}
}

core.Update(35);
core.Update(AUTOUPDATEPRIORITY);
}
}

Expand Down
2 changes: 1 addition & 1 deletion RueI/RueI/Displays/AutoGiving/Roles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public enum Roles
/// <summary>
/// Gets the Facility Guard role id.
/// </summary>
FacilityGuard = 1 << RoleTypeId.Tutorial,
FacilityGuard = 1 << RoleTypeId.FacilityGuard,

/// <summary>
/// Gets the SCP-939 role id.
Expand Down
8 changes: 4 additions & 4 deletions RueI/RueI/Displays/DisplayCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ public static IElemReference<T> GetReference<T>()
/// <summary>
/// Updates this <see cref="DisplayCore"/>.
/// </summary>
/// <param name="priority">The priority of the update - defaults to 100.</param>
public void Update(int priority = 100)
/// <param name="priority">The priority of the update - defaults to 10.</param>
public void Update(int priority = 10)
{
if (IgnoreUpdate)
{
return;
}

Scheduler.Schedule(TimeSpan.Zero, () => { }, priority);
Scheduler.ScheduleUpdate(TimeSpan.Zero, priority);
}

/// <summary>
Expand All @@ -113,7 +113,7 @@ public void Update(int priority = 100)
}
else
{
return default;
return null;
}
}

Expand Down
123 changes: 70 additions & 53 deletions RueI/RueI/Displays/Scheduling/Scheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,49 @@
using eMEC;
using RueI.Displays.Scheduling.Records;
using RueI.Extensions;
using UnityEngine;

/// <summary>
/// Provides a means of doing batch operations.
/// </summary>
/// <remarks>
/// The <see cref="Scheduler"/> is a powerful class that enables "batch operations". This means that multiple updates to a display can happen at once, helping to avoid the hint ratelimit.
/// More detailed information is available at <see href="https://ruemena.github.io/RueI/markdown/scheduling.html">Using the Scheduler</see>.
/// </remarks>
public class Scheduler
{
private static readonly TimeSpan MinimumBatch = TimeSpan.FromMilliseconds(625);

private readonly Cooldown rateLimiter = new();
private readonly Cooldown hintRateLimit = new();
private readonly List<ScheduledJob> jobs = new();

private readonly UpdateTask performTask = new();

private readonly Queue<BatchJob> currentBatches = new(4);
private readonly DisplayCore core;

private BatchJob? nextBatch;

/// <summary>
/// Initializes a new instance of the <see cref="Scheduler"/> class.
/// </summary>
/// <param name="coordinator">The <see cref="DisplayCore"/> to use.</param>
public Scheduler(DisplayCore coordinator)
/// <param name="core">The <see cref="DisplayCore"/> to use.</param>
public Scheduler(DisplayCore core)
{
this.core = coordinator;
this.core = core;
}

/// <summary>
/// Gets a value indicating whether or not the rate limit is currently active.
/// </summary>
internal bool RateLimitActive => rateLimiter.Active;
internal bool RateLimitActive => hintRateLimit.Active;

/// <summary>
/// Gets the <see cref="DateTimeOffset"/> used by <see cref="Scheduler"/> classes for the current timeSChedued.
/// </summary>
private static DateTimeOffset Now => DateTimeOffset.UtcNow;

/// <summary>
/// Calculates the weighted time for a list of jobs to be performed.
/// </summary>
/// <param name="jobs">The jobs.</param>
/// <param name="jobs">The <see cref="ScheduledJob"/> operations to schedule.</param>
/// <returns>The weighted <see cref="DateTimeOffset"/> of all of the jobs.</returns>
public static DateTimeOffset CalculateWeighted(IEnumerable<ScheduledJob> jobs)
{
Expand All @@ -66,7 +69,7 @@ public static DateTimeOffset CalculateWeighted(IEnumerable<ScheduledJob> jobs)
}

/// <summary>
/// Schedules a job.
/// Schedules a <see cref="ScheduledJob"/>.
/// </summary>
/// <param name="job">The job to schedule.</param>
public void Schedule(ScheduledJob job)
Expand All @@ -76,35 +79,51 @@ public void Schedule(ScheduledJob job)
}

/// <summary>
/// Schedules multiple jobs.
/// Schedules multiple <see cref="ScheduledJob"/> operations.
/// </summary>
/// <param name="job">The first job to schedule.</param>
/// <param name="jobs">The rest of the jobs to schedule.</param>
public void Schedule(ScheduledJob job, params ScheduledJob[] jobs)
/// <param name="jobs">The jobs to schedule.</param>
/// <remarks>
/// When scheduling multiple jobs at a time, this method is preferred to calling <see cref="Schedule(ScheduledJob)"/> several
/// times since it only recalculates the batches once.
/// </remarks>
public void Schedule(IEnumerable<ScheduledJob> jobs)
{
ScheduleNoUpdate(job);

foreach (ScheduledJob newJob in jobs)
// since we must check every job for duplicates using the JobToken,
// AddRange or similar can't be used
foreach (ScheduledJob job in jobs)
{
ScheduleNoUpdate(newJob);
ScheduleNoUpdate(job);
}

UpdateBatches();
}

/// <summary>
/// Schedules an uncancellable update job.
/// Schedules multiple <see cref="ScheduledJob"/> operations.
/// </summary>
/// <param name="job">The first <see cref="ScheduledJob"/> to schedule.</param>
/// <param name="jobs">The rest of the <see cref="ScheduledJob"/> operations to schedule.</param>
/// <inheritdoc cref="Schedule(IEnumerable{ScheduledJob})" path="/remarks"/>
public void Schedule(ScheduledJob job, params ScheduledJob[] jobs)
{
ScheduleNoUpdate(job);

Schedule(jobs); // unnecessary to call UpdateBatches, since this already does it
}

/// <summary>
/// Schedules an uncancellable update <see cref="ScheduledJob"/>.
/// </summary>
/// <param name="time">How long into the future to update at.</param>
/// <param name="priority">The priority of the job, giving it additional weight when calculating.</param>
/// <param name="priority">The priority of the <see cref="ScheduledJob"/>, giving it additional weight when calculating.</param>
public void ScheduleUpdate(TimeSpan time, int priority)
{
jobs.Add(new(Now + time, () => { }, priority));
UpdateBatches();
}

/// <summary>
/// Schedules a job.
/// Schedules a new <see cref="ScheduledJob"/>.
/// </summary>
/// <param name="time">How long into the future to run the action at.</param>
/// <param name="action">The <see cref="Action"/> to run.</param>
Expand All @@ -116,19 +135,7 @@ public void Schedule(TimeSpan time, Action action, int priority, JobToken? token
}

/// <summary>
/// Schedules a job.
/// </summary>
/// <param name="action">The <see cref="Action"/> to run.</param>
/// <param name="time">How long into the future to run the action at.</param>
/// <param name="priority">The priority of the job, giving it additional weight when calculating.</param>
/// <param name="token">An optional token to assign to the <see cref="ScheduledJob"/>.</param>
public void Schedule(Action action, TimeSpan time, int priority, JobToken? token = null)
{
Schedule(new ScheduledJob(Now + time, action, priority, token));
}

/// <summary>
/// Schedules a job with a priority of 1.
/// Schedules a <see cref="ScheduledJob"/> with a priority of 1.
/// </summary>
/// <param name="time">How long into the future to run the action at.</param>
/// <param name="action">The <see cref="Action"/> to run.</param>
Expand All @@ -139,7 +146,7 @@ public void Schedule(TimeSpan time, Action action, JobToken? token = null)
}

/// <summary>
/// Attempts to kill the job with the <see cref="JobToken"/>.
/// Attempts to kill the <see cref="ScheduledJob"/> with the <see cref="JobToken"/>.
/// </summary>
/// <param name="token">The <see cref="JobToken"/> to use as a reference.</param>
public void KillJob(JobToken token)
Expand All @@ -157,9 +164,12 @@ public void KillJob(JobToken token)
/// <param name="time">The amount of time to delay for.</param>
internal void Delay(TimeSpan time)
{
rateLimiter.Start(time.Max(MinimumBatch));
hintRateLimit.Start(time.Max(MinimumBatch));
}

/// <summary>
/// Recalculates the next <see cref="BatchJob"/> for this <see cref="Scheduler"/>, and potentially starts it.
/// </summary>
private void UpdateBatches()
{
if (!jobs.Any())
Expand All @@ -168,7 +178,7 @@ private void UpdateBatches()
}

jobs.Sort();
currentBatches.Clear();
nextBatch = null;

List<ScheduledJob> currentBatch = new(2);
DateTimeOffset currentBatchTime = jobs.First().FinishAt + MinimumBatch;
Expand All @@ -181,49 +191,56 @@ private void UpdateBatches()
}
else
{
BatchJob finishedBatch = new(currentBatch, CalculateWeighted(currentBatch));
currentBatches.Enqueue(finishedBatch);
currentBatch = new()
{
job,
};
break;
}
}

if (currentBatch.Count != 0)
if (currentBatch.Count == 0)
{
BatchJob finishedBatch = new(currentBatch, CalculateWeighted(currentBatch));

currentBatches.Enqueue(finishedBatch);
// handle cases where a scheduledjob being removed
// results in nothing to do
performTask.End();
}
else
{
DateTimeOffset dateTimePerform = CalculateWeighted(currentBatch);
nextBatch = new(currentBatch, dateTimePerform);

TimeSpan performAt = (currentBatches.Peek().PerformAt - Now).MaxIf(rateLimiter.Active, rateLimiter.TimeLeft);
TimeSpan performAt = (dateTimePerform - Now).MaxIf(hintRateLimit.Active, hintRateLimit.TimeLeft);

performTask.Start(performAt, PerformFirstBatch);
performTask.Start(performAt, PerformFirstBatch);
}
}

/// <summary>
/// Immediately performs the first batch job.
/// Immediately performs the first <see cref="BatchJob"/>.
/// </summary>
private void PerformFirstBatch()
{
BatchJob batchJob = currentBatches.Dequeue();
if (nextBatch == null)
{
return;
}

core.IgnoreUpdate = true;
foreach (ScheduledJob job in batchJob.Jobs)
foreach (ScheduledJob job in nextBatch.Jobs)
{
jobs.Remove(job);
job.Action();
}

core.IgnoreUpdate = false;

rateLimiter.Start(Constants.HintRateLimit);
hintRateLimit.Start(Constants.HintRateLimit);

core.InternalUpdate();
UpdateBatches();
}

/// <summary>
/// Schedules a job without recalculating the batches.
/// </summary>
/// <param name="job">The <see cref="ScheduledJob"/> to schedule.</param>
private void ScheduleNoUpdate(ScheduledJob job)
{
if (job.Token == null || !jobs.Any(x => x.Token == job.Token))
Expand Down
10 changes: 1 addition & 9 deletions RueI/RueI/Displays/ScreenDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ public void SetScreen(Screen screen)
/// <inheritdoc/>
public override IEnumerable<Element> GetAllElements()
{
foreach (Element element in CurrentScreen.Elements.FilterDisabled())
{
yield return element;
}

foreach (Element element in GlobalElements.FilterDisabled())
{
yield return element;
}
return CurrentScreen.Elements.FilterDisabled().Concat(GlobalElements.FilterDisabled());
}
}
1 change: 0 additions & 1 deletion RueI/RueI/Elements/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace RueI.Elements;
/// <remarks>
/// An <see cref="Element"/> is how text is displayed within RueI. Each <see cref="Element"/>
/// acts like an individual <see cref="Hints.Hint"/>, and cannot influence other <see cref="Element"/>s.
///
/// </remarks>
public abstract class Element
{
Expand Down
2 changes: 1 addition & 1 deletion RueI/RueI/Elements/Enums/ElementOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum ElementOptions
/// <summary>
/// Indicates whether or not to automatically use functional positioning for the element.
/// </summary>
/// <see cref="Ruetility.FunctionalToScaledPosition(float)"/>
/// <seealso cref="Ruetility.FunctionalToScaledPosition(float)"/>
UseFunctionalPosition = 1 << 2,

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion RueI/RueI/Patches/HintPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
public static class HintPatch
{
private const float MAXANONYMOUSHINTTIME = 3;
private const int UPDATEPRIORITY = 10;

private delegate bool TryGetHub(GameObject player, out ReferenceHub hub);

Expand Down Expand Up @@ -100,7 +101,7 @@ public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructio
// core.Scheduler.ScheduleUpdate(TimeSpan.FromSeconds(time), 250);
new(OpCodes.Conv_R8),
new(OpCodes.Call, Method(typeof(TimeSpan), nameof(TimeSpan.FromSeconds))),
new(OpCodes.Ldc_I4, 250),
new(OpCodes.Ldc_I4, UPDATEPRIORITY),
new(OpCodes.Callvirt, Method(typeof(Scheduler), nameof(Scheduler.ScheduleUpdate))),

// }
Expand Down

0 comments on commit da78773

Please sign in to comment.