diff --git a/RueI/RueI.csproj b/RueI/RueI.csproj index 1c3841d..ed27197 100644 --- a/RueI/RueI.csproj +++ b/RueI/RueI.csproj @@ -1,99 +1,89 @@  - - - net48 - latest - enable - enable - AnyCPU;x64 - RueI - CC0 1.0 - 2.0.4 - Rue <3, Override (some help) - True - True - universal hint framework for scp:sl - https://github.com/Ruemena/RueI - https://github.com/Ruemena/RueI - scpsl;hints;scp;exiled;nwapi - 2.0.4 - 2.0.4 - README.md - LICENSE - False - various fixes, improvements to docs, new reflection helpers, and general cleanup - - - - True - portable - - - - True - portable - - - - portable - - - - portable - - - - - + + net48 + latest + enable + enable + AnyCPU;x64 + RueI + CC0 1.0 + 2.0.4 + Rue <3, Override (some help) + True + True + universal hint framework for scp:sl + https://github.com/Ruemena/RueI + https://github.com/Ruemena/RueI + scpsl;hints;scp;exiled;nwapi + 2.0.4 + 2.0.4 + README.md + LICENSE + False + various fixes, improvements to docs, new reflection helpers, and general cleanup + - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - ../References/Assembly-CSharp.dll - - - ../References/Assembly-CSharp-firstpass.dll - - - ../References/Mirror.dll - - - ../References/NorthwoodLib.dll - - - ../References/PluginAPI.dll - - - ../References/UnityEngine.dll - - - ../References/UnityEngine.CoreModule.dll - - - ../References/System.Collections.Immutable.dll - - - - - - True - \ - - - True - \ - - - + + True + portable + + + True + portable + + + portable + + + portable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + ../References/Assembly-CSharp.dll + + + ../References/Assembly-CSharp-firstpass.dll + + + ../References/Mirror.dll + + + ../References/NorthwoodLib.dll + + + ../References/PluginAPI.dll + + + ../References/UnityEngine.dll + + + ../References/UnityEngine.CoreModule.dll + + + ../References/System.Collections.Immutable.dll + + + + + True + \ + + + True + \ + + \ No newline at end of file diff --git a/RueI/RueI/Displays/AutoElements/AutoElements.cs b/RueI/RueI/Displays/AutoElements/AutoElement.cs similarity index 74% rename from RueI/RueI/Displays/AutoElements/AutoElements.cs rename to RueI/RueI/Displays/AutoElements/AutoElement.cs index d43a251..e80890b 100644 --- a/RueI/RueI/Displays/AutoElements/AutoElements.cs +++ b/RueI/RueI/Displays/AutoElements/AutoElement.cs @@ -18,7 +18,12 @@ /// public class AutoElement { - private record PeriodicUpdate(TimeSpan time, int priority, JobToken token); + /// + /// Represents a periodic update for an . + /// + /// How often the should schedule an auto-update. + /// The priority of the scheduled job. + public record PeriodicUpdate(TimeSpan time, int priority = 10); private const int AUTOUPDATEPRIORITY = 5; @@ -29,7 +34,7 @@ private record PeriodicUpdate(TimeSpan time, int priority, JobToken token); private readonly IElemReference reference = DisplayCore.GetReference(); - private PeriodicUpdate? periodicUpdate; + private (PeriodicUpdate update, JobToken token)? autoUpdate = null; static AutoElement() { @@ -68,23 +73,32 @@ public AutoElement(Roles roles, Element element) public Roles Roles { get; set; } /// - /// Disables this . + /// Gets or sets a indicating how often this should + /// schedule an update for players with the element, or null if it should not auto-update. /// - public virtual void Disable() + public PeriodicUpdate? UpdateEvery { - AutoElements.Remove(this); + get => autoUpdate?.update; + + set + { + if (value != null) + { + autoUpdate = (value, new()); + } + else + { + autoUpdate = null; + } + } } /// - /// Schedules an update for all players with one of the every . + /// Disables this . /// - /// How often to schedule an update. - /// The priority of the update. - /// A reference to this . - public AutoElement UpdateEvery(TimeSpan span, int priority = 35) + public virtual void Disable() { - periodicUpdate = new(span, priority, new()); - return this; + AutoElements.Remove(this); } /// @@ -102,9 +116,9 @@ protected virtual void GiveTo(DisplayCore core) core.AddAsReference(reference, creator!(core)); } - if (periodicUpdate != null) + if (autoUpdate != null) { - ScheduleUpdate(core, periodicUpdate); + ScheduleUpdate(core, autoUpdate.Value); } } @@ -116,9 +130,9 @@ protected virtual void RemoveFrom(DisplayCore core) { core.RemoveReference(reference); - if (periodicUpdate != null) + if (autoUpdate != null) { - core.Scheduler.KillJob(periodicUpdate.token); + core.Scheduler.KillJob(autoUpdate.Value.token); } } @@ -144,8 +158,8 @@ private static void OnRoleChanged(ReferenceHub hub, PlayerRoleBase prevRole, Pla } } - private static void ScheduleUpdate(DisplayCore core, PeriodicUpdate update) + private static void ScheduleUpdate(DisplayCore core, (PeriodicUpdate update, JobToken token) autoUpdate) { - core.Scheduler.Schedule(update.time, () => ScheduleUpdate(core, update), update.token); + core.Scheduler.Schedule(autoUpdate.update.time, () => ScheduleUpdate(core, autoUpdate), autoUpdate.token); } } \ No newline at end of file diff --git a/RueI/RueI/Displays/DisplayCore.cs b/RueI/RueI/Displays/DisplayCore.cs index 21a6408..236387b 100644 --- a/RueI/RueI/Displays/DisplayCore.cs +++ b/RueI/RueI/Displays/DisplayCore.cs @@ -1,7 +1,7 @@ namespace RueI.Displays; -using RueI.Elements; using RueI.Displays.Scheduling; +using RueI.Elements; using RueI.Extensions; /// @@ -41,14 +41,14 @@ protected DisplayCore(ReferenceHub hub) public Scheduler Scheduler { get; } /// - /// Gets a dictionary containing the DisplayCores for each ReferenceHub. + /// Gets the that this display is for. /// - internal static Dictionary DisplayCores { get; } = new(); + public ReferenceHub Hub { get; } /// - /// Gets the that this display is for. + /// Gets a dictionary containing the DisplayCores for each ReferenceHub. /// - internal ReferenceHub Hub { get; } + internal static Dictionary DisplayCores { get; } = new(); /// /// Gets or sets a value indicating whether or not updates will currently be ignored. diff --git a/RueI/RueI/Displays/ElemCombiner.cs b/RueI/RueI/Displays/ElemCombiner.cs index 68df15b..7e48369 100644 --- a/RueI/RueI/Displays/ElemCombiner.cs +++ b/RueI/RueI/Displays/ElemCombiner.cs @@ -4,8 +4,8 @@ using NorthwoodLib.Pools; -using RueI.Extensions; using RueI.Elements; +using RueI.Extensions; using RueI.Parsing.Records; /// @@ -32,6 +32,7 @@ public static string Combine(IEnumerable enumElems) } StringBuilder sb = StringBuilderPool.Shared.Rent(); + float totalOffset = 0; float lastPosition = 0; @@ -54,7 +55,7 @@ public static string Combine(IEnumerable enumElems) if (i != 0) { float calcedOffset = CalculateOffset(lastPosition, lastOffset, funcPos); - sb.Append($"\n"); + sb.Append($"\n"); totalOffset += calcedOffset; } else @@ -70,8 +71,12 @@ public static string Combine(IEnumerable enumElems) } ListPool.Shared.Return(elements); - sb.Insert(0, $"\n"); - sb.Append(Constants.ZeroWidthSpace); + sb.Insert(0, $"\n."); + + // a zero width space is appended here to ensure that trailing newlines still occur + // since this is after all tags have been closed, its guaranteed to not + // do anything at all except stop trailing newlines + sb.Append("."); return StringBuilderPool.Shared.ToStringReturn(sb); } diff --git a/RueI/RueI/Displays/Scheduling/JobToken.cs b/RueI/RueI/Displays/Scheduling/JobToken.cs index 03d6a47..5253d42 100644 --- a/RueI/RueI/Displays/Scheduling/JobToken.cs +++ b/RueI/RueI/Displays/Scheduling/JobToken.cs @@ -1,12 +1,14 @@ namespace RueI.Displays.Scheduling; /// -/// Represents a reference to any number of . +/// Represents a reference to any number of . /// /// -/// A provides a unique identifier for a within any number of s. In other words, a can reference multiple (or no) , but only a single with the given can exist in a . +/// A provides a unique identifier for a within any number of +/// s. In other words, a can reference multiple (or no) jobs, +/// but only a single job with the given can exist in a . /// -/// +/// public class JobToken { /// diff --git a/RueI/RueI/Displays/Scheduling/Records/BatchJob.cs b/RueI/RueI/Displays/Scheduling/Records/BatchJob.cs index c5e05bd..21eb911 100644 --- a/RueI/RueI/Displays/Scheduling/Records/BatchJob.cs +++ b/RueI/RueI/Displays/Scheduling/Records/BatchJob.cs @@ -3,7 +3,8 @@ /// /// Defines a number of s that will performed at a certain time. /// -internal class BatchJob +/// +internal record BatchJob { /// /// Initializes a new instance of the class. diff --git a/RueI/RueI/Displays/Scheduling/Records/ScheduledJob.cs b/RueI/RueI/Displays/Scheduling/ScheduledJob.cs similarity index 94% rename from RueI/RueI/Displays/Scheduling/Records/ScheduledJob.cs rename to RueI/RueI/Displays/Scheduling/ScheduledJob.cs index c8b3948..731858b 100644 --- a/RueI/RueI/Displays/Scheduling/Records/ScheduledJob.cs +++ b/RueI/RueI/Displays/Scheduling/ScheduledJob.cs @@ -1,4 +1,4 @@ -namespace RueI.Displays.Scheduling.Records; +namespace RueI.Displays.Scheduling; using RueI.Extensions; @@ -37,7 +37,7 @@ internal ScheduledJob(DateTimeOffset finishAt, Action action, int priority, JobT /// /// Gets the priority of the element. /// - internal int Priority { get; private set; } = 1; + internal int Priority { get; private set; } /// /// Gets the of this, if it has one. diff --git a/RueI/RueI/Displays/Scheduling/Scheduler.cs b/RueI/RueI/Displays/Scheduling/Scheduler.cs index abb24de..dc1a900 100644 --- a/RueI/RueI/Displays/Scheduling/Scheduler.cs +++ b/RueI/RueI/Displays/Scheduling/Scheduler.cs @@ -15,13 +15,15 @@ /// public class Scheduler { + private static readonly Action EmptyAction = () => { }; private static readonly TimeSpan MinimumBatch = TimeSpan.FromMilliseconds(625); + private readonly DisplayCore core; + private readonly Cooldown hintRateLimit = new(); private readonly List jobs = new(); private readonly UpdateTask performTask = new(); - private readonly DisplayCore core; private BatchJob? nextBatch; @@ -29,7 +31,7 @@ public class Scheduler /// Initializes a new instance of the class. /// /// The to use. - public Scheduler(DisplayCore core) + internal Scheduler(DisplayCore core) { this.core = core; } @@ -118,10 +120,21 @@ public void Schedule(ScheduledJob job, params ScheduledJob[] jobs) /// The priority of the , giving it additional weight when calculating. public void ScheduleUpdate(TimeSpan time, int priority) { - jobs.Add(new(Now + time, () => { }, priority)); + jobs.Add(new(Now + time, EmptyAction, priority)); UpdateBatches(); } + /// + /// Schedules an update with the . + /// + /// How long into the future to update at. + /// The priority of the , giving it additional weight when calculating. + /// A token to assign to the . + public void ScheduleUpdateToken(TimeSpan time, int priority, JobToken token) + { + Schedule(new ScheduledJob(Now + time, EmptyAction, priority, token)); + } + /// /// Schedules a new . /// diff --git a/RueI/RueI/Displays/Screen.cs b/RueI/RueI/Displays/Screen.cs index 028330c..1c27303 100644 --- a/RueI/RueI/Displays/Screen.cs +++ b/RueI/RueI/Displays/Screen.cs @@ -1,7 +1,7 @@ namespace RueI.Displays; -using RueI.Elements; using RueI.Displays.Interfaces; +using RueI.Elements; /// /// Represents a inside a . diff --git a/RueI/RueI/Displays/ScreenDisplay.cs b/RueI/RueI/Displays/ScreenDisplay.cs index cc0c48e..94a4632 100644 --- a/RueI/RueI/Displays/ScreenDisplay.cs +++ b/RueI/RueI/Displays/ScreenDisplay.cs @@ -1,7 +1,7 @@ namespace RueI.Displays; -using RueI.Extensions; using RueI.Elements; +using RueI.Extensions; /// /// Represents a display attached to a with support for s. diff --git a/RueI/RueI/Extensions/ElementHelpers.cs b/RueI/RueI/Extensions/ElementHelpers.cs index 5ebcd92..a51a19f 100644 --- a/RueI/RueI/Extensions/ElementHelpers.cs +++ b/RueI/RueI/Extensions/ElementHelpers.cs @@ -1,7 +1,6 @@ namespace RueI.Extensions; using RueI.Elements; -using RueI.Displays.Interfaces; /// /// Provides extensions and helpers for working with elements. @@ -13,7 +12,20 @@ public static class ElementHelpers /// /// The elements to filter. /// The filtered . - public static IEnumerable FilterDisabled(this IEnumerable elements) => elements.Where(x => x.Enabled); + public static IEnumerable FilterDisabled(this IEnumerable elements) + { + using IEnumerator enumerator = elements.GetEnumerator(); + + while (enumerator.MoveNext()) + { + Element element = enumerator.Current; + + if (element.Enabled) + { + yield return element; + } + } + } /// /// Gets the functional (un-scaled) position of an element. diff --git a/RueI/RueI/Extensions/IComparableExtensions.cs b/RueI/RueI/Extensions/IComparableExtensions.cs index b6b3aa9..0a88ed6 100644 --- a/RueI/RueI/Extensions/IComparableExtensions.cs +++ b/RueI/RueI/Extensions/IComparableExtensions.cs @@ -30,12 +30,12 @@ public static T Max(this T first, T second) /// /// The type to use. /// The first value. - /// Whether or not to return the first value. + /// Whether or not to perform the check. /// The second value. /// The maximum of the two, or the first value if the bool is true. - public static T MaxIf(this T first, bool skip, T second) + public static T MaxIf(this T first, bool check, T second) where T : IComparable { - return skip ? first : first.Max(second); + return check ? first.Max(second) : first; } } \ No newline at end of file diff --git a/RueI/RueI/Parsing/Parser.cs b/RueI/RueI/Parsing/Parser.cs index d563319..caec303 100644 --- a/RueI/RueI/Parsing/Parser.cs +++ b/RueI/RueI/Parsing/Parser.cs @@ -154,15 +154,6 @@ public static float CalculateCharacterLength(TextInfo context, char ch) /// Whether or not the line break was caused by an overflow. public static void CreateLineBreak(ParserContext context, bool isOverflow = false) { - if (context.LineHasAnyChars) - { - context.NewOffset += CalculateSizeOffset(context.BiggestCharSize); - } - else - { - context.NewOffset += CalculateSizeOffset(Constants.DEFAULTSIZE); - } - if (context.WidthSinceSpace > context.FunctionalWidth) { context.CurrentLineWidth = 0; @@ -444,10 +435,6 @@ void FailTagMatch() // not a tag, unload buffer } // foreach context.ApplyClosingTags(); - if (context.WidthSinceSpace > 0 || context.CurrentLineWidth > 0) // acount for the last line's size offset - { - context.NewOffset += CalculateSizeOffset(context.BiggestCharSize); - } StringBuilderPool.Shared.Return(tagBuffer); StringBuilderPool.Shared.Return(paramBuffer); @@ -459,7 +446,7 @@ void FailTagMatch() // not a tag, unload buffer /// /// The size of the biggest char within the line. /// An offset that should be added to the parser. - private static float CalculateSizeOffset(float biggestChar) => (((biggestChar / Constants.DEFAULTSIZE * 0.2f) + 0.8f) * Constants.DEFAULTHEIGHT) - Constants.DEFAULTHEIGHT; + private static float CalculateSizeOffset(float biggestChar) => (1 - (biggestChar / Constants.DEFAULTSIZE)) * 8.485f; // (((biggestChar / Constants.DEFAULTSIZE * 0.2f) + 0.8f) * Constants.DEFAULTHEIGHT) - Constants.DEFAULTHEIGHT; private static bool IsValidTagChar(char ch) => (ch > '\u0060' && ch < '\u007B') || ch == '-' || ch == '/'; diff --git a/RueI/RueI/Parsing/ParserContext.cs b/RueI/RueI/Parsing/ParserContext.cs index c12394b..3cb18d4 100644 --- a/RueI/RueI/Parsing/ParserContext.cs +++ b/RueI/RueI/Parsing/ParserContext.cs @@ -56,6 +56,11 @@ public class ParserContext : TextInfo, IDisposable /// public float SpaceBuffer { get; set; } = 0; + /// + /// Gets or sets the newline buffer of the parser. + /// + public float NewlineBuffer { get; set; } = 0; + /// /// Gets or sets the current indent of the parser. /// @@ -72,7 +77,7 @@ public class ParserContext : TextInfo, IDisposable public float LineIndent { get; set; } = 0; /// - /// Gets or sets a value indicating whether the parser should parse tags other than noparse. + /// Gets or sets a value indicating whether the parser should parse tags other than . /// public bool ShouldParse { get; set; } = true; diff --git a/RueI/RueI/Parsing/Records/ParsedData.cs b/RueI/RueI/Parsing/Records/ParsedData.cs index 900df6a..3d66351 100644 --- a/RueI/RueI/Parsing/Records/ParsedData.cs +++ b/RueI/RueI/Parsing/Records/ParsedData.cs @@ -19,7 +19,7 @@ internal ParsedData(string content, float offset) /// /// Gets the content of the element. /// - public string Content { get; } + public string Content { get; } /// /// Gets the offset that should be applied to the element. diff --git a/RueI/RueI/Parsing/Tags/ConcreteTags/CloseLineHeightTag.cs b/RueI/RueI/Parsing/Tags/ConcreteTags/CloseLineHeightTag.cs index f62dd48..2135d5a 100644 --- a/RueI/RueI/Parsing/Tags/ConcreteTags/CloseLineHeightTag.cs +++ b/RueI/RueI/Parsing/Tags/ConcreteTags/CloseLineHeightTag.cs @@ -1,17 +1,22 @@ namespace RueI.Parsing.Tags.ConcreteTags; /// -/// Provides a way to handle closing line-height tags. +/// Provides a way to handle closing size tags. /// [RichTextTag] -public class CloseLineHeightTag : ClosingTag +public class CloseLineHeightTag : NoParamsTag { + private const string TAGFORMAT = ""; + /// - public override string Name { get; } = "/line-height"; + public override string[] Names { get; } = { "/line-height" }; /// - protected override void ApplyTo(ParserContext context) + public override bool HandleTag(ParserContext context) { - context.CurrentLineHeight = 0; + context.CurrentLineHeight = Constants.DEFAULTHEIGHT; + context.ResultBuilder.Append(TAGFORMAT); + + return true; } } diff --git a/RueI/RueI/Parsing/Tags/ConcreteTags/CloseNoparseTag.cs b/RueI/RueI/Parsing/Tags/ConcreteTags/CloseNoparseTag.cs index cae6ed8..cccab90 100644 --- a/RueI/RueI/Parsing/Tags/ConcreteTags/CloseNoparseTag.cs +++ b/RueI/RueI/Parsing/Tags/ConcreteTags/CloseNoparseTag.cs @@ -3,6 +3,10 @@ /// /// Provides a way to handle closing noparse tags. /// +/// +/// The RueI allows this tag to be matched even when is false. +/// This replicates the behavior of normal TextMesh Pro. +/// [RichTextTag] public class CloseNoparseTag : ClosingTag { diff --git a/RueI/RueI/Parsing/Tags/ConcreteTags/LineHeightTag.cs b/RueI/RueI/Parsing/Tags/ConcreteTags/LineHeightTag.cs index 33aa3a7..6218de8 100644 --- a/RueI/RueI/Parsing/Tags/ConcreteTags/LineHeightTag.cs +++ b/RueI/RueI/Parsing/Tags/ConcreteTags/LineHeightTag.cs @@ -19,10 +19,11 @@ public override bool HandleTag(ParserContext context, MeasurementInfo info) { var (value, style) = info; + // the line height of ems and percentages changes based on the current size float convertedValue = style switch { - MeasurementUnit.Percentage => value / 100 * Constants.DEFAULTHEIGHT, - MeasurementUnit.Ems => value * Constants.EMSTOPIXELS, + MeasurementUnit.Percentage => value / 100 * Constants.DEFAULTHEIGHT * (context.Size / Constants.DEFAULTSIZE), + MeasurementUnit.Ems => value * Constants.EMSTOPIXELS * (context.Size / Constants.DEFAULTSIZE), _ => value }; diff --git a/RueI/RueI/Patches/HintPatch.cs b/RueI/RueI/Patches/HintPatch.cs index c03daa9..2a20dd2 100644 --- a/RueI/RueI/Patches/HintPatch.cs +++ b/RueI/RueI/Patches/HintPatch.cs @@ -29,6 +29,8 @@ public static class HintPatch private const float MAXANONYMOUSHINTTIME = 3; private const int UPDATEPRIORITY = 10; + private static readonly JobToken UpdateToken = new(); + private delegate bool TryGetHub(GameObject player, out ReferenceHub hub); /// @@ -64,7 +66,8 @@ public static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable new MECAsyncOperation(span, action); /// - internal override void ShowHint(ReferenceHub hub, string message) => hub.connectionToClient.Send(new HintMessage(new TextHint(message, new HintParameter[] { new StringHintParameter(message) }, new HintEffect[] { HintEffectPresets.FadeIn(0, 0, 1) }, 99999))); + internal override void ShowHint(ReferenceHub hub, string message) => hub.connectionToClient.Send(new HintMessage(new TextHint(message, new HintParameter[] { new StringHintParameter(message) }, null, 99999))); /// /// Represents an async operation using a . @@ -197,7 +197,7 @@ public class MECAsyncOperation : IAsyncOperation /// The action to run when finished. public MECAsyncOperation(TimeSpan span, Action action) { - handle = Timing.CallDelayed(((float)span.TotalSeconds).Max(0), action); + handle = Timing.CallDelayed(((float)span.TotalSeconds).Max(0f), action); } /// diff --git a/RueITests/TestExtensions.cs b/RueITests/TestExtensions.cs index d447fe2..0ff90a4 100644 --- a/RueITests/TestExtensions.cs +++ b/RueITests/TestExtensions.cs @@ -1,9 +1,18 @@ using RueI.Extensions; +using RueI.Extensions.HintBuilding; +using System.Drawing; namespace RueITest; [TestClass] public class TestExtensions { + [TestMethod] + public void TestMax() + { + int maxOne = Math.Max(5, 3); + int maxTwo = 5.Max(3); + Assert.AreEqual(maxOne, maxTwo); + } } ///"\"hello world - - - again\"" \ No newline at end of file