using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using RunningMan.Net; using RunningMan.Patches; using RunningMan.Storage; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("RunningMan")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Marathon race tracker for Valheim dedicated servers")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0a508e319f966e579403952eda19df5d5ddd287a")] [assembly: AssemblyProduct("RunningMan")] [assembly: AssemblyTitle("RunningMan")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RunningMan { [Serializable] public sealed class RaceGate { public Vector3Data PointA = new Vector3Data(); public Vector3Data PointB = new Vector3Data(); public Vector3Data Forward = new Vector3Data(); public RaceGate() { } public RaceGate(Vector3 pointA, Vector3 pointB, Vector3 forward) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) PointA = ValheimUtil.ToData(pointA); PointB = ValheimUtil.ToData(pointB); Forward = ValheimUtil.ToData(forward); } public Vector3 GetPointA() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ValheimUtil.FromData(PointA); } public Vector3 GetPointB() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ValheimUtil.FromData(PointB); } public Vector3 GetForward() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ValheimUtil.FromData(Forward); } public Vector3 GetMidpoint() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) return (GetPointA() + GetPointB()) * 0.5f; } public bool IsConfigured() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (PointA != null && PointB != null) { Vector3 val = GetPointA() - GetPointB(); return ((Vector3)(ref val)).sqrMagnitude > 0.01f; } return false; } public void SetEndpoint(bool pointA, Vector3 worldPosition, Vector3? preferredForward = null) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (pointA) { PointA = ValheimUtil.ToData(worldPosition); } else { PointB = ValheimUtil.ToData(worldPosition); } RecomputeForward(preferredForward); } public void RecomputeForward(Vector3? preferredForward = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Vector3 pointA = GetPointA(); Vector3 val = GetPointB() - pointA; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { Vector3 val2 = (Vector3)(((??)preferredForward) ?? GetForward()); val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { val2 = Vector3.forward; } Forward = ValheimUtil.ToData(((Vector3)(ref val2)).normalized); return; } Vector3 val3 = Vector3.Cross(Vector3.up, ((Vector3)(ref val)).normalized); if (preferredForward.HasValue) { Vector3 value = preferredForward.Value; value.y = 0f; if (((Vector3)(ref value)).sqrMagnitude > 0.0001f && Vector3.Dot(val3, value) < 0f) { val3 = -val3; } } else { Vector3 forward = GetForward(); forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f && Vector3.Dot(val3, forward) < 0f) { val3 = -val3; } } Forward = ValheimUtil.ToData(((Vector3)(ref val3)).normalized); } public static RaceGate FromPositionAndForward(Vector3 position, Vector3 forward, float width) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } else { ((Vector3)(ref forward)).Normalize(); } Vector3 val = Vector3.Cross(Vector3.up, forward); Vector3 normalized = ((Vector3)(ref val)).normalized; float num = width * 0.5f; return new RaceGate(position - normalized * num, position + normalized * num, forward); } public static RaceGate FromPlayerPosition(Player player, float width) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)player).transform.position; Vector3 forward = ((Component)player).transform.forward; return FromPositionAndForward(position, forward, width); } public static RaceGate FromPeerPosition(ZNetPeer peer, float width) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (peer != null && ValheimUtil.TryGetPeerWorldPosition(peer, out var position)) { return FromPositionAndForward(position, Vector3.forward, width); } return FromPositionAndForward(Vector3.zero, Vector3.forward, width); } } [Serializable] public sealed class Checkpoint { public int Index; public RaceGate Gate = new RaceGate(); public Checkpoint() { } public Checkpoint(int index, RaceGate gate) { Index = index; Gate = gate; } } [Serializable] public sealed class TrackConfig { public string Name = string.Empty; public RaceGate StartGate = new RaceGate(); public RaceGate FinishGate = new RaceGate(); public List Checkpoints = new List(); public bool HasStart { get { if (StartGate != null) { return StartGate.IsConfigured(); } return false; } } public bool HasFinish { get { if (FinishGate != null) { return FinishGate.IsConfigured(); } return false; } } public int CheckpointCount => Checkpoints?.Count ?? 0; } public static class CommandContext { [ThreadStatic] private static ZRpc _currentRpc; private static readonly List PendingReplies = new List(); public static long SenderPeerId { get; set; } public static bool ExecutingFromRemote { get; set; } public static bool HasOrigin { get; private set; } public static Vector3 OriginPosition { get; private set; } public static Vector3 OriginForward { get; private set; } public static ZRpc CurrentRpc { get { return _currentRpc; } set { _currentRpc = value; } } public static void SetOrigin(Vector3 position, Vector3 forward) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) HasOrigin = true; OriginPosition = position; OriginForward = forward; } public static bool TryGetOrigin(out Vector3 position, out Vector3 forward) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) position = OriginPosition; forward = OriginForward; return HasOrigin; } public static void ClearOrigin() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) HasOrigin = false; OriginPosition = Vector3.zero; OriginForward = Vector3.forward; } public static void Clear() { _currentRpc = null; SenderPeerId = 0L; ClearOrigin(); PendingReplies.Clear(); } public static void QueueReply(string message) { if (!string.IsNullOrEmpty(message)) { PendingReplies.Add(message); } } public static IReadOnlyList TakePendingReplies() { string[] result = PendingReplies.ToArray(); PendingReplies.Clear(); return result; } } public static class Commands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__HandleRunCommand; public static Action <1>__HandleStart; public static Action <2>__HandleDebug; public static Action <3>__HandleReset; public static Action <4>__HandleExport; public static Action <5>__HandleReload; public static Action <6>__HandleRegister; public static Action <7>__HandleRemove; public static Action <8>__HandleClear; public static Action <9>__HandleAutoDetect; public static Action <10>__HandleTrackName; public static Action <11>__HandleGearSet; public static Action <12>__HandleEdit; public static Action <13>__HandleSetPoint; } private static bool _registered; public static void Register() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (!_registered) { _registered = true; object obj = <>O.<0>__HandleRunCommand; if (obj == null) { ConsoleEvent val = HandleRunCommand; <>O.<0>__HandleRunCommand = val; obj = (object)val; } new ConsoleCommand("run", "RunningMan marathon commands", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); RunningManPlugin.Log.LogInfo((object)"Registered /run chat command."); } } public static void ExecuteFromRemote(long senderPeerId, string argLine) { ZRpc rpc = null; if ((Object)(object)ZNet.instance != (Object)null) { rpc = ZNet.instance.GetPeer(senderPeerId)?.m_rpc; } CommandContext.ExecutingFromRemote = true; CommandContext.SenderPeerId = senderPeerId; try { Execute(argLine, rpc); } finally { CommandContext.ExecutingFromRemote = false; } } public static void Execute(string argLine, ZRpc rpc) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown CommandContext.CurrentRpc = rpc; if (rpc != null && (Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(rpc); if (peer != null) { CommandContext.SenderPeerId = peer.m_uid; } } HandleRunCommand(new ConsoleEventArgs("run" + (string.IsNullOrWhiteSpace(argLine) ? string.Empty : (" " + argLine.Trim())), (Terminal)(object)Console.instance)); } private static void HandleRunCommand(ConsoleEventArgs args) { try { string[] array = NormalizeArgs(args.Args); if (TryHandleClientLocalCommand(array, args)) { return; } if (!ValheimUtil.IsServerAuthority() && !CommandContext.ExecutingFromRemote && (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { ValheimUtil.RunCommand(string.Join(" ", array)); return; } if (array.Length == 0) { PrintHelp(args.Context); return; } string text = array[0].ToLowerInvariant(); switch (text) { case "status": HandleStatus(args); break; case "join": HandleJoin(args); break; case "leave": HandleLeave(args); break; case "gear": case "gearcheck": HandleGearCheck(args); break; case "loadout": ValheimUtil.Reply(args.Context, GearValidator.FormatRequiredLoadout()); break; case "admincheck": HandleAdminCheck(args); break; case "last": HandleLastRun(args); break; case "pb": HandlePersonalBest(args); break; case "open": RequireAdmin(args, delegate(ConsoleEventArgs a) { RaceManager.Instance.OpenRegistration(); ValheimUtil.Reply(a.Context, "Registration opened."); }); break; case "close": RequireAdmin(args, delegate(ConsoleEventArgs a) { RaceManager.Instance.CloseRegistration(); ValheimUtil.Reply(a.Context, "Registration closed."); }); break; case "start": RequireAdmin(args, HandleStart); break; case "cancel": RequireAdmin(args, delegate(ConsoleEventArgs a) { RaceManager.Instance.CancelEvent(); ValheimUtil.Reply(a.Context, "Event cancelled."); }); break; case "debug": RequireAdmin(args, HandleDebug); break; case "records": case "leaderboard": case "worldrecords": HandleWorldRecords(args); break; case "reset": RequireAdmin(args, HandleReset); break; case "export": RequireAdmin(args, HandleExport); break; case "reload": RequireAdmin(args, HandleReload); break; case "sync": HandleSync(args); break; case "register": RequireAdmin(args, HandleRegister); break; case "remove": RequireAdmin(args, HandleRemove); break; case "clear": RequireAdmin(args, HandleClear); break; case "autodetect": RequireAdmin(args, HandleAutoDetect); break; case "trackname": RequireAdmin(args, HandleTrackName); break; case "gearset": RequireAdmin(args, HandleGearSet); break; case "edit": RequireAdmin(args, HandleEdit); break; case "setpoint": RequireAdmin(args, HandleSetPoint); break; case "wrboard": case "bulletin": RequireAdmin(args, delegate(ConsoleEventArgs a) { HandleBulletinBoard(a, RaceBulletinKind.Records); }); break; case "rulesboard": case "rulesign": RequireAdmin(args, delegate(ConsoleEventArgs a) { HandleBulletinBoard(a, RaceBulletinKind.Rules); }); break; default: ValheimUtil.Reply(args.Context, "Unknown /run subcommand: " + text); PrintHelp(args.Context); break; } } catch (Exception ex) { ValheimUtil.Reply(args.Context, "RunningMan error: " + ex.Message); RunningManPlugin.Log.LogError((object)ex); } } private static string[] NormalizeArgs(string[] args) { if (args == null || args.Length == 0) { return Array.Empty(); } if (args[0].Equals("run", StringComparison.OrdinalIgnoreCase)) { return args.Skip(1).ToArray(); } return args; } private static void HandleStatus(ConsoleEventArgs args) { if (!ValheimUtil.TryResolveCommandParticipant(out var participant)) { ValheimUtil.ReplyHud("Could not resolve player for status."); } else { ValheimUtil.ReplyHud(RaceManager.Instance.FormatStatus(participant.PlayerId, DateTime.UtcNow)); } } private static void HandleJoin(ConsoleEventArgs args) { if (!ValheimUtil.TryResolveCommandParticipant(out var participant)) { ValheimUtil.ReplyHud("Could not resolve player."); } else if (RaceManager.Instance.JoinPlayer(participant.PlayerId, participant.PlayerName)) { ValheimUtil.ReplyHud(participant.PlayerName + " joined the race."); } else { ValheimUtil.ReplyHud("Could not join — registration may be closed or you are already registered."); } } private static void HandleLeave(ConsoleEventArgs args) { if (!ValheimUtil.TryResolveCommandParticipant(out var participant)) { ValheimUtil.ReplyHud("Could not resolve player."); } else if (RaceManager.Instance.LeavePlayer(participant.PlayerId)) { ValheimUtil.ReplyHud(participant.PlayerName + " left the race."); } else { ValheimUtil.ReplyHud("You are not registered."); } } private static void HandleStart(ConsoleEventArgs args) { if (RaceManager.Instance.StartCountdown(out var failureReason)) { ValheimUtil.Reply(args.Context, $"Countdown started ({ModConfig.CountdownSeconds.Value}s)."); } else { ValheimUtil.Reply(args.Context, string.IsNullOrEmpty(failureReason) ? "Could not start countdown. Use /run open, have runners /run join, then /run start." : failureReason); } } private static void HandleDebug(ConsoleEventArgs args) { bool? flag = ResolveDebugToggle(NormalizeArgs(args.Args)); if (!flag.HasValue) { ValheimUtil.Reply(args.Context, "Usage: /run debug [on|off]"); return; } RaceManager.Instance.SetDebugMode(flag.Value); ValheimUtil.Reply(args.Context, "Debug markers: " + (flag.Value ? "ON" : "OFF")); } private static bool? ResolveDebugToggle(string[] subArgs) { if (subArgs.Length >= 2) { switch (subArgs[1].ToLowerInvariant()) { case "on": case "true": case "1": return true; case "off": case "false": case "0": return false; } } return !RaceNetSync.IsDebugModeActive(); } private static bool TryHandleClientLocalCommand(string[] subArgs, ConsoleEventArgs args) { if (subArgs.Length == 0 || (Object)(object)Player.m_localPlayer == (Object)null || CommandContext.ExecutingFromRemote) { return false; } if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { return false; } switch (subArgs[0].ToLowerInvariant()) { case "gearcheck": case "gear": HandleGearCheck(args, Player.m_localPlayer); return true; case "edit": HandleEdit(args); return true; case "loadout": ValheimUtil.Reply(args.Context, GearValidator.FormatRequiredLoadout()); return true; default: return false; } } private static void HandleGearCheck(ConsoleEventArgs args, Player player = null) { if (player == null) { player = ValheimUtil.ResolvePlayerForGearCheck(); } if ((Object)(object)player == (Object)null) { ValheimUtil.Reply(args.Context, "Could not resolve player."); return; } GearValidator.GearCheckResult gearCheckResult = RaceManager.Instance.CheckPlayerGear(player); string title = (gearCheckResult.IsValid ? "Gear check passed" : "Gear check failed"); string body = (gearCheckResult.IsValid ? "Gear check passed." : ("- " + string.Join("\n- ", gearCheckResult.Issues))); RaceGui.ShowInfoPanel(title, body, gearCheckResult.IsValid ? 5f : 14f); ValheimUtil.Reply(args.Context, gearCheckResult.Format(multiline: false)); } private static void HandleAdminCheck(ConsoleEventArgs args) { if (ValheimUtil.IsServerAuthority()) { ZRpc currentRpc = CommandContext.CurrentRpc; ZNetPeer val = ((currentRpc != null) ? ZNet.instance.GetPeer(currentRpc) : null); string message = ValheimUtil.FormatAdminIdentity(val, currentRpc); string text = ((val != null) ? ValheimUtil.GetPlatformUserIdString(val) : null); bool flag = ValheimUtil.IsAdmin(currentRpc); ValheimUtil.Reply(args.Context, $"Server admin check: admin={flag}"); ValheimUtil.Reply(args.Context, message); if (!string.IsNullOrEmpty(text)) { ValheimUtil.Reply(args.Context, "Add this exact line to adminlist.txt on the server if crossplay is enabled: " + text); } ValheimUtil.ReplyHud(flag ? "Admin: true" : "Admin: false"); if (CommandContext.SenderPeerId != 0L) { RaceNetSync.SendAdminStatusToPeer(CommandContext.SenderPeerId, flag); } } else { bool num = ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.LocalPlayerIsAdminOrHost()) || RaceNetSync.ClientIsAdmin; ValheimUtil.Reply(args.Context, $"Client: Valheim admin={(Object)(object)ZNet.instance != (Object)null && ZNet.instance.LocalPlayerIsAdminOrHost()}, synced admin={RaceNetSync.ClientIsAdmin}"); ValheimUtil.ReplyHud(num ? "Admin: true" : "Admin: false"); } } private static void HandlePersonalBest(ConsoleEventArgs args) { if (!ValheimUtil.TryResolveCommandParticipant(out var participant)) { ValheimUtil.Reply(args.Context, "Could not resolve player."); } else { ValheimUtil.Reply(args.Context, Leaderboard.FormatPersonalBest(Leaderboard.GetPersonalBest(participant.PlayerId))); } } private static void HandleLastRun(ConsoleEventArgs args) { if (!ValheimUtil.TryResolveCommandParticipant(out var participant)) { ValheimUtil.Reply(args.Context, "Could not resolve player."); } else { ValheimUtil.Reply(args.Context, Leaderboard.FormatLastRun(Leaderboard.GetLastRun(participant.PlayerId))); } } private static void HandleWorldRecords(ConsoleEventArgs args) { string message = Leaderboard.FormatWorldRecords(JsonStorage.Track, ModConfig.LeaderboardLimit.Value); ValheimUtil.Reply(args.Context, message); ValheimUtil.ReplyHud(message); } private static void HandleTrackName(ConsoleEventArgs args) { string[] array = NormalizeArgs(args.Args); if (array.Length < 2) { ValheimUtil.Reply(args.Context, "Current track: " + TrackIdentity.GetDisplayName(JsonStorage.Track) + ". Usage: /run trackname "); return; } string text = string.Join(" ", array.Skip(1)); RaceManager.Instance.SetTrackName(text); ValheimUtil.Reply(args.Context, "Track name set to: " + text); } private static void HandleGearSet(ConsoleEventArgs args) { string[] array = NormalizeArgs(args.Args); if (array.Length < 2) { ValheimUtil.Reply(args.Context, "Usage: /run gearset reset | /run gearset save (use F6 Allowed gear)"); ValheimUtil.Reply(args.Context, GearValidator.FormatRequiredLoadout()); return; } switch (array[1].ToLowerInvariant()) { case "reset": case "defaults": JsonStorage.ResetAllowedGearToDefaults(); RaceManager.Instance.BroadcastState(); ValheimUtil.Reply(args.Context, "Allowed gear reset to marathon defaults."); ValheimUtil.Reply(args.Context, GearValidator.FormatRequiredLoadout()); break; case "save": case "set": { if (!TryParseGearSetPayload(string.Join(" ", array.Skip(2)), out var rules)) { ValheimUtil.Reply(args.Context, "Invalid gearset payload. Use F6 Allowed gear → Save."); break; } JsonStorage.SetAllowedGear(rules); RaceManager.Instance.BroadcastState(); ValheimUtil.Reply(args.Context, "Allowed gear saved."); ValheimUtil.Reply(args.Context, GearValidator.FormatRequiredLoadout()); break; } default: ValheimUtil.Reply(args.Context, "Usage: /run gearset reset | /run gearset save "); break; } } private static bool TryParseGearSetPayload(string payload, out AllowedGearRules rules) { rules = null; if (string.IsNullOrWhiteSpace(payload)) { return false; } string[] array = payload.Split(new char[1] { '§' }, StringSplitOptions.None); if (array.Length < 11) { return false; } if (!int.TryParse(array[8], out var result) || !int.TryParse(array[9], out var result2) || !int.TryParse(array[10], out var _)) { return false; } AllowedGearRules allowedGearRules = JsonStorage.AllowedGear ?? AllowedGearRules.CreateDefaults(); rules = allowedGearRules.Clone(); rules.Helmet = array[0] ?? string.Empty; rules.Chest = array[1] ?? string.Empty; rules.Legs = array[2] ?? string.Empty; rules.Cape = array[3] ?? string.Empty; rules.AllowedHandItems = array[4] ?? string.Empty; if (!string.IsNullOrWhiteSpace(array[5])) { rules.AntiStingPrefab = array[5].Trim(); } if (!string.IsNullOrWhiteSpace(array[6])) { rules.RatatoskPrefab = array[6].Trim(); } rules.RequiredAntiSting = Math.Max(0, result); rules.RequiredRatatosk = Math.Max(0, result2); if (array.Length >= 17 && int.TryParse(array[14], out var result4) && int.TryParse(array[15], out var result5) && int.TryParse(array[16], out var result6)) { if (!string.IsNullOrWhiteSpace(array[11])) { rules.SaladPrefab = array[11].Trim(); } if (!string.IsNullOrWhiteSpace(array[12])) { rules.BloodPuddingPrefab = array[12].Trim(); } if (!string.IsNullOrWhiteSpace(array[13])) { rules.MushroomOmelettePrefab = array[13].Trim(); } rules.RequiredSalad = Math.Max(0, result4); rules.RequiredBloodPudding = Math.Max(0, result5); rules.RequiredMushroomOmelette = Math.Max(0, result6); } return true; } private static void HandleEdit(ConsoleEventArgs args) { string[] array = NormalizeArgs(args.Args); bool flag; if (array.Length >= 2) { switch (array[1].ToLowerInvariant()) { case "on": case "true": case "1": flag = true; break; case "off": case "false": case "0": flag = false; break; default: ValheimUtil.Reply(args.Context, "Usage: /run edit [on|off]"); return; } } else { flag = !RaceGateEditor.EditMode; } RaceGateEditor.SetEditMode(flag); ValheimUtil.Reply(args.Context, flag ? "Gate edit ON. Equip Hammer, look at endpoint dots, Use (E) to pick up / place." : "Gate edit OFF."); } private static void HandleSetPoint(ConsoleEventArgs args) { //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) string[] array = NormalizeArgs(args.Args); if (array.Length < 3) { ValheimUtil.Reply(args.Context, "Usage: /run setpoint start|finish a|b OR /run setpoint checkpoint a|b"); return; } if (!TryResolveSearchOrigin(ResolvePlayer(args), out var origin)) { ValheimUtil.Reply(args.Context, "Could not resolve position for setpoint."); return; } string text = array[1].ToLowerInvariant(); int result = 0; RaceManager.GateKind gateKind; string text2; switch (text) { case "start": gateKind = RaceManager.GateKind.Start; text2 = array[2]; break; case "finish": gateKind = RaceManager.GateKind.Finish; text2 = array[2]; break; case "checkpoint": case "cp": if (array.Length < 4 || !int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { ValheimUtil.Reply(args.Context, "Usage: /run setpoint checkpoint a|b"); return; } gateKind = RaceManager.GateKind.Checkpoint; text2 = array[3]; break; default: ValheimUtil.Reply(args.Context, "Usage: /run setpoint start|finish a|b OR /run setpoint checkpoint a|b"); return; } bool flag; switch (text2.ToLowerInvariant()) { case "a": case "1": case "left": flag = true; break; case "b": case "2": case "right": flag = false; break; default: ValheimUtil.Reply(args.Context, "Endpoint must be a or b."); return; } Vector3? preferredForward = null; if (CommandContext.TryGetOrigin(out var _, out var forward)) { preferredForward = forward; } if (RaceManager.Instance.SetGateEndpoint(gateKind, result, flag, origin, preferredForward)) { string text3 = ((gateKind == RaceManager.GateKind.Checkpoint) ? $"CP{result}" : gateKind.ToString().ToUpperInvariant()); ValheimUtil.Reply(args.Context, "Moved " + text3 + " point " + (flag ? "A" : "B") + "."); } else { ValheimUtil.Reply(args.Context, "Could not move that endpoint (gate missing?)."); } } private static void HandleReset(ConsoleEventArgs args) { string[] array = NormalizeArgs(args.Args); if (array.Length < 2) { ValheimUtil.Reply(args.Context, "Usage: /run reset "); return; } Player val = ValheimUtil.FindPlayerByName(array[1]); if ((Object)(object)val == (Object)null) { ValheimUtil.Reply(args.Context, "Player not found: " + array[1]); } else if (RaceManager.Instance.ResetActiveRun(val.GetPlayerID())) { ValheimUtil.Reply(args.Context, "Reset active run for " + val.GetPlayerName() + "."); } else { ValheimUtil.Reply(args.Context, val.GetPlayerName() + " has no active run."); } } private static void HandleExport(ConsoleEventArgs args) { JsonStorage.ExportAll(); ValheimUtil.Reply(args.Context, "Exported race data to " + JsonStorage.ExportFilePath); } private static void HandleSync(ConsoleEventArgs args) { if (!ValheimUtil.IsServerAuthority()) { ValheimUtil.Reply(args.Context, "State sync must run on the server."); return; } RaceStateSnapshot snapshot = RaceManager.Instance.BuildClientSnapshot(DateTime.UtcNow); long senderPeerId = CommandContext.SenderPeerId; if (senderPeerId != 0L) { RaceNetSync.SendStateToPeer(senderPeerId, snapshot); RaceNetSync.SendTrackToPeer(senderPeerId, JsonStorage.Track); } else { RaceManager.Instance.BroadcastState(); ValheimUtil.Reply(args.Context, "RunningMan state refreshed."); } } private static void HandleReload(ConsoleEventArgs args) { ModConfig.ConfigFile.Reload(); ModConfig.Initialize(ModConfig.ConfigFile); JsonStorage.Initialize(Paths.ConfigPath); RaceManager.Instance.SyncTrack(); RaceNetSync.SendBulletins(); ValheimUtil.Reply(args.Context, "RunningMan configuration and data reloaded."); } private static void HandleBulletinBoard(ConsoleEventArgs args, RaceBulletinKind kind) { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) string text = ((kind == RaceBulletinKind.Rules) ? "RULES" : "WR"); List list = ((kind == RaceBulletinKind.Rules) ? JsonStorage.RulesBoards : JsonStorage.WrBoards); string[] array = NormalizeArgs(args.Args); string text2 = ((array.Length >= 2) ? array[1].ToLowerInvariant() : "add"); string message = ((kind == RaceBulletinKind.Rules) ? "Usage: /run rulesboard [add|remove|list|clear]" : "Usage: /run wrboard [add|remove|list|clear]"); switch (text2) { case "list": ValheimUtil.Reply(args.Context, (list.Count == 0) ? ("No " + text + " bulletin boards marked.") : $"{list.Count} {text} bulletin board(s) marked."); break; case "clear": if (kind == RaceBulletinKind.Rules) { JsonStorage.SetRulesBoards(new List()); } else { JsonStorage.SetWrBoards(new List()); } RaceNetSync.SendBulletins(); ValheimUtil.Reply(args.Context, "All " + text + " bulletin boards cleared."); break; case "remove": { if (!TryResolveBoardPosition(args, out var position2)) { ValheimUtil.Reply(args.Context, "Look at a marked " + text + " Sign (or stand near one) and run the remove command."); break; } int num = -1; float num2 = float.MaxValue; for (int i = 0; i < list.Count; i++) { float num3 = Vector3.Distance(position2, ValheimUtil.FromData(list[i])); if (num3 < num2) { num2 = num3; num = i; } } if (num < 0 || num2 > 8f) { ValheimUtil.Reply(args.Context, "No " + text + " board within 8m to remove."); break; } list.RemoveAt(num); if (kind == RaceBulletinKind.Rules) { JsonStorage.SaveRulesBoards(); } else { JsonStorage.SaveWrBoards(); } RaceNetSync.SendBulletins(); ValheimUtil.Reply(args.Context, $"Removed {text} board ({list.Count} remaining)."); break; } default: { if (text2 != "add" && array.Length >= 2) { ValheimUtil.Reply(args.Context, message); break; } if (!TryResolveBoardPosition(args, out var position)) { ValheimUtil.Reply(args.Context, "Look at a Hammer Sign and run /run " + ((kind == RaceBulletinKind.Rules) ? "rulesboard" : "wrboard") + "."); break; } if (RaceWrBoards.TryFindBoardIndex(RaceBulletinKind.Records, position, 1.5f, out var index) || RaceWrBoards.TryFindBoardIndex(RaceBulletinKind.Rules, position, 1.5f, out index)) { ValheimUtil.Reply(args.Context, "That Sign is already marked as a bulletin."); break; } list.Add(ValheimUtil.ToData(position)); if (kind == RaceBulletinKind.Rules) { JsonStorage.SaveRulesBoards(); } else { JsonStorage.SaveWrBoards(); } RaceNetSync.SendBulletins(); ValheimUtil.Reply(args.Context, $"{text} Sign marked ({list.Count} total). Walk up to it to read."); break; } } } private static bool TryResolveBoardPosition(ConsoleEventArgs args, out Vector3 position) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (CommandContext.TryGetOrigin(out position, out var _)) { return true; } Player val = ResolvePlayer(args); if ((Object)(object)val != (Object)null) { position = ((Component)val).transform.position; return true; } position = Vector3.zero; return false; } private static void HandleRegister(ConsoleEventArgs args) { string[] array = NormalizeArgs(args.Args); if (array.Length < 2) { ValheimUtil.Reply(args.Context, "Usage: /run register start|finish|checkpoint [width]"); return; } Player player = ResolvePlayer(args); float num = ParseRegistrationWidth(array, 2); RaceGate raceGate = ValheimUtil.CreateRegistrationGate(player, ValheimUtil.ResolveCommandPeer(), num); if (raceGate == null) { ValheimUtil.Reply(args.Context, "Could not resolve player position for registration."); return; } switch (array[1].ToLowerInvariant()) { case "start": RaceManager.Instance.RegisterStartGate(raceGate); ValheimUtil.Reply(args.Context, $"Start gate registered (width {num:0.#}m). Enable debug to preview markers."); break; case "finish": RaceManager.Instance.RegisterFinishGate(raceGate); ValheimUtil.Reply(args.Context, $"Finish gate registered (width {num:0.#}m). Enable debug to preview markers."); break; case "checkpoint": { int num2 = RaceManager.Instance.RegisterCheckpoint(raceGate); ValheimUtil.Reply(args.Context, $"Checkpoint {num2} registered (width {num:0.#}m). Later checkpoints were renumbered. Enable debug to preview markers."); break; } default: ValheimUtil.Reply(args.Context, "Usage: /run register start|finish|checkpoint [width]"); break; } } private static void HandleRemove(ConsoleEventArgs args) { //IL_0126: Unknown result type (might be due to invalid IL or missing references) string[] array = NormalizeArgs(args.Args); if (array.Length < 2) { ValheimUtil.Reply(args.Context, "Usage: /run remove start|finish|checkpoint "); return; } switch (array[1].ToLowerInvariant()) { case "start": ValheimUtil.Reply(args.Context, RaceManager.Instance.ClearStartGate() ? "Start gate removed." : "No start gate is registered."); break; case "finish": ValheimUtil.Reply(args.Context, RaceManager.Instance.ClearFinishGate() ? "Finish gate removed." : "No finish gate is registered."); break; case "checkpoint": { int result; if (array.Length < 3) { ValheimUtil.Reply(args.Context, "Usage: /run remove checkpoint "); } else if (array[2].Equals("last", StringComparison.OrdinalIgnoreCase)) { ValheimUtil.Reply(args.Context, RaceManager.Instance.RemoveLastCheckpoint() ? "Last checkpoint removed." : "No checkpoints are registered."); } else if (array[2].Equals("nearest", StringComparison.OrdinalIgnoreCase)) { if (!TryResolveSearchOrigin(ResolvePlayer(args), out var origin)) { ValheimUtil.Reply(args.Context, "Could not resolve position for nearest checkpoint removal."); } else { ValheimUtil.Reply(args.Context, RaceManager.Instance.RemoveNearestCheckpoint(origin) ? "Nearest checkpoint removed." : "No checkpoints are registered."); } } else if (!int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { ValheimUtil.Reply(args.Context, "Usage: /run remove checkpoint "); } else { ValheimUtil.Reply(args.Context, RaceManager.Instance.RemoveCheckpoint(result) ? $"Checkpoint {result} removed." : $"Checkpoint {result} was not found."); } break; } default: ValheimUtil.Reply(args.Context, "Usage: /run remove start|finish|checkpoint "); break; } } private static void HandleClear(ConsoleEventArgs args) { string[] array = NormalizeArgs(args.Args); if (array.Length < 2) { ValheimUtil.Reply(args.Context, "Usage: /run clear checkpoints|start|finish|track|records [all]"); return; } switch (array[1].ToLowerInvariant()) { case "records": case "worldrecords": if (array.Length >= 3 && array[2].Equals("all", StringComparison.OrdinalIgnoreCase)) { int num3 = RaceManager.Instance.ClearAllWorldRecords(); ValheimUtil.Reply(args.Context, $"Cleared {num3} world record(s) for all tracks."); } else { int num4 = RaceManager.Instance.ClearTrackWorldRecords(); ValheimUtil.Reply(args.Context, (num4 > 0) ? $"Cleared {num4} world record(s) for {TrackIdentity.GetDisplayName(JsonStorage.Track)}." : "No world records for this track."); } break; case "checkpoints": case "checkpoint": { int num2 = RaceManager.Instance.ClearCheckpoints(); ValheimUtil.Reply(args.Context, (num2 > 0) ? $"Removed {num2} checkpoint(s)." : "No checkpoints are registered."); break; } case "start": ValheimUtil.Reply(args.Context, RaceManager.Instance.ClearStartGate() ? "Start gate removed." : "No start gate is registered."); break; case "finish": ValheimUtil.Reply(args.Context, RaceManager.Instance.ClearFinishGate() ? "Finish gate removed." : "No finish gate is registered."); break; case "track": { RaceManager.Instance.ClearStartGate(); RaceManager.Instance.ClearFinishGate(); int num = RaceManager.Instance.ClearCheckpoints(); ValheimUtil.Reply(args.Context, $"Track cleared ({num} checkpoint(s) removed)."); break; } default: ValheimUtil.Reply(args.Context, "Usage: /run clear checkpoints|start|finish|track|records [all]"); break; } } private static float ParseRegistrationWidth(string[] subArgs, int widthArgIndex) { float value = ModConfig.GateRegistrationWidth.Value; if (subArgs.Length <= widthArgIndex) { return value; } if (float.TryParse(subArgs[widthArgIndex], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return Mathf.Clamp(result, 1f, 50f); } return value; } private static void HandleAutoDetect(ConsoleEventArgs args) { //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) string[] array = NormalizeArgs(args.Args); if (!TryResolveSearchOrigin(ResolvePlayer(args), out var origin)) { ValheimUtil.Reply(args.Context, "Could not resolve player for auto-detect."); return; } float value = ModConfig.AutoDetectSearchRadius.Value; switch ((array.Length > 1) ? array[1].ToLowerInvariant() : "checkpoints") { case "start": { RaceGate raceGate3 = TriggerDetector.DetectGraustenGate(origin, value); if (raceGate3 == null) { ValheimUtil.Reply(args.Context, "No Grausten gate found nearby."); break; } RaceManager.Instance.RegisterStartGate(raceGate3); ValheimUtil.Reply(args.Context, "Start gate auto-detected."); break; } case "finish": { RaceGate raceGate2 = TriggerDetector.DetectGraustenGate(origin, value); if (raceGate2 == null) { ValheimUtil.Reply(args.Context, "No Grausten gate found nearby."); break; } RaceManager.Instance.RegisterFinishGate(raceGate2); ValheimUtil.Reply(args.Context, "Finish gate auto-detected."); break; } case "checkpoints": { List list = TriggerDetector.DetectTorchCheckpoints(origin, value, ModConfig.AutoDetectTorchPairMinDistance.Value, ModConfig.AutoDetectTorchPairMaxDistance.Value); if (list.Count == 0) { ValheimUtil.Reply(args.Context, "No torch checkpoint pairs found nearby."); break; } RaceManager.Instance.ReplaceCheckpoints(list); ValheimUtil.Reply(args.Context, $"Auto-detected {list.Count} checkpoint pair(s)."); break; } case "checkpoint": case "cp": case "nearest": case "here": { float value2 = ModConfig.AutoDetectNearestSearchRadius.Value; RaceGate raceGate = TriggerDetector.DetectNearestTorchPair(origin, value2, ModConfig.AutoDetectTorchPairMinDistance.Value, ModConfig.AutoDetectTorchPairMaxDistance.Value); if (raceGate == null) { ValheimUtil.Reply(args.Context, $"No valid Standing Iron Torch pair within {value2:0.#}m. Need two torches {ModConfig.AutoDetectTorchPairMinDistance.Value:0.#}-{ModConfig.AutoDetectTorchPairMaxDistance.Value:0.#}m apart."); break; } int num = RaceManager.Instance.RegisterCheckpoint(raceGate); ValheimUtil.Reply(args.Context, $"Added checkpoint {num} from the two nearest Standing Iron Torches (later CPs renumbered)."); break; } default: ValheimUtil.Reply(args.Context, "Usage: /run autodetect start|finish|checkpoints|checkpoint"); break; } } private static void RequireAdmin(ConsoleEventArgs args, Action action) { if (!ValheimUtil.IsServerAuthority()) { ValheimUtil.Reply(args.Context, "RunningMan admin commands must run on the server."); } else if (!ValheimUtil.IsAdmin(CommandContext.CurrentRpc)) { string text = ValheimUtil.FormatAdminIdentity(ValheimUtil.ResolveCommandPeer(), CommandContext.CurrentRpc); RunningManPlugin.Log.LogWarning((object)("RunningMan admin denied for " + text + ".")); ValheimUtil.Reply(args.Context, "Admin privileges required. If this server uses crossplay, adminlist.txt needs the Platform ID from F2, e.g. Steam_7656119..."); ValheimUtil.Reply(args.Context, "Server sees: " + text); } else { action(args); } } private static bool TryResolveSearchOrigin(Player player, out Vector3 origin) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)null) { origin = ((Component)player).transform.position; return true; } if (CommandContext.TryGetOrigin(out origin, out var _)) { return true; } ZNetPeer val = ValheimUtil.ResolveCommandPeer(); if (val != null && ValheimUtil.TryGetPeerWorldPosition(val, out origin)) { return true; } origin = Vector3.zero; return false; } private static Player ResolvePlayer(ConsoleEventArgs args) { return ValheimUtil.ResolvePlayerForGearCheck(); } private static void PrintHelp(Terminal context) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("RunningMan — press F6 for GUI"); stringBuilder.AppendLine("Player:"); stringBuilder.AppendLine(" /run join | leave | status | gearcheck | loadout | admincheck | pb | last"); stringBuilder.AppendLine("Admin:"); stringBuilder.AppendLine(" /run open | close | start | cancel | debug"); stringBuilder.AppendLine(" /run register start|finish|checkpoint [width]"); stringBuilder.AppendLine(" /run remove start|finish|checkpoint "); stringBuilder.AppendLine(" /run clear checkpoints|start|finish|track"); stringBuilder.AppendLine(" /run autodetect start|finish|checkpoints|checkpoint"); stringBuilder.AppendLine(" /run edit [on|off] (hammer + Use to move gate endpoints)"); stringBuilder.AppendLine(" /run setpoint start|finish a|b | checkpoint a|b"); stringBuilder.AppendLine(" /run worldrecords | trackname | gearset reset|save"); stringBuilder.AppendLine(" /run wrboard [add|remove|list|clear] (look at a Hammer Sign)"); stringBuilder.AppendLine(" /run rulesboard [add|remove|list|clear] (RULES Sign)"); stringBuilder.AppendLine(" /run clear records | clear records all"); stringBuilder.AppendLine(" /run leaderboard | reset | export | reload"); ValheimUtil.Reply(context, stringBuilder.ToString()); } } public static class ModConfig { public static ConfigEntry EnableBroadcasts { get; private set; } public static ConfigEntry EnableHud { get; private set; } public static ConfigEntry DebugMode { get; private set; } public static ConfigEntry GuiHotkey { get; private set; } public static ConfigEntry CountdownSeconds { get; private set; } public static ConfigEntry RequireRegistration { get; private set; } public static ConfigEntry StartTriggerDistance { get; private set; } public static ConfigEntry CheckpointDistance { get; private set; } public static ConfigEntry GateEndPadding { get; private set; } public static ConfigEntry CheckpointVerticalDistance { get; private set; } public static ConfigEntry GateVerticalDistance { get; private set; } public static ConfigEntry FinishTriggerDistance { get; private set; } public static ConfigEntry EnableLiveStandings { get; private set; } public static ConfigEntry LiveStandingsLimit { get; private set; } public static ConfigEntry UpdateInterval { get; private set; } public static ConfigEntry GateRegistrationWidth { get; private set; } public static ConfigEntry CountdownFontSize { get; private set; } public static ConfigEntry CheckpointHudFontSize { get; private set; } public static ConfigEntry SavePath { get; private set; } public static ConfigEntry ExportFormat { get; private set; } public static ConfigEntry LeaderboardLimit { get; private set; } public static ConfigEntry WorldRecordsLimit { get; private set; } public static ConfigEntry EnableRaceSounds { get; private set; } public static ConfigEntry RaceSoundVolume { get; private set; } public static ConfigEntry RaceStartSounds { get; private set; } public static ConfigEntry CheckpointSound { get; private set; } public static ConfigEntry FirstPlaceFinishSounds { get; private set; } public static ConfigEntry UseCustomRaceSounds { get; private set; } public static ConfigEntry CustomSoundsFolder { get; private set; } public static ConfigEntry CustomStartSoundFile { get; private set; } public static ConfigEntry CustomCheckpointSoundFile { get; private set; } public static ConfigEntry CustomFinishSoundFile { get; private set; } public static ConfigEntry CustomCountdownSoundFile { get; private set; } public static ConfigEntry CustomFalseStartSoundFile { get; private set; } public static ConfigEntry StartingAreaOffset { get; private set; } public static ConfigEntry StartingAreaDepth { get; private set; } public static ConfigEntry StartingAreaSidePadding { get; private set; } public static ConfigEntry RequireStartingArea { get; private set; } public static ConfigEntry DisqualifyOnFalseStart { get; private set; } public static ConfigEntry AutoDetectTorchPairMinDistance { get; private set; } public static ConfigEntry AutoDetectTorchPairMaxDistance { get; private set; } public static ConfigEntry AutoDetectSearchRadius { get; private set; } public static ConfigEntry AutoDetectNearestSearchRadius { get; private set; } public static ConfigEntry EndpointSnapRadius { get; private set; } public static ConfigEntry EndpointSnapAngle { get; private set; } public static ConfigEntry RegisterCheckpointHotkey { get; private set; } public static ConfigEntry AutoDetectNearestCheckpointHotkey { get; private set; } public static ConfigEntry NormalizeRunSkill { get; private set; } public static ConfigEntry NormalizedRunSkillLevel { get; private set; } public static ConfigEntry EnableGearCheck { get; private set; } public static ConfigEntry DisqualifyOnGearViolation { get; private set; } public static ConfigEntry RequiredAntiStingCount { get; private set; } public static ConfigEntry RequiredRatatoskCount { get; private set; } public static ConfigEntry TriggerSweepStepDistance { get; private set; } public static ConfigEntry AllowedHandItems { get; private set; } public static ConfigEntry MountainBiomeUnlocksFeatherCape { get; private set; } public static ConfigFile ConfigFile { get; private set; } public static void Initialize(ConfigFile config) { ConfigFile = config; EnableBroadcasts = config.Bind("General", "EnableBroadcasts", true, "Broadcast race events to all players in chat."); EnableHud = config.Bind("General", "EnableHud", true, "Show live race overlay with time and place."); EnableLiveStandings = config.Bind("General", "EnableLiveStandings", true, "Show a live ranking panel during races."); EnableRaceSounds = config.Bind("Audio", "EnableRaceSounds", true, "Play local sounds for race start, checkpoints, and 1st-place finishes."); RaceSoundVolume = config.Bind("Audio", "RaceSoundVolume", 1f, "Volume multiplier for RunningMan race sounds."); RaceStartSounds = config.Bind("Audio", "RaceStartSounds", "sfx_offering,sfx_demister_start", "Comma-separated Valheim SFX prefab names played when the race starts (GO!)."); CheckpointSound = config.Bind("Audio", "CheckpointSound", "sfx_archery_target_hit", "Valheim SFX prefab name played when you reach a checkpoint."); FirstPlaceFinishSounds = config.Bind("Audio", "FirstPlaceFinishSounds", "sfx_coins_placed,sfx_coins_placed,sfx_boar_love", "Comma-separated Valheim SFX prefab names played when you finish in 1st place (fallback)."); UseCustomRaceSounds = config.Bind("Audio", "UseCustomRaceSounds", true, "Prefer custom audio files from the plugin Sounds folder when present."); CustomSoundsFolder = config.Bind("Audio", "CustomSoundsFolder", "Sounds", "Subfolder next to RunningMan.dll (…/BepInEx/plugins/RunningMan/Sounds)."); CustomStartSoundFile = config.Bind("Audio", "CustomStartSoundFile", "start.mp3", "Race start sound file name (mp3, wav, or ogg)."); CustomCheckpointSoundFile = config.Bind("Audio", "CustomCheckpointSoundFile", "checkpoint.mp3", "Checkpoint sound file name (mp3, wav, or ogg)."); CustomFinishSoundFile = config.Bind("Audio", "CustomFinishSoundFile", "finish.mp3", "1st-place finish sound file name (mp3, wav, or ogg)."); CustomCountdownSoundFile = config.Bind("Audio", "CustomCountdownSoundFile", "countdown.mp3", "Countdown voice file played when an admin starts the race (e.g. 5-4-3-2-1)."); CustomFalseStartSoundFile = config.Bind("Audio", "CustomFalseStartSoundFile", "false_start.mp3", "Played only for a player who false-starts (stops their countdown audio)."); LiveStandingsLimit = config.Bind("General", "LiveStandingsLimit", 6, "Maximum runners shown in the live ranking panel."); DebugMode = config.Bind("General", "DebugMode", false, "Show start/finish/checkpoint gate markers in the world (synced from server)."); GuiHotkey = config.Bind("General", "GuiHotkey", (KeyCode)287, "Hotkey to open the RunningMan panel."); RegisterCheckpointHotkey = config.Bind("General", "RegisterCheckpointHotkey", (KeyCode)288, "Hotkey to register a checkpoint at your position (admin). None disables."); AutoDetectNearestCheckpointHotkey = config.Bind("General", "AutoDetectNearestCheckpointHotkey", (KeyCode)289, "Hotkey to auto-detect 1 checkpoint from the two nearest Standing Iron Torches (admin). None disables."); NormalizeRunSkill = config.Bind("General", "NormalizeRunSkill", true, "Clamp every runner's Run skill to NormalizedRunSkillLevel during a race."); NormalizedRunSkillLevel = config.Bind("General", "NormalizedRunSkillLevel", 50f, "Run skill level used for all participants while a race is active (0-100)."); CountdownSeconds = config.Bind("General", "CountdownSeconds", 5, "Seconds counted down before a registered race starts."); RequireRegistration = config.Bind("General", "RequireRegistration", true, "Only registered participants can race after the countdown."); StartTriggerDistance = config.Bind("Detection", "StartTriggerDistance", 6f, "Maximum horizontal distance from the start gate line to trigger a start."); RequireStartingArea = config.Bind("Detection", "RequireStartingArea", true, "Block countdown unless every registered runner is inside the starting grid."); StartingAreaOffset = config.Bind("Detection", "StartingAreaOffset", 1f, "How far before the start line (meters) the starting grid is centered."); StartingAreaDepth = config.Bind("Detection", "StartingAreaDepth", 4f, "Depth of the starting grid along the approach (meters)."); StartingAreaSidePadding = config.Bind("Detection", "StartingAreaSidePadding", 1.5f, "Extra width beyond the start gate endpoints for the starting grid."); DisqualifyOnFalseStart = config.Bind("Detection", "DisqualifyOnFalseStart", true, "Remove a runner from the event if they cross the start line during countdown."); CheckpointDistance = config.Bind("Detection", "CheckpointDistance", 2.5f, "Maximum horizontal distance from a checkpoint gate line to register passage."); GateEndPadding = config.Bind("Detection", "GateEndPadding", 0f, "How far past gate endpoints (meters) still counts as inside. 0 = must pass between A and B."); CheckpointVerticalDistance = config.Bind("Detection", "CheckpointVerticalDistance", 15f, "Maximum vertical distance above/below a checkpoint gate line to register passage."); GateVerticalDistance = config.Bind("Detection", "GateVerticalDistance", 8f, "Maximum vertical distance above/below start/finish gate lines."); FinishTriggerDistance = config.Bind("Detection", "FinishTriggerDistance", 8f, "Maximum horizontal distance from the finish gate line to trigger completion."); UpdateInterval = config.Bind("Detection", "UpdateInterval", 0.05f, "Seconds between race position checks (lower = better for fast runners)."); TriggerSweepStepDistance = config.Bind("Detection", "TriggerSweepStepDistance", 1.5f, "Sub-step size (meters) when checking movement between ticks so fast runners do not skip gates."); GateRegistrationWidth = config.Bind("Detection", "GateRegistrationWidth", 6f, "Default gate width when registering from player position."); CountdownFontSize = config.Bind("General", "CountdownFontSize", 72, "Font size for the on-screen race countdown."); CheckpointHudFontSize = config.Bind("General", "CheckpointHudFontSize", 11, "Font size for the checkpoint progress line on the race HUD."); SavePath = config.Bind("Storage", "SavePath", "RunningMan/", "Subfolder under BepInEx/config/ for race JSON files."); ExportFormat = config.Bind("Storage", "ExportFormat", "json", "Export format identifier."); LeaderboardLimit = config.Bind("Storage", "LeaderboardLimit", 10, "Number of entries shown in /run worldrecords."); WorldRecordsLimit = config.Bind("Storage", "WorldRecordsLimit", 5, "Number of world record entries shown on the HUD and WR bulletin Signs (default 5)."); AutoDetectTorchPairMinDistance = config.Bind("AutoDetect", "TorchPairMinDistance", 3f, "Minimum spacing between paired Standing Iron Torches."); AutoDetectTorchPairMaxDistance = config.Bind("AutoDetect", "TorchPairMaxDistance", 25f, "Maximum spacing between paired Standing Iron Torches."); AutoDetectSearchRadius = config.Bind("AutoDetect", "SearchRadius", 500f, "Search radius for auto-detection commands."); AutoDetectNearestSearchRadius = config.Bind("AutoDetect", "NearestSearchRadius", 40f, "Search radius when auto-detecting a single checkpoint from the two nearest torches."); EndpointSnapRadius = config.Bind("AutoDetect", "EndpointSnapRadius", 14f, "How far endpoint edit mode looks for snap targets (Standing Iron Torches)."); EndpointSnapAngle = config.Bind("AutoDetect", "EndpointSnapAngle", 22f, "Max look angle (degrees) for endpoint snap-to-torch."); EnableGearCheck = config.Bind("GearCheck", "EnableGearCheck", true, "Validate marathon loadout at start and during the race."); DisqualifyOnGearViolation = config.Bind("GearCheck", "DisqualifyOnViolation", true, "Remove runners who equip non-approved gear mid-race."); RequiredAntiStingCount = config.Bind("GearCheck", "RequiredAntiStingCount", 1, "Anti-Sting Concoctions (MeadBugRepellent) required in inventory at start."); RequiredRatatoskCount = config.Bind("GearCheck", "RequiredRatatoskCount", 2, "Tonic of Ratatosk (MeadHasty) required in inventory at start."); AllowedHandItems = config.Bind("GearCheck", "AllowedHandItems", "", "Unused (kept for compatibility). Hand items are not restricted during races."); MountainBiomeUnlocksFeatherCape = config.Bind("GearCheck", "MountainBiomeUnlocksFeatherCape", false, "Unused (kept for config compatibility). Feather Cape is always allowed; Troll Hide is not."); } } public static class GearRules { public const string HelmetTroll = "HelmetTrollLeather"; public const string ChestTroll = "ArmorTrollLeatherChest"; public const string LegsTroll = "ArmorTrollLeatherLegs"; public const string CapeFeather = "CapeFeather"; public const string AntiSting = "MeadBugRepellent"; public const string TonicRatatosk = "MeadHasty"; public const string Salad = "Salad"; public const string BloodPudding = "BloodPudding"; public const string MushroomOmelette = "MushroomOmelette"; } public static class GearValidator { public sealed class GearCheckResult { public bool IsValid; public readonly List Issues = new List(); public string Format(bool multiline = true) { if (IsValid) { return "Gear check passed."; } if (multiline) { return "Gear check failed:\n- " + string.Join("\n- ", Issues); } return string.Join("; ", Issues); } } public static AllowedGearRules GetActiveRules() { if (ValheimUtil.IsServerAuthority()) { return JsonStorage.AllowedGear ?? AllowedGearRules.CreateDefaults(); } return RaceNetSync.ClientAllowedGear ?? AllowedGearRules.CreateDefaults(); } public static GearCheckResult CheckStartGear(Player player) { GearCheckResult gearCheckResult = new GearCheckResult { IsValid = true }; if ((Object)(object)player == (Object)null || !ModConfig.EnableGearCheck.Value) { return gearCheckResult; } AllowedGearRules activeRules = GetActiveRules(); ValidateArmor(player, activeRules, gearCheckResult); ValidateConsumables(player, activeRules, gearCheckResult, atStart: true); gearCheckResult.IsValid = gearCheckResult.Issues.Count == 0; return gearCheckResult; } public static GearCheckResult CheckRuntimeGear(Player player, bool ignored = true) { GearCheckResult gearCheckResult = new GearCheckResult { IsValid = true }; if ((Object)(object)player == (Object)null || !ModConfig.EnableGearCheck.Value) { return gearCheckResult; } AllowedGearRules activeRules = GetActiveRules(); ValidateArmor(player, activeRules, gearCheckResult); gearCheckResult.IsValid = gearCheckResult.Issues.Count == 0; return gearCheckResult; } private static void ValidateArmor(Player player, AllowedGearRules rules, GearCheckResult result) { RequireSlot(((Humanoid)player).m_helmetItem, rules.Helmet, "helmet", result); RequireSlot(((Humanoid)player).m_chestItem, rules.Chest, "chest", result); RequireSlot(((Humanoid)player).m_legItem, rules.Legs, "legs", result); ValidateCape(((Humanoid)player).m_shoulderItem, rules.Cape, result); } private static void ValidateCape(ItemData cape, string allowedCape, GearCheckResult result) { if (cape == null) { return; } string prefabName = GetPrefabName(cape); if (!string.IsNullOrEmpty(prefabName)) { if (string.IsNullOrWhiteSpace(allowedCape)) { result.Issues.Add("Disallowed cape: " + GetDisplayName(cape) + " (no cape allowed)."); } else if (!string.Equals(prefabName, allowedCape.Trim(), StringComparison.OrdinalIgnoreCase)) { result.Issues.Add("Disallowed cape: " + GetDisplayName(cape) + " (allowed: " + allowedCape.Trim() + " only, or no cape)."); } } } private static void RequireSlot(ItemData item, string requiredPrefab, string slot, GearCheckResult result) { if (string.IsNullOrWhiteSpace(requiredPrefab)) { if (item != null) { result.Issues.Add("Slot " + slot + " must be empty (have " + GetDisplayName(item) + ")."); } } else if (item == null) { result.Issues.Add("Missing required " + slot + ": " + requiredPrefab.Trim() + "."); } else if (!string.Equals(GetPrefabName(item), requiredPrefab.Trim(), StringComparison.OrdinalIgnoreCase)) { result.Issues.Add("Wrong " + slot + ": " + GetDisplayName(item) + " (required " + requiredPrefab.Trim() + ")."); } } private static void ValidateConsumables(Player player, AllowedGearRules rules, GearCheckResult result, bool atStart) { if (atStart) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { result.Issues.Add("Could not read inventory."); return; } RequireConsumableCount(inventory, rules.AntiStingPrefab, rules.RequiredAntiSting, "Anti-Sting", result); RequireConsumableCount(inventory, rules.RatatoskPrefab, rules.RequiredRatatosk, "Tonic of Ratatosk", result); RequireConsumableCount(inventory, rules.SaladPrefab, rules.RequiredSalad, "Salad", result); RequireConsumableCount(inventory, rules.BloodPuddingPrefab, rules.RequiredBloodPudding, "Blood Pudding", result); RequireConsumableCount(inventory, rules.MushroomOmelettePrefab, rules.RequiredMushroomOmelette, "Mushroom Omelette", result); } } private static void RequireConsumableCount(Inventory inventory, string prefab, int required, string label, GearCheckResult result) { if (required > 0 && !string.IsNullOrWhiteSpace(prefab)) { int num = CountPrefabInInventory(inventory, prefab.Trim()); if (num < required) { result.Issues.Add($"Need {required} {label} ({prefab.Trim()}) (have {num})."); } } } private static int CountPrefabInInventory(Inventory inventory, string prefabName) { if (inventory == null || string.IsNullOrEmpty(prefabName)) { return 0; } if ((Object)(object)ObjectDB.instance != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); string text = ((!((Object)(object)itemPrefab != (Object)null)) ? null : itemPrefab.GetComponent()?.m_itemData?.m_shared?.m_name); if (!string.IsNullOrEmpty(text)) { return inventory.CountItems(text, -1, true); } } int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && string.Equals(GetPrefabName(allItem), prefabName, StringComparison.OrdinalIgnoreCase)) { num += Math.Max(1, allItem.m_stack); } } return num; } public static string GetPrefabName(ItemData item) { if (item == null) { return string.Empty; } if ((Object)(object)item.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name; } return item.m_shared?.m_name ?? string.Empty; } private static string GetDisplayName(ItemData item) { string prefabName = GetPrefabName(item); if (!string.IsNullOrEmpty(prefabName)) { return prefabName; } return item?.m_shared?.m_name ?? "unknown item"; } public static bool IsConsumableItem(ItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 if (item?.m_shared == null) { return false; } if ((int)item.m_shared.m_itemType == 2) { return true; } if (item.m_shared.m_food > 0.01f) { return true; } return (Object)(object)item.m_shared.m_consumeStatusEffect != (Object)null; } public static bool IsAllowedRaceConsumable(ItemData item) { if (!IsConsumableItem(item)) { return true; } AllowedGearRules activeRules = GetActiveRules(); return IsAllowedRaceConsumablePrefab(GetPrefabName(item), activeRules); } public static bool IsAllowedRaceConsumablePrefab(string prefabName, AllowedGearRules rules = null) { if (string.IsNullOrWhiteSpace(prefabName)) { return false; } if (rules == null) { rules = GetActiveRules(); } if (!string.Equals(prefabName, rules.AntiStingPrefab, StringComparison.OrdinalIgnoreCase) && !string.Equals(prefabName, rules.RatatoskPrefab, StringComparison.OrdinalIgnoreCase) && !string.Equals(prefabName, rules.SaladPrefab, StringComparison.OrdinalIgnoreCase) && !string.Equals(prefabName, rules.BloodPuddingPrefab, StringComparison.OrdinalIgnoreCase)) { return string.Equals(prefabName, rules.MushroomOmelettePrefab, StringComparison.OrdinalIgnoreCase); } return true; } public static bool IsFoodItem(ItemData item) { if (item?.m_shared != null) { return item.m_shared.m_food > 0.01f; } return false; } public static string FormatRequiredLoadout() { return GetActiveRules().Format(); } public static AllowedGearRules CaptureFromPlayer(Player player) { AllowedGearRules allowedGearRules = GetActiveRules().Clone(); if ((Object)(object)player == (Object)null) { return allowedGearRules; } allowedGearRules.Helmet = GetPrefabName(((Humanoid)player).m_helmetItem); allowedGearRules.Chest = GetPrefabName(((Humanoid)player).m_chestItem); allowedGearRules.Legs = GetPrefabName(((Humanoid)player).m_legItem); allowedGearRules.Cape = GetPrefabName(((Humanoid)player).m_shoulderItem); List list = new List(); AddUniqueHandPrefab(list, GetPrefabName(((Humanoid)player).m_rightItem)); AddUniqueHandPrefab(list, GetPrefabName(((Humanoid)player).m_leftItem)); allowedGearRules.AllowedHandItems = string.Join(",", list); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { if (!string.IsNullOrWhiteSpace(allowedGearRules.AntiStingPrefab)) { allowedGearRules.RequiredAntiSting = CountPrefabInInventory(inventory, allowedGearRules.AntiStingPrefab); } if (!string.IsNullOrWhiteSpace(allowedGearRules.RatatoskPrefab)) { allowedGearRules.RequiredRatatosk = CountPrefabInInventory(inventory, allowedGearRules.RatatoskPrefab); } if (!string.IsNullOrWhiteSpace(allowedGearRules.SaladPrefab)) { allowedGearRules.RequiredSalad = CountPrefabInInventory(inventory, allowedGearRules.SaladPrefab); } if (!string.IsNullOrWhiteSpace(allowedGearRules.BloodPuddingPrefab)) { allowedGearRules.RequiredBloodPudding = CountPrefabInInventory(inventory, allowedGearRules.BloodPuddingPrefab); } if (!string.IsNullOrWhiteSpace(allowedGearRules.MushroomOmelettePrefab)) { allowedGearRules.RequiredMushroomOmelette = CountPrefabInInventory(inventory, allowedGearRules.MushroomOmelettePrefab); } } return allowedGearRules; } private static void AddUniqueHandPrefab(List names, string prefab) { if (string.IsNullOrWhiteSpace(prefab)) { return; } foreach (string name in names) { if (string.Equals(name, prefab, StringComparison.OrdinalIgnoreCase)) { return; } } names.Add(prefab); } } internal enum HudPanelId { Runner, Standings, RaceBanner, Finish, Registered, WorldRecords } internal static class HudPanelLayout { private const float DragTitleHeight = 20f; private const int PanelCount = 6; private static readonly Rect[] Positions = (Rect[])(object)new Rect[6]; private static readonly bool[] HasPosition = new bool[6]; private static readonly bool[] PanelVisible = new bool[6]; private static int _draggingId = -1; private static GUIStyle _dragTitleStyle; private static GUIStyle _closeButtonStyle; private static bool _loaded; public static float TitleBarHeight => 20f; public static HudPanelId Normalize(HudPanelId id) { if (id != HudPanelId.Finish) { return id; } return HudPanelId.RaceBanner; } public static bool ShouldDrawPanel(HudPanelId id, bool layoutMode = false) { EnsureLoaded(); return PanelVisible[(int)Normalize(id)]; } public static bool IsPanelVisible(HudPanelId id) { EnsureLoaded(); return PanelVisible[(int)Normalize(id)]; } public static void SetPanelVisible(HudPanelId id, bool visible) { EnsureLoaded(); int num = (int)Normalize(id); PanelVisible[num] = visible; PlayerPrefs.SetInt(PrefKey(num, "Visible"), visible ? 1 : 0); PlayerPrefs.Save(); } public static void ShowAllPanels() { for (int i = 0; i < 6; i++) { if (i != 3) { SetPanelVisible((HudPanelId)i, visible: true); } } } public static string GetPanelTitle(HudPanelId id) { return Normalize(id) switch { HudPanelId.Runner => "Race stats", HudPanelId.Standings => "Live standings", HudPanelId.RaceBanner => "Race banner", HudPanelId.Registered => "Registration", _ => id.ToString(), }; } public static IEnumerable ToggleablePanels() { yield return HudPanelId.Runner; yield return HudPanelId.Standings; yield return HudPanelId.RaceBanner; yield return HudPanelId.Registered; } public static bool Resolve(HudPanelId id, Rect defaultRect, bool draggable, string title, out Rect panelRect) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected I4, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) EnsureLoaded(); id = Normalize(id); int num = (int)id; Rect val = (HasPosition[num] ? Positions[num] : defaultRect); ((Rect)(ref val)).width = ((Rect)(ref defaultRect)).width; ((Rect)(ref val)).height = ((Rect)(ref defaultRect)).height + (draggable ? 20f : 0f); if (!draggable) { Positions[num] = val; panelRect = val; return true; } EnsureDragStyle(); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 20f); float num2 = 22f; GUI.Label(new Rect(((Rect)(ref val2)).x + 8f, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width - num2 - 10f, ((Rect)(ref val2)).height), title + " (drag)", _dragTitleStyle); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref val2)).xMax - num2 - 2f, ((Rect)(ref val2)).y + 1f, num2, ((Rect)(ref val2)).height - 2f); if (GUI.Button(val3, "X", _closeButtonStyle)) { SetPanelVisible(id, visible: false); panelRect = val; return false; } Event current = Event.current; EventType type = current.type; switch ((int)type) { case 0: if (((Rect)(ref val2)).Contains(current.mousePosition) && !((Rect)(ref val3)).Contains(current.mousePosition)) { _draggingId = num; current.Use(); } break; case 3: if (_draggingId == num) { ((Rect)(ref val)).x = ((Rect)(ref val)).x + current.delta.x; ((Rect)(ref val)).y = ((Rect)(ref val)).y + current.delta.y; current.Use(); } break; case 1: if (_draggingId == num) { _draggingId = -1; SavePosition(num, val); current.Use(); } break; } Positions[num] = val; HasPosition[num] = true; panelRect = val; return true; } public static Rect ContentRect(Rect panelRect, bool draggable) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) if (!draggable) { return panelRect; } return new Rect(((Rect)(ref panelRect)).x, ((Rect)(ref panelRect)).y + 20f, ((Rect)(ref panelRect)).width, ((Rect)(ref panelRect)).height - 20f); } public static void ResetAll() { for (int i = 0; i < HasPosition.Length; i++) { HasPosition[i] = false; PanelVisible[i] = true; PlayerPrefs.DeleteKey(PrefKey(i, "X")); PlayerPrefs.DeleteKey(PrefKey(i, "Y")); PlayerPrefs.DeleteKey(PrefKey(i, "Visible")); } PlayerPrefs.Save(); } private static void EnsureLoaded() { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (_loaded) { return; } _loaded = true; for (int i = 0; i < HasPosition.Length; i++) { PanelVisible[i] = !PlayerPrefs.HasKey(PrefKey(i, "Visible")) || PlayerPrefs.GetInt(PrefKey(i, "Visible")) == 1; if (PlayerPrefs.HasKey(PrefKey(i, "X"))) { Positions[i] = new Rect(PlayerPrefs.GetFloat(PrefKey(i, "X")), PlayerPrefs.GetFloat(PrefKey(i, "Y")), 0f, 0f); HasPosition[i] = true; } } } private static void SavePosition(int index, Rect rect) { PlayerPrefs.SetFloat(PrefKey(index, "X"), ((Rect)(ref rect)).x); PlayerPrefs.SetFloat(PrefKey(index, "Y"), ((Rect)(ref rect)).y); PlayerPrefs.Save(); } private static string PrefKey(int index, string suffix) { return $"RunningMan.Hud.{index}.{suffix}"; } private static void EnsureDragStyle() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00af: Expected O, but got Unknown if (_dragTitleStyle == null) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 12, fontStyle = (FontStyle)1, alignment = (TextAnchor)3, clipping = (TextClipping)0, padding = new RectOffset(0, 0, 2, 2) }; val.normal.textColor = new Color(0.75f, 0.85f, 1f); _dragTitleStyle = val; _closeButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 11, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, padding = new RectOffset(0, 0, 0, 0), margin = new RectOffset(0, 0, 0, 0) }; } } } public static class Leaderboard { public static void RecordCompletedRun(RaceRecord record) { RaceDatabase database = JsonStorage.Database; if (string.IsNullOrEmpty(record.TrackId)) { record.TrackId = TrackIdentity.GetId(JsonStorage.Track); } database.Runs.Add(record); string playerId = record.PlayerId; database.LastRuns[playerId] = record; if (!database.PersonalBests.TryGetValue(playerId, out var value) || record.TotalTimeMs < value.TotalTimeMs) { database.PersonalBests[playerId] = record; } JsonStorage.SaveDatabase(); } public static RaceRecord GetPersonalBest(long playerId) { JsonStorage.Database.PersonalBests.TryGetValue(playerId.ToString(), out var value); return value; } public static RaceRecord GetLastRun(long playerId) { JsonStorage.Database.LastRuns.TryGetValue(playerId.ToString(), out var value); return value; } public static IReadOnlyList GetWorldRecords(TrackConfig track, int limit) { string trackId = TrackIdentity.GetId(track); if (string.IsNullOrEmpty(trackId)) { return new List(); } return (from record in JsonStorage.Database.Runs where string.Equals(record.TrackId, trackId, StringComparison.OrdinalIgnoreCase) orderby record.TotalTimeMs select record).Take(limit).ToList(); } public static int ClearRecordsForTrackId(string trackId) { if (string.IsNullOrEmpty(trackId)) { return 0; } RaceDatabase database = JsonStorage.Database; int result = database.Runs.RemoveAll((RaceRecord record) => string.Equals(record.TrackId, trackId, StringComparison.OrdinalIgnoreCase)); PruneDerivedRecords(database, trackId); JsonStorage.SaveDatabase(); return result; } public static int ClearWorldRecords(TrackConfig track) { return ClearRecordsForTrackId(TrackIdentity.GetId(track)); } public static int ClearAllWorldRecords() { RaceDatabase database = JsonStorage.Database; int count = database.Runs.Count; database.Runs.Clear(); database.PersonalBests.Clear(); database.LastRuns.Clear(); JsonStorage.SaveDatabase(); return count; } public static string FormatWorldRecords(TrackConfig track, int limit) { string displayName = TrackIdentity.GetDisplayName(track); IReadOnlyList worldRecords = GetWorldRecords(track, limit); if (worldRecords.Count == 0) { return "No world records for " + displayName + " yet."; } List list = new List { displayName + " — World Records:" }; for (int i = 0; i < worldRecords.Count; i++) { RaceRecord raceRecord = worldRecords[i]; list.Add($"{i + 1}. {raceRecord.Player} - {raceRecord.TotalTime} ({raceRecord.Date})"); } return string.Join("\n", list); } public static List BuildWorldRecordEntries(TrackConfig track, int limit) { List list = new List(); IReadOnlyList worldRecords = GetWorldRecords(track, limit); for (int i = 0; i < worldRecords.Count; i++) { RaceRecord raceRecord = worldRecords[i]; list.Add(new WorldRecordEntry { Place = i + 1, PlayerName = raceRecord.Player, Time = raceRecord.TotalTime, TimeMs = raceRecord.TotalTimeMs, Date = raceRecord.Date }); } return list; } public static string FormatPersonalBest(RaceRecord record) { if (record == null) { return "No personal best recorded yet."; } return record.Player + " PB: " + record.TotalTime + " on " + record.Date; } public static string FormatLastRun(RaceRecord record) { if (record == null) { return "No completed runs recorded yet."; } return record.Player + " last run: " + record.TotalTime + " on " + record.Date; } private static void PruneDerivedRecords(RaceDatabase database, string clearedTrackId) { Dictionary> dictionary = (from record in database.Runs group record by record.PlayerId).ToDictionary((IGrouping group) => group.Key, (IGrouping group) => group.OrderBy((RaceRecord record) => record.TotalTimeMs).ToList()); Dictionary dictionary2 = new Dictionary(StringComparer.Ordinal); Dictionary dictionary3 = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair> item in dictionary) { if (item.Value.Count != 0) { dictionary2[item.Key] = item.Value[0]; dictionary3[item.Key] = item.Value[item.Value.Count - 1]; } } database.PersonalBests = dictionary2; database.LastRuns = dictionary3; } } public sealed class RaceClientGearMonitor : MonoBehaviour { private float _nextCheckTime; private bool _reportedViolation; private void Update() { if (!ModConfig.EnableGearCheck.Value || (Object)(object)Player.m_localPlayer == (Object)null || Time.time < _nextCheckTime) { return; } _nextCheckTime = Time.time + Mathf.Max(0.25f, ModConfig.UpdateInterval.Value); RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); if (localRunner == null || localRunner.StartUtcTicks <= 0 || localRunner.Finished || localRunner.Disqualified) { _reportedViolation = false; } else { if (RaceNetSync.ClientState == null || RaceNetSync.ClientState.Phase != 3 || _reportedViolation) { return; } GearValidator.GearCheckResult gearCheckResult = GearValidator.CheckRuntimeGear(Player.m_localPlayer); if (!gearCheckResult.IsValid) { _reportedViolation = true; string text = ((gearCheckResult.Issues.Count > 0) ? gearCheckResult.Issues[0] : "illegal gear change"); localRunner.Disqualified = true; localRunner.DisqualifiedReason = text; if (localRunner.FinishTimeMs <= 0) { localRunner.FinishTimeMs = (long)(DateTime.UtcNow - new DateTime(localRunner.StartUtcTicks, DateTimeKind.Utc)).TotalMilliseconds; } RaceGui.ShowYellowHud("DISQUALIFIED: " + text); RaceGui.ShowInfoPanel("Disqualified", text, 12f); RaceNetSync.SendGearViolation(text); } } } } public sealed class RaceClientPositionReporter : MonoBehaviour { private float _nextSendTime; private void Update() { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || Time.time < _nextSendTime) { return; } RaceStateSnapshot clientState = RaceNetSync.ClientState; if (clientState == null || clientState.Phase != 3) { _nextSendTime = Time.time + 0.5f; return; } RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); if (localRunner == null || localRunner.StartUtcTicks <= 0 || localRunner.Finished || localRunner.Disqualified) { _nextSendTime = Time.time + 0.5f; return; } _nextSendTime = Time.time + Mathf.Clamp(ModConfig.UpdateInterval.Value, 0.05f, 0.2f); Vector3 position = ((Component)Player.m_localPlayer).transform.position; RaceNetSync.SendRunnerPosition(Player.m_localPlayer.GetPlayerID(), position); } } public enum RaceSoundSlot { Start, Checkpoint, Finish, Countdown, FalseStart } public static class RaceCustomAudio { private static readonly Dictionary Clips = new Dictionary(); private static readonly HashSet LoadAttempted = new HashSet(); private static readonly HashSet Loading = new HashSet(); private static MonoBehaviour _host; private static AudioSource _oneShotSource; private static AudioSource _streamSource; private static bool _folderEnsured; public static void Initialize(MonoBehaviour host) { _host = host; _oneShotSource = ((Component)host).gameObject.AddComponent(); _oneShotSource.playOnAwake = false; _oneShotSource.spatialBlend = 0f; _oneShotSource.spatialize = false; _oneShotSource.bypassEffects = true; _oneShotSource.bypassListenerEffects = true; _oneShotSource.bypassReverbZones = true; _streamSource = ((Component)host).gameObject.AddComponent(); _streamSource.playOnAwake = false; _streamSource.spatialBlend = 0f; _streamSource.spatialize = false; _streamSource.loop = false; _streamSource.bypassEffects = true; _streamSource.bypassListenerEffects = true; _streamSource.bypassReverbZones = true; EnsureSoundsFolder(); } public static string GetSoundsFolderPath() { string text = ModConfig.CustomSoundsFolder.Value?.Trim() ?? "Sounds"; text = text.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); string runningManPluginDirectory = GetRunningManPluginDirectory(); if (Path.IsPathRooted(text)) { return text; } return Path.Combine(runningManPluginDirectory, text); } private static string GetRunningManPluginDirectory() { try { string location = typeof(RunningManPlugin).Assembly.Location; if (!string.IsNullOrEmpty(location)) { string directoryName = Path.GetDirectoryName(location); if (!string.IsNullOrEmpty(directoryName)) { return directoryName; } } } catch { } return Path.Combine(Paths.PluginPath, "RunningMan"); } public static void PreloadAll() { if (!((Object)(object)_host == (Object)null) && ModConfig.UseCustomRaceSounds.Value) { _host.StartCoroutine(PreloadRoutine()); } } public static bool TryPlay(RaceSoundSlot slot, float volume, Action playFallback) { if (!ModConfig.UseCustomRaceSounds.Value) { return false; } if (Clips.TryGetValue(slot, out var value) && (Object)(object)value != (Object)null) { PlayClip(value, volume); return true; } if (LoadAttempted.Contains(slot)) { return false; } if ((Object)(object)_host == (Object)null) { return false; } if (!HasCustomFile(slot)) { LoadAttempted.Add(slot); return false; } _host.StartCoroutine(LoadAndPlay(slot, volume, playFallback)); return true; } public static bool HasCustomFile(RaceSoundSlot slot) { string path; return TryResolveFilePath(GetConfiguredFileName(slot), out path); } private static IEnumerator PreloadRoutine() { yield return LoadClip(RaceSoundSlot.Start); yield return LoadClip(RaceSoundSlot.Checkpoint); yield return LoadClip(RaceSoundSlot.Finish); yield return LoadClip(RaceSoundSlot.Countdown); yield return LoadClip(RaceSoundSlot.FalseStart); } private static IEnumerator LoadAndPlay(RaceSoundSlot slot, float volume, Action playFallback) { if (!Loading.Contains(slot)) { Loading.Add(slot); yield return LoadClip(slot); Loading.Remove(slot); if (Clips.TryGetValue(slot, out var value) && (Object)(object)value != (Object)null) { PlayClip(value, volume); } else { playFallback?.Invoke(); } } } private static IEnumerator LoadClip(RaceSoundSlot slot) { if (LoadAttempted.Contains(slot)) { yield break; } LoadAttempted.Add(slot); string configuredFileName = GetConfiguredFileName(slot); if (!TryResolveFilePath(configuredFileName, out var path)) { RunningManPlugin.Log.LogInfo((object)$"RunningMan: no custom sound for {slot} (expected {configuredFileName} in {GetSoundsFolderPath()})."); yield break; } string text = "file:///" + path.Replace("\\", "/"); AudioType audioType = GetAudioType(path); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(text, audioType); try { yield return request.SendWebRequest(); if (!string.IsNullOrEmpty(request.error)) { RunningManPlugin.Log.LogWarning((object)("RunningMan: failed to load custom sound '" + path + "': " + request.error)); yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null) { RunningManPlugin.Log.LogWarning((object)("RunningMan: custom sound '" + path + "' loaded empty.")); yield break; } ((Object)content).name = Path.GetFileName(path); Clips[slot] = content; RunningManPlugin.Log.LogInfo((object)$"RunningMan: loaded custom sound '{((Object)content).name}' for {slot}."); } finally { ((IDisposable)request)?.Dispose(); } } public static bool TryPlayStream(RaceSoundSlot slot, float volume) { if (!ModConfig.UseCustomRaceSounds.Value || (Object)(object)_streamSource == (Object)null) { return false; } if (Clips.TryGetValue(slot, out var value) && (Object)(object)value != (Object)null) { PlayStream(value, volume); return true; } if (LoadAttempted.Contains(slot) || (Object)(object)_host == (Object)null || !HasCustomFile(slot)) { return false; } _host.StartCoroutine(LoadAndPlayStream(slot, volume)); return true; } public static void StopStream() { if ((Object)(object)_streamSource != (Object)null && _streamSource.isPlaying) { _streamSource.Stop(); } } private static IEnumerator LoadAndPlayStream(RaceSoundSlot slot, float volume) { if (!Loading.Contains(slot)) { Loading.Add(slot); yield return LoadClip(slot); Loading.Remove(slot); if (Clips.TryGetValue(slot, out var value) && (Object)(object)value != (Object)null) { PlayStream(value, volume); } } } private static void PlayStream(AudioClip clip, float volume) { if (!((Object)(object)_streamSource == (Object)null) && !((Object)(object)clip == (Object)null)) { _streamSource.Stop(); _streamSource.clip = clip; _streamSource.volume = Mathf.Clamp(volume, 0f, 2f); _streamSource.Play(); } } private static void PlayClip(AudioClip clip, float volume) { if (!((Object)(object)_oneShotSource == (Object)null) && !((Object)(object)clip == (Object)null)) { _oneShotSource.PlayOneShot(clip, Mathf.Clamp(volume, 0f, 2f)); } } private static string GetConfiguredFileName(RaceSoundSlot slot) { return slot switch { RaceSoundSlot.Start => ModConfig.CustomStartSoundFile.Value, RaceSoundSlot.Checkpoint => ModConfig.CustomCheckpointSoundFile.Value, RaceSoundSlot.Finish => ModConfig.CustomFinishSoundFile.Value, RaceSoundSlot.Countdown => ModConfig.CustomCountdownSoundFile.Value, RaceSoundSlot.FalseStart => ModConfig.CustomFalseStartSoundFile.Value, _ => string.Empty, }; } private static bool TryResolveFilePath(string configuredName, out string path) { path = null; if (string.IsNullOrWhiteSpace(configuredName)) { return false; } string soundsFolderPath = GetSoundsFolderPath(); string text = configuredName.Trim(); string text2 = Path.Combine(soundsFolderPath, text); if (File.Exists(text2)) { path = text2; return true; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); string[] array = new string[3] { ".mp3", ".wav", ".ogg" }; foreach (string text3 in array) { text2 = Path.Combine(soundsFolderPath, fileNameWithoutExtension + text3); if (File.Exists(text2)) { path = text2; return true; } } return false; } private static AudioType GetAudioType(string path) { string text = Path.GetExtension(path)?.ToLowerInvariant(); if (!(text == ".ogg")) { if (text == ".wav") { return (AudioType)20; } return (AudioType)13; } return (AudioType)14; } private static void EnsureSoundsFolder() { if (_folderEnsured) { return; } _folderEnsured = true; try { string soundsFolderPath = GetSoundsFolderPath(); Directory.CreateDirectory(soundsFolderPath); RunningManPlugin.Log.LogInfo((object)("RunningMan custom sounds folder: " + soundsFolderPath + " (add start.mp3, checkpoint.mp3, finish.mp3, countdown.mp3, false_start.mp3)")); } catch (Exception ex) { RunningManPlugin.Log.LogWarning((object)("RunningMan: could not create sounds folder: " + ex.Message)); } } } public sealed class RaceDebugDrawer : MonoBehaviour { private static Texture2D _lineTexture; private static GUIStyle _labelStyle; private static readonly Color CheckpointColor = new Color(1f, 0.92f, 0.16f); private void OnEnable() { RaceNetSync.TrackUpdated += MarkDirty; RaceNetSync.StateUpdated += MarkDirty; } private void OnDisable() { RaceNetSync.TrackUpdated -= MarkDirty; RaceNetSync.StateUpdated -= MarkDirty; } private static void MarkDirty() { } private void OnGUI() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (!RaceNetSync.IsDebugModeActive() || (int)Event.current.type != 7) { return; } TrackConfig activeTrack = GetActiveTrack(); if (activeTrack == null) { return; } Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } if (activeTrack.HasStart) { DrawGate(main, activeTrack.StartGate, Color.green, "START", RaceManager.GateKind.Start, 0); DrawStartingArea(main, activeTrack.StartGate); } if (activeTrack.HasFinish) { DrawGate(main, activeTrack.FinishGate, Color.red, "FINISH", RaceManager.GateKind.Finish, 0); } if (activeTrack.Checkpoints != null) { for (int i = 0; i < activeTrack.Checkpoints.Count; i++) { Checkpoint checkpoint = activeTrack.Checkpoints[i]; DrawGate(main, checkpoint.Gate, CheckpointColor, $"CP{checkpoint.Index}", RaceManager.GateKind.Checkpoint, checkpoint.Index); } } DrawRacePath(main, activeTrack); } private static void DrawRacePath(Camera camera, TrackConfig track) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) TrackPath.PathData pathData = TrackPath.Build(track); if (pathData.Points.Count >= 2) { Color color = default(Color); ((Color)(ref color))..ctor(1f, 0.55f, 0.15f, 0.9f); for (int i = 0; i < pathData.Points.Count - 1; i++) { Vector3 worldA = pathData.Points[i] + Vector3.up * 2f; Vector3 worldB = pathData.Points[i + 1] + Vector3.up * 2f; DrawWorldLine(camera, worldA, worldB, color, 3f); } DrawWorldLabel(camera, pathData.Points[pathData.Points.Count / 2] + Vector3.up * 4f, $"PATH {pathData.TotalLength:0}m", color); } } private static TrackConfig GetActiveTrack() { return RaceNetSync.GetActiveTrack(); } private static void DrawGate(Camera camera, RaceGate gate, Color color, string label, RaceManager.GateKind kind, int checkpointIndex) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (gate != null && gate.IsConfigured()) { Vector3 val = gate.GetPointA() + Vector3.up * 1.5f; Vector3 val2 = gate.GetPointB() + Vector3.up * 1.5f; Vector3 worldPoint = gate.GetMidpoint() + Vector3.up * 3f; DrawWorldLine(camera, val, val2, color, RaceGateEditor.EditMode ? 7f : 5f); DrawWorldLabel(camera, worldPoint, label, color); DrawEndpointDot(camera, val, color, kind, checkpointIndex, pointA: true); DrawEndpointDot(camera, val2, color, kind, checkpointIndex, pointA: false); } } private static void DrawEndpointDot(Camera camera, Vector3 world, Color color, RaceManager.GateKind kind, int checkpointIndex, bool pointA) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) float size = (RaceGateEditor.EditMode ? 12f : 8f); Color color2 = color; RaceGateEditor.EndpointRef endpoint2; if (RaceGateEditor.TryGetCarried(out var endpoint) && endpoint.Kind == kind && endpoint.CheckpointIndex == checkpointIndex && endpoint.PointA == pointA) { size = 22f; color2 = Color.white; world = endpoint.Position + Vector3.up * 1.5f; } else if (RaceGateEditor.TryGetHighlight(out endpoint2) && endpoint2.Kind == kind && endpoint2.CheckpointIndex == checkpointIndex && endpoint2.PointA == pointA) { size = 18f; color2 = Color.cyan; } DrawWorldDot(camera, world, color2, size); } private static void DrawStartingArea(Camera camera, RaceGate startGate) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) TriggerDetector.GetStartingAreaCorners(startGate, ModConfig.StartingAreaOffset.Value, ModConfig.StartingAreaDepth.Value, ModConfig.StartingAreaSidePadding.Value, out var c, out var c2, out var c3, out var c4); Color color = default(Color); ((Color)(ref color))..ctor(0.25f, 0.85f, 1f, 0.95f); DrawWorldLine(camera, c, c2, color, 4f); DrawWorldLine(camera, c2, c3, color, 4f); DrawWorldLine(camera, c3, c4, color, 4f); DrawWorldLine(camera, c4, c, color, 4f); DrawWorldLabel(camera, (c + c2 + c3 + c4) * 0.25f + Vector3.up * 1.5f, "START GRID", color); } private static void DrawWorldLine(Camera camera, Vector3 worldA, Vector3 worldB, Color color, float thickness) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Vector3 val = camera.WorldToScreenPoint(worldA); Vector3 val2 = camera.WorldToScreenPoint(worldB); if (!(val.z <= 0f) && !(val2.z <= 0f)) { val.y = (float)Screen.height - val.y; val2.y = (float)Screen.height - val2.y; DrawScreenLine(val, val2, color, thickness); } } private static void DrawWorldDot(Camera camera, Vector3 worldPoint, Color color, float size) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) Vector3 val = camera.WorldToScreenPoint(worldPoint); if (!(val.z <= 0f)) { val.y = (float)Screen.height - val.y; Rect val2 = new Rect(val.x - size * 0.5f, val.y - size * 0.5f, size, size); EnsureLineTexture(); GUI.color = color; GUI.DrawTexture(val2, (Texture)(object)_lineTexture); GUI.color = Color.white; } } private static void DrawWorldLabel(Camera camera, Vector3 worldPoint, string label, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) Vector3 val = camera.WorldToScreenPoint(worldPoint); if (!(val.z <= 0f)) { val.y = (float)Screen.height - val.y; EnsureLabelStyle(color); Vector2 val2 = _labelStyle.CalcSize(new GUIContent(label)); GUI.Label(new Rect(val.x - val2.x * 0.5f - 6f, val.y - val2.y - 6f, val2.x + 12f, val2.y + 12f), label, _labelStyle); } } private static void EnsureLabelStyle(Color color) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (_labelStyle == null || !(_labelStyle.normal.textColor == color)) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, alignment = (TextAnchor)7, clipping = (TextClipping)0, wordWrap = false }; val.normal.textColor = color; _labelStyle = val; } } private static void DrawScreenLine(Vector3 start, Vector3 end, Color color, float width) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) EnsureLineTexture(); GUI.color = color; Vector3 val = end - start; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.01f) { GUI.color = Color.white; return; } float num = Mathf.Atan2(val.y, val.x) * 57.29578f; Matrix4x4 matrix = GUI.matrix; GUIUtility.RotateAroundPivot(num, Vector2.op_Implicit(start)); GUI.DrawTexture(new Rect(start.x, start.y, magnitude, width), (Texture)(object)_lineTexture); GUI.matrix = matrix; GUI.color = Color.white; } private static void EnsureLineTexture() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_lineTexture != (Object)null)) { _lineTexture = new Texture2D(1, 1); _lineTexture.SetPixel(0, 0, Color.white); _lineTexture.Apply(); } } } public enum RaceEventPhase { Idle, Registration, Countdown, Racing, Ready } public sealed class RaceGateEditor : MonoBehaviour { public struct EndpointRef { public RaceManager.GateKind Kind; public int CheckpointIndex; public bool PointA; public Vector3 Position; public string Label; } private EndpointRef? _carrying; private EndpointRef? _highlight; private string _status = string.Empty; private GUIStyle _hudStyle; public static RaceGateEditor Instance { get; private set; } public static bool EditMode { get; private set; } public static Vector3? PendingPlacePosition { get; set; } public static void SetEditMode(bool enabled) { EditMode = enabled; if ((Object)(object)Instance != (Object)null) { Instance._carrying = null; Instance._highlight = null; Instance._status = (enabled ? "Gate edit ON — equip Hammer, look at A/B dots, Use (E) to pick up / place." : string.Empty); } if (enabled && !RaceNetSync.IsDebugModeActive()) { ValheimUtil.RunCommand("debug on"); } } public static void ToggleEditMode() { SetEditMode(!EditMode); } private void Awake() { Instance = this; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { if (!EditMode || (Object)(object)Player.m_localPlayer == (Object)null) { _highlight = null; return; } if (_carrying.HasValue) { UpdateCarriedEndpoint(); if (WasUsePressed() && !RaceGui.IsOpen) { PlaceCarriedEndpoint(); } return; } if (RaceGui.IsOpen) { _highlight = null; return; } _highlight = FindLookedEndpoint(); if (_highlight.HasValue && IsHoldingHammer() && WasUsePressed()) { _carrying = _highlight; _status = "Picked up " + _highlight.Value.Label + ". Move, then Use (E) to place."; } } private void OnGUI() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (EditMode && !string.IsNullOrEmpty(_status)) { EnsureHudStyle(); float num = 560f; GUI.Label(new Rect((float)Screen.width * 0.5f - num * 0.5f, 24f, num, 36f), _status, _hudStyle); } } public static bool TryGetHighlight(out EndpointRef endpoint) { RaceGateEditor instance = Instance; if (instance != null && instance._highlight.HasValue) { endpoint = Instance._highlight.Value; return true; } endpoint = default(EndpointRef); return false; } public static bool TryGetCarried(out EndpointRef endpoint) { RaceGateEditor instance = Instance; if (instance != null && instance._carrying.HasValue) { endpoint = Instance._carrying.Value; return true; } endpoint = default(EndpointRef); return false; } public static Vector3 GetPlacePosition() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Camera main = Camera.main; if ((Object)(object)localPlayer == (Object)null) { return Vector3.zero; } Vector3 lookDirection = (((Object)(object)main != (Object)null) ? ((Component)main).transform.forward : ((Component)localPlayer).transform.forward); if (TriggerDetector.TryFindLookSnapTarget(((Component)localPlayer).transform.position, lookDirection, ModConfig.EndpointSnapRadius.Value, ModConfig.EndpointSnapAngle.Value, out var position, out var _)) { return position; } if (TryGetLookPiece(out var point)) { return point; } if (TryGetLookGroundPoint(out var point2)) { return point2; } return ((Component)localPlayer).transform.position; } private static bool TryGetLookPiece(out Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) point = Vector3.zero; Camera main = Camera.main; Player localPlayer = Player.m_localPlayer; if ((Object)(object)main == (Object)null || (Object)(object)localPlayer == (Object)null) { return false; } RaycastHit[] array = Physics.RaycastAll(main.ScreenPointToRay(new Vector3((float)Screen.width * 0.5f, (float)Screen.height * 0.5f, 0f)), 40f); if (array == null || array.Length == 0) { return false; } Array.Sort(array, (RaycastHit left, RaycastHit right) => ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance)); RaycastHit[] array2 = array; for (int num = 0; num < array2.Length; num++) { RaycastHit val = array2[num]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !((Component)((RaycastHit)(ref val)).collider).transform.IsChildOf(((Component)localPlayer).transform) && !((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponentInParent() == (Object)(object)localPlayer)) { Piece componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { point = ((Component)componentInParent).transform.position; return true; } } } return false; } private static bool TryGetLookGroundPoint(out Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) point = Vector3.zero; Camera main = Camera.main; Player localPlayer = Player.m_localPlayer; if ((Object)(object)main == (Object)null || (Object)(object)localPlayer == (Object)null) { return false; } Ray val = main.ScreenPointToRay(new Vector3((float)Screen.width * 0.5f, (float)Screen.height * 0.5f, 0f)); RaycastHit[] array = Physics.RaycastAll(val, 50f); if (array != null && array.Length != 0) { Array.Sort(array, (RaycastHit left, RaycastHit right) => ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance)); RaycastHit[] array2 = array; for (int num = 0; num < array2.Length; num++) { RaycastHit val2 = array2[num]; if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && !((Component)((RaycastHit)(ref val2)).collider).transform.IsChildOf(((Component)localPlayer).transform) && !((Object)(object)((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent() == (Object)(object)localPlayer)) { point = SnapToGround(((RaycastHit)(ref val2)).point, ((Component)localPlayer).transform.position.y); return true; } } } float y = ((Component)localPlayer).transform.position.y; Vector3 direction = ((Ray)(ref val)).direction; if (Mathf.Abs(direction.y) < 0.001f) { direction.y = -0.001f; } float num2 = (y - ((Ray)(ref val)).origin.y) / direction.y; if (num2 < 0.5f || num2 > 45f) { Vector3 val3 = Flatten(((Component)main).transform.forward); point = SnapToGround(((Component)localPlayer).transform.position + val3 * 6f, y); return true; } point = SnapToGround(((Ray)(ref val)).origin + direction * num2, y); return true; } private static Vector3 SnapToGround(Vector3 approximate, float fallbackY) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) float num = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(approximate, ref num)) { return new Vector3(approximate.x, num, approximate.z); } return new Vector3(approximate.x, fallbackY, approximate.z); } private static Vector3 Flatten(Vector3 vector) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) vector.y = 0f; if (((Vector3)(ref vector)).sqrMagnitude < 0.0001f) { return Vector3.forward; } return ((Vector3)(ref vector)).normalized; } private void UpdateCarriedEndpoint() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (_carrying.HasValue) { EndpointRef value = _carrying.Value; Vector3 placePosition = GetPlacePosition(); value.Position = placePosition; _carrying = value; ApplyOptimisticPreview(value); Player localPlayer = Player.m_localPlayer; Camera main = Camera.main; if ((Object)(object)localPlayer != (Object)null && TriggerDetector.TryFindLookSnapTarget(((Component)localPlayer).transform.position, ((Object)(object)main != (Object)null) ? ((Component)main).transform.forward : ((Component)localPlayer).transform.forward, ModConfig.EndpointSnapRadius.Value, ModConfig.EndpointSnapAngle.Value, out var _, out var prefabHint)) { _status = "Carrying " + value.Label + " — snapping to " + prefabHint + ". Use (E) to place."; } else { _status = "Carrying " + value.Label + " — ground place. Aim at a torch to snap. Use (E) to place."; } } } private void PlaceCarriedEndpoint() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (_carrying.HasValue) { EndpointRef value = _carrying.Value; Vector3 placePosition = GetPlacePosition(); PendingPlacePosition = placePosition; string args = BuildSetpointCommand(value, placePosition); _status = "Placed " + value.Label + "."; _carrying = null; ValheimUtil.RunCommand(args); } } private static string BuildSetpointCommand(EndpointRef endpoint, Vector3 position) { string text = (endpoint.PointA ? "a" : "b"); return endpoint.Kind switch { RaceManager.GateKind.Start => "setpoint start " + text, RaceManager.GateKind.Finish => "setpoint finish " + text, _ => $"setpoint checkpoint {endpoint.CheckpointIndex} {text}", }; } private static void ApplyOptimisticPreview(EndpointRef endpoint) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) TrackConfig activeTrack = RaceNetSync.GetActiveTrack(); if (activeTrack == null) { return; } RaceGate raceGate = null; switch (endpoint.Kind) { case RaceManager.GateKind.Start: raceGate = activeTrack.StartGate; break; case RaceManager.GateKind.Finish: raceGate = activeTrack.FinishGate; break; case RaceManager.GateKind.Checkpoint: raceGate = activeTrack.Checkpoints?.Find((Checkpoint item) => item.Index == endpoint.CheckpointIndex)?.Gate; break; } raceGate?.SetEndpoint(endpoint.PointA, endpoint.Position); } private EndpointRef? FindLookedEndpoint() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; TrackConfig activeTrack = RaceNetSync.GetActiveTrack(); if ((Object)(object)main == (Object)null || activeTrack == null || (Object)(object)Player.m_localPlayer == (Object)null) { return null; } Ray ray = main.ScreenPointToRay(new Vector3((float)Screen.width * 0.5f, (float)Screen.height * 0.5f, 0f)); EndpointRef? best = null; float bestScore = float.MaxValue; if (activeTrack.HasStart) { Consider(RaceManager.GateKind.Start, 0, activeTrack.StartGate, "START"); } if (activeTrack.HasFinish) { Consider(RaceManager.GateKind.Finish, 0, activeTrack.FinishGate, "FINISH"); } if (activeTrack.Checkpoints != null) { foreach (Checkpoint checkpoint in activeTrack.Checkpoints) { Consider(RaceManager.GateKind.Checkpoint, checkpoint.Index, checkpoint.Gate, $"CP{checkpoint.Index}"); } } return best; void Consider(RaceManager.GateKind kind, int index, RaceGate gate, string prefix) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (gate != null && gate.IsConfigured()) { Score(kind, index, pointA: true, gate.GetPointA(), prefix + " A"); Score(kind, index, pointA: false, gate.GetPointB(), prefix + " B"); } } void Score(RaceManager.GateKind kind, int index, bool pointA, Vector3 world, string label) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = world + Vector3.up * 1.2f - ((Ray)(ref ray)).origin; float magnitude = ((Vector3)(ref val)).magnitude; if (!(magnitude > 12f) && !(magnitude < 0.01f)) { float num = Vector3.Angle(((Ray)(ref ray)).direction, val); if (!(num > 18f)) { float num2 = num * 2f + magnitude; if (!(num2 >= bestScore)) { bestScore = num2; best = new EndpointRef { Kind = kind, CheckpointIndex = index, PointA = pointA, Position = world, Label = label }; } } } } } private static bool IsHoldingHammer() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } ItemData rightItem = ((Humanoid)localPlayer).GetRightItem(); if (rightItem?.m_shared != null && (Object)(object)rightItem.m_shared.m_buildPieces != (Object)null) { return true; } string text = (((Object)(object)rightItem?.m_dropPrefab != (Object)null) ? ((Object)rightItem.m_dropPrefab).name : rightItem?.m_shared?.m_name); if (!string.IsNullOrEmpty(text)) { return text.IndexOf("Hammer", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static bool WasUsePressed() { try { if (ZInput.GetButtonDown("Use")) { return true; } } catch { } return Input.GetKeyDown((KeyCode)101); } private static string FormatLabel(RaceManager.GateKind kind, int checkpointIndex, bool pointA) { string text = (pointA ? "A" : "B"); return kind switch { RaceManager.GateKind.Start => "START " + text, RaceManager.GateKind.Finish => "FINISH " + text, _ => $"CP{checkpointIndex} {text}", }; } private void EnsureHudStyle() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if (_hudStyle == null) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, clipping = (TextClipping)0 }; val.normal.textColor = new Color(1f, 0.92f, 0.35f); _hudStyle = val; } } } [DefaultExecutionOrder(10000)] public sealed class RaceGui : MonoBehaviour { private static RaceGui _instance; private bool _visible; private Vector2 _scroll; private Vector2 _logScroll; private Rect _windowRect = new Rect(40f, 40f, 480f, 680f); private GUIStyle _headerStyle; private GUIStyle _labelStyle; private GUIStyle _countdownStyle; private GUIStyle _checkpointStyle; private GUIStyle _hudTitleStyle; private GUIStyle _hudLineStyle; private GUIStyle _hudSubLineStyle; private GUIStyle _infoBodyStyle; private GUIStyle _finishBannerStyle; private GUIStyle _finishPlaceStyle; private GUIStyle _finishSubStyle; private GUIStyle _standingsTitleStyle; private GUIStyle _standingsLineStyle; private GUIStyle _standingsLocalLineStyle; private GUIStyle _standingsDisqualifiedLineStyle; private GUIStyle _f6DisqualifiedLabelStyle; private GUIStyle _parAheadStyle; private GUIStyle _parBehindStyle; private static Texture2D _hudBackground; private float _finishBannerUntil; private string _finishBannerPlace = string.Empty; private string _finishBannerText = string.Empty; private bool _localWasFinished; private float _nextClientSyncTime; private string _trackNameDraft = string.Empty; private bool _allowedGearDraftReady; private string _gearHelmetDraft = string.Empty; private string _gearChestDraft = string.Empty; private string _gearLegsDraft = string.Empty; private string _gearCapeDraft = string.Empty; private string _gearHandsDraft = string.Empty; private string _gearAntiStingDraft = "1"; private string _gearRatatoskDraft = "2"; private string _gearSaladDraft = "2"; private string _gearBloodPuddingDraft = "2"; private string _gearOmeletteDraft = "2"; private float _infoPanelUntil; private string _infoPanelTitle = string.Empty; private string _infoPanelBody = string.Empty; public static bool IsOpen { get { if ((Object)(object)_instance != (Object)null) { return _instance._visible; } return false; } } public static void ShowInfoPanel(string title, string body, float seconds = 8f) { if ((Object)(object)_instance == (Object)null) { RaceGuiLog.Add(body); return; } _instance._infoPanelTitle = title ?? "RunningMan"; _instance._infoPanelBody = body ?? string.Empty; _instance._infoPanelUntil = Time.time + Mathf.Max(2f, seconds); } public static void ShowYellowHud(string message) { if (!string.IsNullOrEmpty(message)) { if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null); } else { RaceGuiLog.Add(message); } } } private void SetVisible(bool visible) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) _visible = visible; if (!_visible) { GUI.FocusControl((string)null); return; } _windowRect = new Rect(((float)Screen.width - 480f) * 0.5f, ((float)Screen.height - 680f) * 0.5f, 480f, 680f); Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; _allowedGearDraftReady = false; RaceNetSync.RequestStateRefresh(); } private void Awake() { _instance = this; RaceGuiLog.Updated += OnLogUpdated; } private void OnDestroy() { RaceGuiLog.Updated -= OnLogUpdated; } private void OnLogUpdated() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) _logScroll = Vector2.zero; } private void Update() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (Input.GetKeyDown(ModConfig.GuiHotkey.Value)) { SetVisible(!_visible); } HandleTrackHotkeys(); if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && Time.time >= _nextClientSyncTime) { _nextClientSyncTime = Time.time + 1.5f; RaceNetSync.RequestStateRefresh(); } } } private void LateUpdate() { if (_visible) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; Input.ResetInputAxes(); } } private void OnGUI() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (_visible) { HandleGuiCloseInput(); ConsumeScrollWheel(); } DrawLiveHud(); if (_visible) { EnsureStyles(); _windowRect = GUI.Window(987654, _windowRect, new WindowFunction(DrawWindow), "RunningMan (F6 — Esc to close)"); } if (Time.time < _infoPanelUntil) { EnsureStyles(); DrawInfoPanel(); } } private static void HandleGuiCloseInput() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 Event current = Event.current; if ((int)current.type == 4 && (int)current.keyCode == 27) { GUI.FocusControl((string)null); if ((Object)(object)_instance != (Object)null) { _instance.SetVisible(visible: false); } current.Use(); } } private static void ConsumeScrollWheel() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if ((int)Event.current.type == 6) { Event.current.Use(); } } private void DrawLiveHud() { if (!ModConfig.EnableHud.Value) { return; } RaceStateSnapshot clientState = RaceNetSync.ClientState; if (clientState == null) { return; } EnsureStyles(); bool visible = _visible; RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); bool num = HudPanelLayout.ShouldDrawPanel(HudPanelId.RaceBanner); bool flag = Time.time < _finishBannerUntil; if ((!flag || clientState.Phase == 3 || clientState.Phase == 2) && clientState.Phase != 3 && clientState.Phase != 2) { flag = false; _finishBannerUntil = 0f; } bool flag2 = RaceNetSync.IsCountdownActive() || (clientState.Phase == 2 && clientState.CountdownEndUtcTicks > 0); if (num) { if (flag) { DrawRaceBanner(finishMode: true); } else if (flag2) { int remainingSeconds = (RaceNetSync.IsCountdownActive() ? RaceNetSync.GetCountdownRemainingSeconds() : Math.Max(0, (int)Math.Ceiling((new DateTime(clientState.CountdownEndUtcTicks, DateTimeKind.Utc) - DateTime.UtcNow).TotalSeconds))); DrawRaceBanner(finishMode: false, remainingSeconds); } else if (visible) { DrawRaceBanner(finishMode: false, 3); } } if ((visible || (ModConfig.EnableLiveStandings.Value && clientState.Phase == 3)) && HudPanelLayout.ShouldDrawPanel(HudPanelId.Standings)) { DrawLiveStandings(clientState, visible); } if ((visible || IsActiveRaceRunnerHud(clientState, localRunner)) && HudPanelLayout.ShouldDrawPanel(HudPanelId.Runner)) { if (localRunner != null && localRunner.StartUtcTicks > 0 && clientState.Phase == 3) { if (localRunner.Finished && !_localWasFinished) { _finishBannerPlace = TimeFormatter.FormatOrdinal(localRunner.Place); _finishBannerText = TimeFormatter.FormatDurationMs(localRunner.FinishTimeMs); _finishBannerUntil = Time.time + 8f; } _localWasFinished = localRunner.Finished; } else if (clientState.Phase != 3 && Time.time >= _finishBannerUntil) { _localWasFinished = false; } DrawRunnerOverlay(localRunner, visible); } else { if (clientState.Phase != 3 && Time.time >= _finishBannerUntil) { _finishBannerUntil = 0f; } _localWasFinished = false; } if ((visible || (RaceNetSync.IsLocalRegistered() && (clientState.Phase == 1 || clientState.Phase == 4))) && HudPanelLayout.ShouldDrawPanel(HudPanelId.Registered)) { DrawRegisteredOverlay(clientState, visible); } } private static bool IsActiveRaceRunnerHud(RaceStateSnapshot state, RunnerSnapshot local) { if (state == null || local == null || local.StartUtcTicks <= 0) { return false; } if (state.Phase != 3) { return state.Phase == 2; } return true; } private void DrawRaceBanner(bool finishMode, int remainingSeconds = 0) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) Rect defaultRect = default(Rect); if (finishMode) { int num = ModConfig.CountdownFontSize.Value + 12; float num2 = (float)num * 2.5f; float num3 = (float)num * 1.6f; float num4 = 36f; float num5 = num2 + 48f; float num6 = num3 + num4 + 24f; ((Rect)(ref defaultRect))..ctor((float)Screen.width * 0.5f - num5 * 0.5f, (float)Screen.height * 0.32f - num6 * 0.5f, num5, num6); } else { int value = ModConfig.CountdownFontSize.Value; float num7 = (float)value * 3f; float num8 = (float)value * 1.8f; ((Rect)(ref defaultRect))..ctor((float)Screen.width * 0.5f - num7 * 0.5f, (float)Screen.height * 0.28f - num8 * 0.5f, num7, num8); } if (HudPanelLayout.Resolve(HudPanelId.RaceBanner, defaultRect, _visible, "Race banner", out var panelRect)) { Rect val = HudPanelLayout.ContentRect(panelRect, _visible); int depth = GUI.depth; GUI.depth = -1000; if (finishMode) { int num9 = ModConfig.CountdownFontSize.Value + 12; float num10 = (float)num9 * 2.5f; float num11 = (float)num9 * 1.6f; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + (((Rect)(ref val)).width - num10) * 0.5f, ((Rect)(ref val)).y + 4f, num10, num11); Rect val3 = new Rect(((Rect)(ref val)).x + (((Rect)(ref val)).width - 420f) * 0.5f, ((Rect)(ref val2)).yMax + 4f, 420f, 36f); GUI.Label(val2, string.IsNullOrEmpty(_finishBannerPlace) ? "1st" : _finishBannerPlace, _finishPlaceStyle); GUI.Label(val3, "Finished in " + (string.IsNullOrEmpty(_finishBannerText) ? "12:34" : _finishBannerText), _finishSubStyle); } else { string text = ((remainingSeconds > 0) ? remainingSeconds.ToString() : "GO!"); GUI.Label(val, text, _countdownStyle); } GUI.depth = depth; } } private void DrawLiveStandings(RaceStateSnapshot state, bool layoutMode) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) if (state.Runners == null || state.Runners.Count == 0) { return; } List list = (from r in state.Runners?.Where((RunnerSnapshot r) => r.StartUtcTicks > 0) orderby r.Place select r).Take(ModConfig.LiveStandingsLimit.Value).ToList(); if ((list == null || list.Count == 0) && !layoutMode) { return; } if (list == null || list.Count == 0) { list = new List { new RunnerSnapshot { Place = 1, PlayerName = "Preview", NextCheckpointIndex = 1, FinishTimeMs = 0L } }; } long num = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID() : 0); float num2 = 22f; float num3 = 280f; float num4 = 36f + (float)list.Count * num2; Rect defaultRect = default(Rect); ((Rect)(ref defaultRect))..ctor(24f, (float)Screen.height * 0.22f, num3, num4); if (!HudPanelLayout.Resolve(HudPanelId.Standings, defaultRect, _visible, "Live standings", out var panelRect)) { return; } Rect rect = HudPanelLayout.ContentRect(panelRect, _visible); int depth = GUI.depth; GUI.depth = -997; DrawHudPanel(rect, new Color(0f, 0f, 0f, 0.62f)); float num5 = ((Rect)(ref rect)).y + 10f; GUI.Label(new Rect(((Rect)(ref rect)).x + 12f, num5, num3 - 24f, 24f), "Live standings", _standingsTitleStyle); num5 += 26f; foreach (RunnerSnapshot item in list) { GUIStyle val = ((!item.Disqualified) ? ((item.PlayerId == num) ? _standingsLocalLineStyle : _standingsLineStyle) : _standingsDisqualifiedLineStyle); string text = ((!item.Disqualified) ? ((!item.Finished) ? string.Empty : " FIN") : " DQ"); GUI.Label(new Rect(((Rect)(ref rect)).x + 12f, num5, num3 - 24f, num2), TimeFormatter.FormatOrdinal(item.Place) + " " + item.PlayerName + text, val); num5 += num2; } GUI.depth = depth; } private void DrawRunnerOverlay(RunnerSnapshot runner, bool layoutMode) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) bool flag = runner?.Disqualified ?? false; long num = ((runner == null || runner.StartUtcTicks <= 0) ? 0 : ((runner.Finished || flag) ? runner.FinishTimeMs : ((long)(DateTime.UtcNow - new DateTime(runner.StartUtcTicks, DateTimeKind.Utc)).TotalMilliseconds))); float num2 = 400f; int totalCheckpoints = RaceNetSync.GetTotalCheckpoints(); string text = ((runner != null && runner.StartUtcTicks > 0) ? FormatCheckpointLabel(runner, totalCheckpoints) : "Checkpoints: 0 / 0 completed (preview)"); int num3 = ((runner == null || runner.Place <= 0) ? 1 : runner.Place); string label = string.Empty; long deltaMs = 0L; bool flag2 = TryFormatPar(runner, num, out label, out deltaMs); float num4 = 96f; if (!string.IsNullOrEmpty(text)) { num4 += 22f; } if (flag2) { num4 += 22f; } Rect defaultRect = default(Rect); ((Rect)(ref defaultRect))..ctor((float)Screen.width - num2 - 28f, (float)Screen.height * 0.56f, num2, num4); if (HudPanelLayout.Resolve(HudPanelId.Runner, defaultRect, _visible, "Race stats", out var panelRect)) { Rect rect = HudPanelLayout.ContentRect(panelRect, _visible); int depth = GUI.depth; GUI.depth = -998; DrawHudPanel(rect, new Color(0f, 0f, 0f, 0.62f)); float num5 = ((Rect)(ref rect)).y + 12f; GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, num5, num2 - 32f, 28f), "RunningMan", _hudTitleStyle); num5 += 30f; string text2 = (flag ? ("DQ | Time " + TimeFormatter.FormatDurationMs(num)) : $"Place {num3} | Time {TimeFormatter.FormatDurationMs(num)}"); GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, num5, num2 - 32f, 24f), text2, flag ? _standingsDisqualifiedLineStyle : _hudLineStyle); num5 += 26f; if (!string.IsNullOrEmpty(text)) { GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, num5, num2 - 32f, 22f), text, _hudSubLineStyle); num5 += 24f; } if (flag2) { EnsureStyles(); GUIStyle val = ((deltaMs < 0) ? _parAheadStyle : ((deltaMs > 0) ? _parBehindStyle : _hudSubLineStyle)); GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, num5, num2 - 32f, 22f), label, val); num5 += 24f; } string text3 = (flag ? TruncateHud(string.IsNullOrWhiteSpace(runner?.DisqualifiedReason) ? "DISQUALIFIED" : ("DQ: " + runner.DisqualifiedReason), 48) : ((runner != null && runner.Finished) ? "Race complete!" : ((layoutMode && (runner == null || runner.StartUtcTicks <= 0)) ? "Preview panel" : "Keep running!"))); GUI.Label(new Rect(((Rect)(ref rect)).x + 16f, num5, num2 - 32f, 22f), text3, flag ? _standingsDisqualifiedLineStyle : _hudSubLineStyle); GUI.depth = depth; } } private static string TruncateHud(string text, int maxChars) { if (string.IsNullOrEmpty(text) || text.Length <= maxChars) { return text ?? string.Empty; } return text.Substring(0, Math.Max(0, maxChars - 1)) + "…"; } private static bool TryFormatPar(RunnerSnapshot runner, long elapsedMs, out string label, out long deltaMs) { label = string.Empty; deltaMs = 0L; RaceStateSnapshot clientState = RaceNetSync.ClientState; if (runner == null || clientState == null || clientState.ParTotalTimeMs <= 0) { return false; } if (runner.Disqualified || runner.StartUtcTicks <= 0) { return false; } long num; long num2; string text; if (runner.Finished) { num = clientState.ParTotalTimeMs; num2 = ((runner.FinishTimeMs > 0) ? runner.FinishTimeMs : elapsedMs); text = "finish"; } else { int num3 = Math.Max(0, runner.NextCheckpointIndex - 1); if (num3 <= 0 || clientState.ParCheckpointTimesMs == null || num3 > clientState.ParCheckpointTimesMs.Count) { return false; } num = clientState.ParCheckpointTimesMs[num3 - 1]; num2 = ((runner.LastCheckpointTimeMs > 0) ? runner.LastCheckpointTimeMs : elapsedMs); text = $"CP{num3}"; } if (num <= 0 || num2 <= 0) { return false; } deltaMs = num2 - num; label = "WR " + text + " " + TimeFormatter.FormatSignedDeltaMs(deltaMs); return true; } private static void DrawHudPanel(Rect rect, Color color) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) EnsureHudBackground(); Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(rect, (Texture)(object)_hudBackground); GUI.color = color2; } private static void EnsureHudBackground() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_hudBackground != (Object)null)) { _hudBackground = new Texture2D(1, 1); _hudBackground.SetPixel(0, 0, Color.white); _hudBackground.Apply(); } } private static string FormatCheckpointLabel(RunnerSnapshot runner, int totalCheckpoints) { if (runner.Finished) { return string.Empty; } if (totalCheckpoints <= 0) { return "Head to the FINISH line!"; } int num = Math.Max(0, runner.NextCheckpointIndex - 1); if (num >= totalCheckpoints) { return "All checkpoints done — head to FINISH!"; } return $"Checkpoints: {num} / {totalCheckpoints} completed"; } private void DrawRegisteredOverlay(RaceStateSnapshot state, bool layoutMode) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) List list = state?.Registered; List list2 = new List(); if (list != null) { foreach (RegisteredParticipant item in list) { if (!string.IsNullOrWhiteSpace(item.PlayerName)) { list2.Add(item.PlayerName); } } } if (list2.Count == 0 && layoutMode) { list2.Add("Preview Runner"); } float num = 20f; float num2 = 340f; float num3 = 44f + (float)Math.Max(1, list2.Count) * num; Rect defaultRect = default(Rect); ((Rect)(ref defaultRect))..ctor((float)Screen.width - num2 - 28f, (float)Screen.height * 0.4f, num2, num3); if (!HudPanelLayout.Resolve(HudPanelId.Registered, defaultRect, _visible, "Registration", out var panelRect)) { return; } Rect rect = HudPanelLayout.ContentRect(panelRect, _visible); DrawHudPanel(rect, new Color(0f, 0f, 0f, 0.62f)); float num4 = ((Rect)(ref rect)).y + 10f; GUI.Label(new Rect(((Rect)(ref rect)).x + 12f, num4, num2 - 24f, 22f), $"Registered ({list2.Count}) — waiting to start", _standingsTitleStyle); num4 += 24f; if (list2.Count == 0) { GUI.Label(new Rect(((Rect)(ref rect)).x + 12f, num4, num2 - 24f, num), "Nobody registered yet.", _standingsLineStyle); return; } foreach (string item2 in list2) { GUI.Label(new Rect(((Rect)(ref rect)).x + 12f, num4, num2 - 24f, num), "• " + item2, _standingsLineStyle); num4 += num; } } private void DrawInfoPanel() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) EnsureStyles(); string obj = _infoPanelBody ?? string.Empty; float num = Mathf.Clamp((float)Screen.width * 0.55f, 520f, 900f); GUIStyle val = _infoBodyStyle ?? _hudSubLineStyle; GUIStyle standingsTitleStyle = _standingsTitleStyle; GUIContent val2 = new GUIContent(obj); GUIContent val3 = new GUIContent(_infoPanelTitle ?? "RunningMan"); float num2 = standingsTitleStyle.CalcHeight(val3, num - 40f); float num3 = val.CalcHeight(val2, num - 40f); float num4 = Mathf.Clamp(28f + num2 + num3 + 28f, 100f, (float)Screen.height * 0.7f); Rect rect = default(Rect); ((Rect)(ref rect))..ctor((float)Screen.width * 0.5f - num * 0.5f, (float)Screen.height * 0.42f - num4 * 0.5f, num, num4); int depth = GUI.depth; GUI.depth = -2000; DrawHudPanel(rect, new Color(0.08f, 0.05f, 0.02f, 0.94f)); GUI.Label(new Rect(((Rect)(ref rect)).x + 20f, ((Rect)(ref rect)).y + 14f, num - 40f, num2), val3, standingsTitleStyle); GUI.Label(new Rect(((Rect)(ref rect)).x + 20f, ((Rect)(ref rect)).y + 18f + num2, num - 40f, num3), val2, val); GUI.depth = depth; } private void DrawWindow(int id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 if ((int)Event.current.type == 4 && (int)Event.current.keyCode == 27) { GUI.FocusControl((string)null); SetVisible(visible: false); Event.current.Use(); return; } _scroll = GUILayout.BeginScrollView(_scroll, Array.Empty()); RaceStateSnapshot clientState = RaceNetSync.ClientState; RaceEventPhase raceEventPhase = (RaceEventPhase)(clientState?.Phase ?? 0); if (ValheimUtil.IsLocalPlayerAdmin()) { GUILayout.Label("Event phase: " + FormatPhase(raceEventPhase), _headerStyle, Array.Empty()); if (raceEventPhase == RaceEventPhase.Countdown && clientState != null) { int num = Math.Max(0, (int)Math.Ceiling((new DateTime(clientState.CountdownEndUtcTicks, DateTimeKind.Utc) - DateTime.UtcNow).TotalSeconds)); GUILayout.Label($"Countdown: {num}", _countdownStyle, Array.Empty()); } GUILayout.Space(8f); DrawPlayerSection(clientState, raceEventPhase, includeRefreshStatus: true); GUILayout.Space(8f); DrawStandings(clientState); GUILayout.Space(8f); DrawAdminSection(raceEventPhase); GUILayout.Space(8f); DrawTrackSection(); GUILayout.Space(8f); DrawHudPanelsSection(); GUILayout.Space(8f); DrawLogSection(); GUILayout.Space(8f); DrawHelpSection(); } else { DrawPlayerSection(clientState, raceEventPhase, includeRefreshStatus: false); GUILayout.Space(8f); if (GUILayout.Button("Admin check", Array.Empty())) { SendCommand("admincheck"); } GUILayout.Space(8f); DrawHudPanelsSection(); } GUILayout.EndScrollView(); GUI.DragWindow(); } private static string FormatPhase(RaceEventPhase phase) { return phase switch { RaceEventPhase.Ready => "Ready (waiting to start)", RaceEventPhase.Registration => "Registration", RaceEventPhase.Countdown => "Countdown", RaceEventPhase.Racing => "Racing", _ => "Idle", }; } private void DrawPlayerSection(RaceStateSnapshot state, RaceEventPhase phase, bool includeRefreshStatus) { GUILayout.Label("Player", _headerStyle, Array.Empty()); bool num = RaceNetSync.IsLocalRegistered(); GUILayout.Label((state != null && state.RegistrationOpen) ? "Registration is open." : "Registration is closed.", Array.Empty()); GUILayout.Label(num ? "You are registered." : "You are not registered.", Array.Empty()); if (GUILayout.Button("Check my gear", Array.Empty())) { SendCommand("gearcheck"); } GUI.enabled = RaceNetSync.CanJoinRace(); if (GUILayout.Button("Join race", Array.Empty())) { SendCommand("join"); } GUI.enabled = num && phase != RaceEventPhase.Racing && phase != RaceEventPhase.Countdown; if (GUILayout.Button("Leave race", Array.Empty())) { SendCommand("leave"); } GUI.enabled = true; if (includeRefreshStatus && GUILayout.Button("Refresh status", Array.Empty())) { SendCommand("status"); } } private void DrawStandings(RaceStateSnapshot state) { GUILayout.Space(4f); GUILayout.Label("Standings", _headerStyle, Array.Empty()); if (state?.Runners == null || state.Runners.Count == 0) { GUILayout.Label("No active runners.", Array.Empty()); return; } foreach (RunnerSnapshot runner in state.Runners) { string text = TimeFormatter.FormatDurationMs(runner.FinishTimeMs); string text2 = ((!runner.Disqualified) ? (runner.Finished ? "finished" : $"CP {runner.NextCheckpointIndex}") : "disqualified"); string text3 = $"{runner.Place}. {runner.PlayerName} — {text} ({text2})"; if (runner.Disqualified) { GUILayout.Label(text3, _f6DisqualifiedLabelStyle, Array.Empty()); } else { GUILayout.Label(text3, _labelStyle, Array.Empty()); } } if (state.Registered == null || state.Registered.Count <= 0) { return; } GUILayout.Space(6f); GUILayout.Label("Registered:", _headerStyle, Array.Empty()); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < state.Registered.Count; i++) { if (i > 0) { stringBuilder.Append(", "); } stringBuilder.Append(state.Registered[i].PlayerName); } GUILayout.Label(stringBuilder.ToString(), Array.Empty()); } private void DrawAdminSection(RaceEventPhase phase) { GUILayout.Label("Event admin", _headerStyle, Array.Empty()); if (GUILayout.Button("Open registration", Array.Empty())) { SendCommand("open"); } if (GUILayout.Button("Close registration", Array.Empty())) { SendCommand("close"); } bool flag = phase == RaceEventPhase.Racing || phase == RaceEventPhase.Countdown; if (GUILayout.Button(flag ? "Cancel event" : "Start countdown", Array.Empty())) { SendCommand(flag ? "cancel" : "start"); } if (GUILayout.Button("Show world records", Array.Empty())) { SendCommand("worldrecords"); } if (ValheimUtil.IsLocalPlayerAdmin()) { if (GUILayout.Button("Clear track world records", Array.Empty())) { SendCommand("clear records"); } if (GUILayout.Button("Clear ALL world records", Array.Empty())) { SendCommand("clear records all"); } } if (GUILayout.Button("Admin check", Array.Empty())) { SendCommand("admincheck"); } DrawAllowedGearSection(); } private void DrawAllowedGearSection() { GUILayout.Space(8f); GUILayout.Label("Allowed gear", _headerStyle, Array.Empty()); EnsureAllowedGearDraft(); GUILayout.Label("Helmet", _labelStyle, Array.Empty()); _gearHelmetDraft = GUILayout.TextField(_gearHelmetDraft ?? string.Empty, Array.Empty()); GUILayout.Label("Chest", _labelStyle, Array.Empty()); _gearChestDraft = GUILayout.TextField(_gearChestDraft ?? string.Empty, Array.Empty()); GUILayout.Label("Legs", _labelStyle, Array.Empty()); _gearLegsDraft = GUILayout.TextField(_gearLegsDraft ?? string.Empty, Array.Empty()); GUILayout.Label("Cape (empty = no cape)", _labelStyle, Array.Empty()); _gearCapeDraft = GUILayout.TextField(_gearCapeDraft ?? string.Empty, Array.Empty()); GUILayout.Label("Allowed hands (ignored — any weapon/tool OK)", _labelStyle, Array.Empty()); _gearHandsDraft = GUILayout.TextField(_gearHandsDraft ?? string.Empty, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Anti-Sting", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); _gearAntiStingDraft = GUILayout.TextField(_gearAntiStingDraft ?? "0", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.Label("Ratatosk", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); _gearRatatoskDraft = GUILayout.TextField(_gearRatatoskDraft ?? "0", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Salad", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); _gearSaladDraft = GUILayout.TextField(_gearSaladDraft ?? "0", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.Label("BloodPud", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); _gearBloodPuddingDraft = GUILayout.TextField(_gearBloodPuddingDraft ?? "0", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.Label("Omelette", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); _gearOmeletteDraft = GUILayout.TextField(_gearOmeletteDraft ?? "0", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.EndHorizontal(); if (GUILayout.Button("Use my loadout", Array.Empty())) { ApplyLocalLoadoutToDraft(); } if (GUILayout.Button("Save allowed gear", Array.Empty())) { SendCommand(BuildGearSetSaveCommand()); } if (GUILayout.Button("Reset allowed gear to defaults", Array.Empty())) { SendCommand("gearset reset"); _allowedGearDraftReady = false; } } private void EnsureAllowedGearDraft() { if (!_allowedGearDraftReady) { AllowedGearRules activeRules = GearValidator.GetActiveRules(); ApplyRulesToDraft(activeRules); _allowedGearDraftReady = true; } } private void ApplyRulesToDraft(AllowedGearRules rules) { if (rules == null) { rules = AllowedGearRules.CreateDefaults(); } _gearHelmetDraft = rules.Helmet ?? string.Empty; _gearChestDraft = rules.Chest ?? string.Empty; _gearLegsDraft = rules.Legs ?? string.Empty; _gearCapeDraft = rules.Cape ?? string.Empty; _gearHandsDraft = rules.AllowedHandItems ?? string.Empty; _gearAntiStingDraft = rules.RequiredAntiSting.ToString(); _gearRatatoskDraft = rules.RequiredRatatosk.ToString(); _gearSaladDraft = rules.RequiredSalad.ToString(); _gearBloodPuddingDraft = rules.RequiredBloodPudding.ToString(); _gearOmeletteDraft = rules.RequiredMushroomOmelette.ToString(); } private void ApplyLocalLoadoutToDraft() { AllowedGearRules rules = GearValidator.CaptureFromPlayer(Player.m_localPlayer); ApplyRulesToDraft(rules); RaceGuiLog.Add("Allowed gear draft filled from your current loadout."); } private string BuildGearSetSaveCommand() { AllowedGearRules allowedGearRules = GearValidator.GetActiveRules().Clone(); allowedGearRules.Helmet = SanitizeGearField(_gearHelmetDraft); allowedGearRules.Chest = SanitizeGearField(_gearChestDraft); allowedGearRules.Legs = SanitizeGearField(_gearLegsDraft); allowedGearRules.Cape = SanitizeGearField(_gearCapeDraft); allowedGearRules.AllowedHandItems = SanitizeGearField(_gearHandsDraft); if (!int.TryParse(_gearAntiStingDraft, out var result)) { result = allowedGearRules.RequiredAntiSting; } if (!int.TryParse(_gearRatatoskDraft, out var result2)) { result2 = allowedGearRules.RequiredRatatosk; } if (!int.TryParse(_gearSaladDraft, out var result3)) { result3 = allowedGearRules.RequiredSalad; } if (!int.TryParse(_gearBloodPuddingDraft, out var result4)) { result4 = allowedGearRules.RequiredBloodPudding; } if (!int.TryParse(_gearOmeletteDraft, out var result5)) { result5 = allowedGearRules.RequiredMushroomOmelette; } allowedGearRules.RequiredAntiSting = Math.Max(0, result); allowedGearRules.RequiredRatatosk = Math.Max(0, result2); allowedGearRules.RequiredSalad = Math.Max(0, result3); allowedGearRules.RequiredBloodPudding = Math.Max(0, result4); allowedGearRules.RequiredMushroomOmelette = Math.Max(0, result5); return "gearset save " + EncodeGearSetPayload(allowedGearRules); } private static string SanitizeGearField(string value) { return (value ?? string.Empty).Replace("§", string.Empty).Trim(); } internal static string EncodeGearSetPayload(AllowedGearRules rules) { if (rules == null) { rules = AllowedGearRules.CreateDefaults(); } return string.Join("§", SanitizeGearField(rules.Helmet), SanitizeGearField(rules.Chest), SanitizeGearField(rules.Legs), SanitizeGearField(rules.Cape), SanitizeGearField(rules.AllowedHandItems), SanitizeGearField(rules.AntiStingPrefab), SanitizeGearField(rules.RatatoskPrefab), string.Empty, Math.Max(0, rules.RequiredAntiSting).ToString(), Math.Max(0, rules.RequiredRatatosk).ToString(), "0", SanitizeGearField(rules.SaladPrefab), SanitizeGearField(rules.BloodPuddingPrefab), SanitizeGearField(rules.MushroomOmelettePrefab), Math.Max(0, rules.RequiredSalad).ToString(), Math.Max(0, rules.RequiredBloodPudding).ToString(), Math.Max(0, rules.RequiredMushroomOmelette).ToString()); } private void DrawTrackSection() { //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Track setup", _headerStyle, Array.Empty()); string text = RaceNetSync.ClientState?.TrackName; if (string.IsNullOrWhiteSpace(text)) { text = TrackIdentity.GetDisplayName(RaceNetSync.GetActiveTrack()); } GUILayout.Label("Track: " + text, _labelStyle, Array.Empty()); if (string.IsNullOrEmpty(_trackNameDraft)) { _trackNameDraft = text ?? string.Empty; } _trackNameDraft = GUILayout.TextField(_trackNameDraft, Array.Empty()); if (GUILayout.Button("Save track name", Array.Empty())) { SendCommand("trackname " + _trackNameDraft); } bool flag = RaceNetSync.IsDebugModeActive(); if (GUILayout.Button(flag ? "Debug markers: ON" : "Debug markers: OFF", Array.Empty())) { SendCommand(flag ? "debug off" : "debug on"); } GUILayout.Space(4f); GUILayout.Label("Move endpoints: equip Hammer, look at A/B dots, Use (E).", _labelStyle, Array.Empty()); if (GUILayout.Button(RaceGateEditor.EditMode ? "Endpoint edit: ON" : "Endpoint edit: OFF", Array.Empty())) { RaceGateEditor.ToggleEditMode(); } GUILayout.Space(4f); GUILayout.Label("Bulletins: place Hammer Signs, look at them, then mark.", _labelStyle, Array.Empty()); if (GUILayout.Button("Mark WR Sign (look at Sign)", Array.Empty())) { SendCommand("wrboard add"); } if (GUILayout.Button("Mark RULES Sign (look at Sign)", Array.Empty())) { SendCommand("rulesboard add"); } if (GUILayout.Button("Remove nearest WR Sign", Array.Empty())) { SendCommand("wrboard remove"); } if (GUILayout.Button("Remove nearest RULES Sign", Array.Empty())) { SendCommand("rulesboard remove"); } GUILayout.Space(4f); GUILayout.Label("Register gates (stand at gate, face forward):", _labelStyle, Array.Empty()); if (GUILayout.Button("Register START here", Array.Empty())) { SendCommand("register start"); } if (GUILayout.Button("Register FINISH here", Array.Empty())) { SendCommand("register finish"); } if (GUILayout.Button("Register CHECKPOINT here", Array.Empty())) { SendCommand("register checkpoint"); } GUILayout.Label($"Hotkey: {ModConfig.RegisterCheckpointHotkey.Value} — inserts by track order (between neighbors)", _labelStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Remove gates:", _labelStyle, Array.Empty()); if (GUILayout.Button("Remove last checkpoint", Array.Empty())) { SendCommand("remove checkpoint last"); } if (GUILayout.Button("Remove nearest checkpoint", Array.Empty())) { SendCommand("remove checkpoint nearest"); } if (GUILayout.Button("Clear all checkpoints", Array.Empty())) { SendCommand("clear checkpoints"); } if (GUILayout.Button("Clear start gate", Array.Empty())) { SendCommand("clear start"); } if (GUILayout.Button("Clear finish gate", Array.Empty())) { SendCommand("clear finish"); } GUILayout.Space(4f); GUILayout.Label("Register with custom width: /run register checkpoint 10", _labelStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Auto-detect nearby gates:", _labelStyle, Array.Empty()); if (GUILayout.Button("Auto-detect START", Array.Empty())) { SendCommand("autodetect start"); } if (GUILayout.Button("Auto-detect FINISH", Array.Empty())) { SendCommand("autodetect finish"); } if (GUILayout.Button("Auto-detect CHECKPOINTS", Array.Empty())) { SendCommand("autodetect checkpoints"); } if (GUILayout.Button("Auto-detect 1 checkpoint here", Array.Empty())) { SendCommand("autodetect checkpoint"); } GUILayout.Label($"Hotkey: {ModConfig.AutoDetectNearestCheckpointHotkey.Value}", _labelStyle, Array.Empty()); } private void DrawLogSection() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Command log", _headerStyle, Array.Empty()); if (GUILayout.Button("Clear log", Array.Empty())) { RaceGuiLog.Clear(); } _logScroll = GUILayout.BeginScrollView(_logScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) }); if (RaceGuiLog.Lines.Count == 0) { GUILayout.Label("Button and /run command replies appear here.", _labelStyle, Array.Empty()); } else { foreach (string line in RaceGuiLog.Lines) { GUILayout.Label(line, _labelStyle, Array.Empty()); } } GUILayout.EndScrollView(); } private void DrawHudPanelsSection() { GUILayout.Label("HUD panels", _headerStyle, Array.Empty()); GUILayout.Label("Toggle overlays or use X on a panel title bar while F6 is open.", _labelStyle, Array.Empty()); foreach (HudPanelId item in HudPanelLayout.ToggleablePanels()) { bool flag = HudPanelLayout.IsPanelVisible(item); bool flag2 = GUILayout.Toggle(flag, HudPanelLayout.GetPanelTitle(item), Array.Empty()); if (flag2 != flag) { HudPanelLayout.SetPanelVisible(item, flag2); } } if (GUILayout.Button("Show all HUD panels", Array.Empty())) { HudPanelLayout.ShowAllPanels(); } if (GUILayout.Button("Reset HUD positions", Array.Empty())) { HudPanelLayout.ResetAll(); } } private void DrawHelpSection() { EnsureStyles(); GUILayout.Label("Player: /run status | join | leave | gearcheck | pb | last", _labelStyle, Array.Empty()); GUILayout.Label("Admin: register, autodetect, debug, worldrecords, clear records (server validates admin)", _labelStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("HUD: open F6, drag title bars to move overlays, X to hide, re-enable above.", _labelStyle, Array.Empty()); } private static void SendCommand(string args) { RaceGuiLog.Add("> run " + args); ApplyOptimisticCommand(args); ValheimUtil.RunCommand(args); } private static void HandleTrackHotkeys() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (IsTextInputBlockingHotkeys()) { return; } KeyCode value = ModConfig.RegisterCheckpointHotkey.Value; if ((int)value != 0 && Input.GetKeyDown(value)) { SendCommand("register checkpoint"); return; } KeyCode value2 = ModConfig.AutoDetectNearestCheckpointHotkey.Value; if ((int)value2 != 0 && Input.GetKeyDown(value2)) { SendCommand("autodetect checkpoint"); } } private static bool IsTextInputBlockingHotkeys() { if (Console.IsVisible()) { return true; } try { if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } } catch { } try { if (TextInput.IsVisible()) { return true; } } catch { } if (IsOpen && GUIUtility.keyboardControl != 0) { return true; } return false; } private static void ApplyOptimisticCommand(string args) { if (string.IsNullOrWhiteSpace(args)) { return; } string[] array = args.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return; } string text = array[0].ToLowerInvariant(); if (text == null) { return; } switch (text.Length) { case 5: switch (text[2]) { case 'o': if (text == "close") { RaceNetSync.ApplyOptimisticRegistrationClosed(); } return; case 'a': _ = text == "start"; return; case 'e': break; case 'b': if (text == "debug") { ApplyOptimisticDebug(array); } return; default: return; } if (!(text == "clear")) { break; } goto IL_0129; case 6: switch (text[0]) { default: return; case 'c': if (text == "cancel") { RaceNetSync.ApplyOptimisticCancel(); if ((Object)(object)_instance != (Object)null) { _instance._finishBannerUntil = 0f; _instance._localWasFinished = false; } } return; case 'r': break; } if (!(text == "remove")) { break; } goto IL_0129; case 4: { if (text == "open") { RaceNetSync.ApplyOptimisticRegistrationOpened(); } break; } IL_0129: RaceNetSync.ApplyOptimisticTrackCommand(array); break; } } private static void ApplyOptimisticDebug(string[] parts) { if (parts.Length >= 2) { switch (parts[1].ToLowerInvariant()) { case "on": case "true": case "1": RaceNetSync.ApplyOptimisticDebug(enabled: true); break; case "off": case "false": case "0": RaceNetSync.ApplyOptimisticDebug(enabled: false); break; } } } private void EnsureStyles() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected O, but got Unknown //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Expected O, but got Unknown //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Expected O, but got Unknown //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Expected O, but got Unknown //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Expected O, but got Unknown //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Expected O, but got Unknown //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Expected O, but got Unknown //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Expected O, but got Unknown //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Expected O, but got Unknown //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_0408: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Expected O, but got Unknown //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_044e: Expected O, but got Unknown //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_0485: Expected O, but got Unknown //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_0491: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04bc: Expected O, but got Unknown if (_headerStyle == null) { GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 15, fontStyle = (FontStyle)1, padding = new RectOffset(4, 4, 4, 4), clipping = (TextClipping)0 }; val.normal.textColor = Color.white; _headerStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontSize = 13, clipping = (TextClipping)0 }; val2.normal.textColor = Color.white; val2.wordWrap = true; _labelStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { fontSize = ModConfig.CountdownFontSize.Value, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, clipping = (TextClipping)0 }; val3.normal.textColor = Color.yellow; _countdownStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.label) { fontSize = ModConfig.CheckpointHudFontSize.Value, clipping = (TextClipping)0 }; val4.normal.textColor = new Color(0.85f, 0.85f, 0.85f); _checkpointStyle = val4; GUIStyle val5 = new GUIStyle(GUI.skin.label) { fontSize = 22, fontStyle = (FontStyle)1, alignment = (TextAnchor)0, clipping = (TextClipping)0, padding = new RectOffset(0, 0, 4, 0) }; val5.normal.textColor = Color.white; _hudTitleStyle = val5; GUIStyle val6 = new GUIStyle(GUI.skin.label) { fontSize = 20, fontStyle = (FontStyle)1, alignment = (TextAnchor)0, clipping = (TextClipping)0 }; val6.normal.textColor = Color.white; _hudLineStyle = val6; GUIStyle val7 = new GUIStyle(GUI.skin.label) { fontSize = 18, alignment = (TextAnchor)0, clipping = (TextClipping)0, padding = new RectOffset(0, 0, 2, 0) }; val7.normal.textColor = new Color(0.92f, 0.92f, 0.92f); _hudSubLineStyle = val7; _infoBodyStyle = new GUIStyle(_hudSubLineStyle) { fontSize = 16, wordWrap = true, clipping = (TextClipping)1, richText = false }; GUIStyle val8 = new GUIStyle(GUI.skin.label) { fontSize = 28, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, clipping = (TextClipping)0 }; val8.normal.textColor = Color.green; _finishBannerStyle = val8; GUIStyle val9 = new GUIStyle(GUI.skin.label) { fontSize = ModConfig.CountdownFontSize.Value + 12, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, clipping = (TextClipping)0 }; val9.normal.textColor = new Color(1f, 0.92f, 0.2f); _finishPlaceStyle = val9; GUIStyle val10 = new GUIStyle(GUI.skin.label) { fontSize = 24, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, clipping = (TextClipping)0 }; val10.normal.textColor = Color.white; _finishSubStyle = val10; GUIStyle val11 = new GUIStyle(GUI.skin.label) { fontSize = 18, fontStyle = (FontStyle)1, alignment = (TextAnchor)0, clipping = (TextClipping)0 }; val11.normal.textColor = Color.white; _standingsTitleStyle = val11; GUIStyle val12 = new GUIStyle(GUI.skin.label) { fontSize = 16, alignment = (TextAnchor)0, clipping = (TextClipping)0 }; val12.normal.textColor = new Color(0.9f, 0.9f, 0.9f); _standingsLineStyle = val12; GUIStyle val13 = new GUIStyle(_standingsLineStyle) { fontStyle = (FontStyle)1 }; val13.normal.textColor = new Color(0.55f, 0.95f, 1f); _standingsLocalLineStyle = val13; GUIStyle val14 = new GUIStyle(_standingsLineStyle) { fontStyle = (FontStyle)1 }; val14.normal.textColor = new Color(1f, 0.35f, 0.35f); _standingsDisqualifiedLineStyle = val14; GUIStyle val15 = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; val15.normal.textColor = new Color(1f, 0.35f, 0.35f); _f6DisqualifiedLabelStyle = val15; GUIStyle val16 = new GUIStyle(_hudSubLineStyle) { fontStyle = (FontStyle)1 }; val16.normal.textColor = new Color(0.45f, 0.95f, 0.5f); _parAheadStyle = val16; GUIStyle val17 = new GUIStyle(_hudSubLineStyle) { fontStyle = (FontStyle)1 }; val17.normal.textColor = new Color(1f, 0.55f, 0.35f); _parBehindStyle = val17; } } } public static class RaceGuiLog { private const int MaxLines = 30; private static readonly List _lines = new List(); public static IReadOnlyList Lines => _lines; public static event Action Updated; public static void Add(string message) { if (!string.IsNullOrWhiteSpace(message)) { string[] array = message.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { _lines.Insert(0, text.Trim()); } while (_lines.Count > 30) { _lines.RemoveAt(_lines.Count - 1); } RaceGuiLog.Updated?.Invoke(); } } public static void Clear() { _lines.Clear(); RaceGuiLog.Updated?.Invoke(); } } public sealed class RaceManager : MonoBehaviour { public enum GateKind { Start, Finish, Checkpoint } private readonly Dictionary _activeSessions = new Dictionary(); private readonly Dictionary _registered = new Dictionary(); private readonly Dictionary _previousPositions = new Dictionary(); private readonly Dictionary _reportedPositions = new Dictionary(); private readonly Dictionary _reportedPositionAt = new Dictionary(); private RaceEventPhase _phase; private DateTime _countdownEndUtc; private bool _registrationOpen; private bool _debugMode; private float _nextTickTime; private float _nextSyncTime; private int _lastCountdownSecond = -1; public static RaceManager Instance { get; private set; } public static void Initialize(RunningManPlugin plugin) { if (!((Object)(object)Instance != (Object)null)) { Instance = ((Component)plugin).gameObject.AddComponent(); } } private void Start() { _debugMode = ModConfig.DebugMode.Value; if (ValheimUtil.IsServerAuthority()) { RaceNetSync.SendTrack(JsonStorage.Track); BroadcastState(); } } private void Update() { if (ValheimUtil.IsServerAuthority()) { DateTime utcNow = DateTime.UtcNow; if (Time.time >= _nextTickTime) { _nextTickTime = Time.time + ModConfig.UpdateInterval.Value; Tick(utcNow); } if (Time.time >= _nextSyncTime) { _nextSyncTime = Time.time + ((_phase == RaceEventPhase.Countdown) ? 0.1f : 0.5f); BroadcastState(); } } } public void Tick(DateTime nowUtc) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) if (_phase == RaceEventPhase.Countdown) { TickCountdown(nowUtc); return; } _lastCountdownSecond = -1; if (_phase != RaceEventPhase.Racing) { return; } TrackConfig track = JsonStorage.Track; if (!track.HasStart) { return; } HashSet hashSet = new HashSet(); foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null)) { long playerID = allPlayer.GetPlayerID(); hashSet.Add(playerID); MarkPeerProcessed(allPlayer, hashSet); try { Vector3 currentPosition = ResolveParticipantPosition(playerID, ((Component)allPlayer).transform.position); ProcessParticipant(playerID, allPlayer.GetPlayerName(), currentPosition, track, nowUtc, allPlayer); } catch (Exception arg) { RunningManPlugin.Log.LogError((object)$"RunningMan ProcessParticipant failed for {allPlayer.GetPlayerName()}: {arg}"); } } } if ((Object)(object)ZNet.instance == (Object)null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && !hashSet.Contains(peer.m_uid) && TryResolvePeerRacePosition(peer, out var position)) { hashSet.Add(peer.m_uid); try { ProcessParticipant(peer.m_uid, peer.m_playerName, position, track, nowUtc, null); } catch (Exception arg2) { RunningManPlugin.Log.LogError((object)$"RunningMan ProcessParticipant failed for peer {peer.m_playerName}: {arg2}"); } } } } public void ReportRunnerPosition(long id, Vector3 position) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (id != 0L) { _reportedPositions[id] = position; _reportedPositionAt[id] = Time.time; } } private Vector3 ResolveParticipantPosition(long id, Vector3 fallback) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (TryGetFreshReportedPosition(id, out var position)) { return position; } return fallback; } private bool TryResolvePeerRacePosition(ZNetPeer peer, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if (peer == null) { return false; } if (TryGetFreshReportedPosition(peer.m_uid, out position)) { return true; } Player val = ValheimUtil.FindPlayerFromPeer(peer); if ((Object)(object)val != (Object)null && TryGetFreshReportedPosition(val.GetPlayerID(), out position)) { return true; } return ValheimUtil.TryGetPeerWorldPosition(peer, out position); } private bool TryGetFreshReportedPosition(long id, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if (!_reportedPositions.TryGetValue(id, out position)) { return false; } if (!_reportedPositionAt.TryGetValue(id, out var value) || Time.time - value > 1.5f) { return false; } return true; } private static void MarkPeerProcessed(Player player, HashSet processed) { if ((Object)(object)player == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && ((Object)(object)ValheimUtil.FindPlayerFromPeer(peer) == (Object)(object)player || peer.m_uid == player.GetPlayerID())) { processed.Add(peer.m_uid); } } } private void TickCountdown(DateTime nowUtc) { TrackConfig track = JsonStorage.Track; if (track.HasStart) { CheckFalseStarts(track); } int num = (int)Math.Ceiling((_countdownEndUtc - nowUtc).TotalSeconds); if (num != _lastCountdownSecond && num >= 0) { _lastCountdownSecond = num; if (num > 0) { ValheimUtil.Announce($"RunningMan: {num}..."); } } if (nowUtc >= _countdownEndUtc) { BeginRacing(nowUtc); } } private void CheckFalseStarts(TrackConfig track) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.DisqualifyOnFalseStart.Value || !track.HasStart) { return; } List list = new List(); foreach (RegisteredParticipant value in _registered.Values) { if (!ValheimUtil.TryGetParticipantPosition(value.PlayerId, out var position)) { continue; } if (!sessionHasPrevious(value.PlayerId)) { StorePreviousPosition(value.PlayerId, position); continue; } Vector3 previousPosition = GetPreviousPosition(value.PlayerId); StorePreviousPosition(value.PlayerId, position); if (TriggerDetector.HasFalseStarted(previousPosition, position, track.StartGate, ModConfig.StartTriggerDistance.Value, ModConfig.GateVerticalDistance.Value)) { list.Add(value); } } foreach (RegisteredParticipant item in list) { _registered.Remove(item.PlayerId); ValheimUtil.Announce("RunningMan: " + item.PlayerName + " false started and is out of this race!"); RaceNetSync.SendRaceCue(item.PlayerId, 3); RunningManPlugin.Log.LogWarning((object)("False start: " + item.PlayerName)); } if (list.Count > 0) { BroadcastState(); } } private void ProcessParticipant(long playerId, string playerName, Vector3 currentPosition, TrackConfig track, DateTime nowUtc, Player player) { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) if (playerId == 0L) { return; } if (!_activeSessions.TryGetValue(playerId, out var value) && !TryGetSession(playerId, playerName, out value)) { value = null; } if (value == null) { if (ModConfig.RequireRegistration.Value && !_registered.ContainsKey(playerId) && !IsRegisteredByName(playerName)) { StorePreviousPosition(playerId, currentPosition); return; } if (!sessionHasPrevious(playerId)) { StorePreviousPosition(playerId, currentPosition); return; } if (TriggerDetector.DidCrossForward(GetPreviousPosition(playerId), currentPosition, track.StartGate, ModConfig.StartTriggerDistance.Value, ModConfig.GateVerticalDistance.Value)) { if ((Object)(object)player != (Object)null) { StartRace(player, nowUtc); } else { StartRaceSession(playerId, playerName, nowUtc, currentPosition); } } StorePreviousPosition(playerId, currentPosition); } else { if (value.Finished) { return; } if (value.Disqualified) { StorePreviousPosition(playerId, currentPosition); return; } if ((Object)(object)player != (Object)null) { RaceSkillUtil.EnforceRunLevel(player, ModConfig.NormalizedRunSkillLevel.Value); } if (!value.HasPreviousPosition) { value.LastPosition = currentPosition; value.HasPreviousPosition = true; if ((Object)(object)player != (Object)null) { ValidateRuntimeGear(player, value); } return; } Vector3 lastPosition = value.LastPosition; value.LastPosition = currentPosition; if (TryCompleteCheckpoint(value, track, lastPosition, currentPosition, nowUtc)) { if ((Object)(object)player != (Object)null && !value.Disqualified) { ValidateRuntimeGear(player, value); } return; } CheckMissedCheckpoints(value, track, lastPosition, currentPosition); TryFinishRace(value, track, lastPosition, currentPosition, nowUtc, playerId); if ((Object)(object)player != (Object)null && !value.Finished && !value.Disqualified) { ValidateRuntimeGear(player, value); } } } private void CheckMissedCheckpoints(RaceSession session, TrackConfig track, Vector3 previous, Vector3 current) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (track.Checkpoints == null || session.Disqualified || session.Finished) { return; } int nextCheckpointIndex = session.NextCheckpointIndex; if (nextCheckpointIndex <= 0) { return; } if (session.LastMissedCheckpointNotified < nextCheckpointIndex) { foreach (Checkpoint checkpoint in track.Checkpoints) { if (checkpoint.Index > nextCheckpointIndex && TriggerDetector.DidTriggerGate(previous, current, checkpoint.Gate, ModConfig.CheckpointDistance.Value, ModConfig.CheckpointVerticalDistance.Value)) { session.LastMissedCheckpointNotified = nextCheckpointIndex; string text = $"{session.PlayerName} missed checkpoint {nextCheckpointIndex} (triggered CP{checkpoint.Index} early)."; ValheimUtil.Announce("RunningMan: " + text); RaceNetSync.SendYellowHudToPlayer(session.PlayerId, $"Missed checkpoint {nextCheckpointIndex}!"); return; } } } if (track.HasFinish && nextCheckpointIndex <= track.CheckpointCount && TriggerDetector.DidTriggerGate(previous, current, track.FinishGate, ModConfig.FinishTriggerDistance.Value, ModConfig.GateVerticalDistance.Value) && session.LastMissedCheckpointNotified < nextCheckpointIndex) { session.LastMissedCheckpointNotified = nextCheckpointIndex; ValheimUtil.Announce($"RunningMan: {session.PlayerName} reached finish early — still missing checkpoint {nextCheckpointIndex}+."); RaceNetSync.SendYellowHudToPlayer(session.PlayerId, $"Finish early — you still need checkpoint {nextCheckpointIndex}!"); } } private bool TryCompleteCheckpoint(RaceSession session, TrackConfig track, Vector3 previous, Vector3 current, DateTime nowUtc) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) int expectedIndex = session.NextCheckpointIndex; Checkpoint checkpoint = track.Checkpoints.Find((Checkpoint item) => item.Index == expectedIndex); if (checkpoint == null) { return false; } if (!TriggerDetector.DidTriggerGate(previous, current, checkpoint.Gate, ModConfig.CheckpointDistance.Value, ModConfig.CheckpointVerticalDistance.Value)) { return false; } session.RecordCheckpoint(nowUtc); long milliseconds = session.CheckpointTimesMs[session.CheckpointTimesMs.Count - 1]; string text = $"{session.PlayerName} reached checkpoint {expectedIndex} in {TimeFormatter.FormatDurationMs(milliseconds)}"; RunningManPlugin.Log.LogInfo((object)text); ValheimUtil.Announce(text); BroadcastState(); RaceNetSync.SendRaceCue(session.PlayerId, 1); return true; } private bool TryFinishRace(RaceSession session, TrackConfig track, Vector3 previous, Vector3 current, DateTime nowUtc, long playerId) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!track.HasFinish || session.NextCheckpointIndex <= track.CheckpointCount) { return false; } if (!TriggerDetector.DidTriggerGate(previous, current, track.FinishGate, ModConfig.FinishTriggerDistance.Value, ModConfig.GateVerticalDistance.Value)) { return false; } CompleteRace(session, track, nowUtc, playerId); return true; } public void OpenRegistration() { _registrationOpen = true; _phase = RaceEventPhase.Registration; ValheimUtil.Announce("RunningMan: registration is open! Use /run join or press F6."); BroadcastState(); } public void CloseRegistration() { _registrationOpen = false; if (_phase == RaceEventPhase.Registration) { _phase = ((_registered.Count > 0) ? RaceEventPhase.Ready : RaceEventPhase.Idle); } ValheimUtil.Announce("RunningMan: registration is closed."); BroadcastState(); } public bool JoinPlayer(long playerId, string playerName) { if (playerId == 0L || string.IsNullOrWhiteSpace(playerName)) { return false; } if (!_registrationOpen && _phase != RaceEventPhase.Registration) { return false; } if (_registered.ContainsKey(playerId)) { return false; } _registered[playerId] = new RegisteredParticipant { PlayerId = playerId, PlayerName = playerName }; ValheimUtil.Broadcast(playerName + " joined the marathon."); if (ModConfig.EnableGearCheck.Value) { Player val = ValheimUtil.FindPlayerById(playerId); if ((Object)(object)val != (Object)null) { GearValidator.GearCheckResult gearCheckResult = GearValidator.CheckStartGear(val); if (!gearCheckResult.IsValid) { ValheimUtil.Broadcast(playerName + " gear warning: " + gearCheckResult.Issues[0]); } } } BroadcastState(); return true; } public GearValidator.GearCheckResult CheckPlayerGear(Player player) { return GearValidator.CheckStartGear(player); } private void PreflightGearCheck() { foreach (RegisteredParticipant item in _registered.Values.ToList()) { Player val = ValheimUtil.FindPlayerById(item.PlayerId); if (!((Object)(object)val == (Object)null)) { GearValidator.GearCheckResult gearCheckResult = GearValidator.CheckStartGear(val); if (!gearCheckResult.IsValid) { ValheimUtil.Broadcast(item.PlayerName + " gear issue: " + gearCheckResult.Issues[0]); } } } } private bool ValidateRuntimeGear(Player player, RaceSession session) { GearValidator.GearCheckResult gearCheckResult = GearValidator.CheckRuntimeGear(player); if (gearCheckResult.IsValid) { return true; } string reason = gearCheckResult.Issues.FirstOrDefault() ?? "illegal gear change"; HandleGearViolation(session, reason); return !session.Disqualified; } public void ReportGearViolation(long playerId, string reason) { if (ValheimUtil.IsServerAuthority() && ModConfig.EnableGearCheck.Value && _phase == RaceEventPhase.Racing && TryResolveActiveSession(playerId, out var session) && !session.Disqualified && !session.Finished) { HandleGearViolation(session, reason ?? "illegal gear change"); } } private bool TryResolveActiveSession(long playerId, out RaceSession session) { if (_activeSessions.TryGetValue(playerId, out session)) { return true; } if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(playerId); if (peer != null) { Player val = ValheimUtil.FindPlayerFromPeer(peer); if ((Object)(object)val != (Object)null && _activeSessions.TryGetValue(val.GetPlayerID(), out session)) { return true; } if (!string.IsNullOrWhiteSpace(peer.m_playerName)) { foreach (RaceSession value in _activeSessions.Values) { if (string.Equals(value.PlayerName, peer.m_playerName, StringComparison.OrdinalIgnoreCase)) { session = value; return true; } } } } } session = null; return false; } private void HandleGearViolation(RaceSession session, string reason) { if (session != null) { if (ModConfig.DisqualifyOnGearViolation.Value) { DisqualifyPlayer(session, reason); return; } ValheimUtil.Broadcast(session.PlayerName + " gear warning: " + reason); RaceNetSync.SendYellowHudToPlayer(session.PlayerId, "Gear warning: " + reason); } } private void DisqualifyPlayer(RaceSession session, string reason) { if (!session.Disqualified) { session.Disqualified = true; session.DisqualifiedReason = reason ?? "rule violation"; if (session.FinishTimeMs <= 0) { session.FinishTimeMs = session.ElapsedMs(DateTime.UtcNow); } RaceSkillUtil.RestoreRunLevel(ValheimUtil.FindPlayerById(session.PlayerId), session); ValheimUtil.Broadcast(session.PlayerName + " disqualified: " + session.DisqualifiedReason); RaceNetSync.SendYellowHudToPlayer(session.PlayerId, "DISQUALIFIED: " + session.DisqualifiedReason); RaceNetSync.SendRaceCue(session.PlayerId, 3); RunningManPlugin.Log.LogWarning((object)("Disqualified " + session.PlayerName + ": " + session.DisqualifiedReason)); BroadcastState(); } } public void TryFlagIllegalConsumable(Player player, ItemData item) { if (!ValheimUtil.IsServerAuthority() || (Object)(object)player == (Object)null || item == null || !ModConfig.EnableGearCheck.Value || _phase != RaceEventPhase.Racing) { return; } long playerID = player.GetPlayerID(); if (_activeSessions.TryGetValue(playerID, out var value) && !value.Disqualified && !value.Finished && !GearValidator.IsAllowedRaceConsumable(item)) { string text = GearValidator.GetPrefabName(item); if (string.IsNullOrEmpty(text)) { text = (GearValidator.IsFoodItem(item) ? "food" : "consumable"); } string reason = (GearValidator.IsFoodItem(item) ? ("illegal food: " + text) : ("illegal consumable: " + text)); DisqualifyPlayer(value, reason); } } public bool LeavePlayer(long playerId) { if (!_registered.Remove(playerId)) { return false; } ResetActiveRun(playerId); if (_phase == RaceEventPhase.Ready && _registered.Count == 0) { _phase = RaceEventPhase.Idle; } BroadcastState(); return true; } public bool StartCountdown(out string failureReason) { failureReason = null; if (_registered.Count == 0) { TryAutoRegisterLocalAdmin(); TryAutoRegisterCommandSender(); } if (_registered.Count == 0) { failureReason = "Could not start countdown. Use /run open, have runners /run join, then /run start."; RunningManPlugin.Log.LogWarning((object)"RunningMan countdown blocked: no registered runners."); return false; } if (ModConfig.RequireStartingArea.Value) { TrackConfig track = JsonStorage.Track; if (!track.HasStart) { failureReason = "No start gate registered — cannot verify starting area."; return false; } List participantsOutsideStartingArea = GetParticipantsOutsideStartingArea(track); if (participantsOutsideStartingArea.Count > 0) { failureReason = "Not all participants inside starting area: " + string.Join(", ", participantsOutsideStartingArea); RaceNetSync.SendYellowHud(failureReason); return false; } } PreflightGearCheck(); _activeSessions.Clear(); _previousPositions.Clear(); _phase = RaceEventPhase.Countdown; _countdownEndUtc = DateTime.UtcNow.AddSeconds(ModConfig.CountdownSeconds.Value); _registrationOpen = false; _lastCountdownSecond = -1; _nextSyncTime = 0f; ValheimUtil.Announce($"RunningMan: race starts in {ModConfig.CountdownSeconds.Value} seconds!"); RaceNetSync.SendCountdownStart(_countdownEndUtc); BroadcastState(); return true; } private List GetParticipantsOutsideStartingArea(TrackConfig track) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (RegisteredParticipant value in _registered.Values) { if (!ValheimUtil.TryGetParticipantPosition(value.PlayerId, out var position)) { list.Add(value.PlayerName + " (no position)"); } else if (!TriggerDetector.IsInStartingArea(position, track.StartGate, ModConfig.StartingAreaOffset.Value, ModConfig.StartingAreaDepth.Value, ModConfig.StartingAreaSidePadding.Value, ModConfig.GateVerticalDistance.Value)) { list.Add(value.PlayerName); } } return list; } private bool TryAutoRegisterLocalAdmin() { if ((Object)(object)Player.m_localPlayer == (Object)null || !ValheimUtil.IsLocalPlayerAdmin()) { return false; } long playerID = Player.m_localPlayer.GetPlayerID(); if (playerID == 0L || _registered.ContainsKey(playerID)) { return _registered.ContainsKey(playerID); } _registered[playerID] = new RegisteredParticipant { PlayerId = playerID, PlayerName = Player.m_localPlayer.GetPlayerName() }; return true; } private bool TryAutoRegisterCommandSender() { if (!CommandContext.ExecutingFromRemote || CommandContext.SenderPeerId == 0L) { return false; } if (_registered.ContainsKey(CommandContext.SenderPeerId)) { return true; } ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(CommandContext.SenderPeerId) : null); if (val == null || string.IsNullOrWhiteSpace(val.m_playerName)) { return false; } _registered[CommandContext.SenderPeerId] = new RegisteredParticipant { PlayerId = CommandContext.SenderPeerId, PlayerName = val.m_playerName }; return true; } private void BeginRacing(DateTime nowUtc) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (_registered.Count == 0) { _phase = RaceEventPhase.Idle; ValheimUtil.Announce("RunningMan: no runners left to start (false starts?)."); BroadcastState(); return; } _phase = RaceEventPhase.Racing; ValheimUtil.Announce("RunningMan: GO!"); foreach (RegisteredParticipant item in _registered.Values.ToList()) { if (_activeSessions.ContainsKey(item.PlayerId) || TryGetSession(item.PlayerId, item.PlayerName, out var _)) { continue; } if (ValheimUtil.TryGetParticipantPosition(item.PlayerId, out var position)) { StartRaceSession(item.PlayerId, item.PlayerName, nowUtc, position); } else { StartRaceSession(item.PlayerId, item.PlayerName, nowUtc, Vector3.zero); } Player obj = ValheimUtil.FindPlayerById(item.PlayerId); if (obj == null) { ZNet instance = ZNet.instance; obj = ValheimUtil.FindPlayerFromPeer((instance != null) ? instance.GetPeer(item.PlayerId) : null); } Player val = obj; if ((Object)(object)val != (Object)null && _activeSessions.TryGetValue(item.PlayerId, out var value)) { GearValidator.GearCheckResult gearCheckResult = GearValidator.CheckStartGear(val); if (!gearCheckResult.IsValid) { ValheimUtil.Broadcast(item.PlayerName + " started with gear issue: " + gearCheckResult.Issues[0]); } RaceSkillUtil.TrySaveAndSetRunLevel(val, value, ModConfig.NormalizedRunSkillLevel.Value); } } BroadcastState(); } public void CancelEvent() { RestoreRunSkillsForAllSessions(); _phase = RaceEventPhase.Idle; _registrationOpen = false; _lastCountdownSecond = -1; _registered.Clear(); _activeSessions.Clear(); _previousPositions.Clear(); _reportedPositions.Clear(); _reportedPositionAt.Clear(); RaceNetSync.ClearLocalCountdown(); RaceNetSync.SendCountdownStart(DateTime.UtcNow); ValheimUtil.Announce("RunningMan: event cancelled."); BroadcastState(); } public void StartRaceSession(long playerId, string playerName, DateTime nowUtc, Vector3 position) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (!_activeSessions.ContainsKey(playerId)) { RaceSession raceSession = new RaceSession(playerId, playerName, nowUtc); if (position != Vector3.zero) { raceSession.LastPosition = position; raceSession.HasPreviousPosition = true; StorePreviousPosition(playerId, position); } _activeSessions[playerId] = raceSession; RaceSkillUtil.TrySaveAndSetRunLevel(ValheimUtil.FindPlayerById(playerId), raceSession, ModConfig.NormalizedRunSkillLevel.Value); ValheimUtil.Announce(raceSession.PlayerName + " has started the marathon!"); BroadcastState(); } } public void StartRace(Player player, DateTime nowUtc) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) long playerID = player.GetPlayerID(); if (_activeSessions.ContainsKey(playerID)) { return; } GearValidator.GearCheckResult gearCheckResult = GearValidator.CheckStartGear(player); if (!gearCheckResult.IsValid) { RunningManPlugin.Log.LogWarning((object)(player.GetPlayerName() + " blocked from start: " + gearCheckResult.Format(multiline: false))); ValheimUtil.Announce(player.GetPlayerName() + " cannot start — " + gearCheckResult.Issues[0]); return; } RaceSession raceSession = new RaceSession(playerID, player.GetPlayerName(), nowUtc) { LastPosition = ((Component)player).transform.position, HasPreviousPosition = true }; _activeSessions[playerID] = raceSession; RaceSkillUtil.TrySaveAndSetRunLevel(player, raceSession, ModConfig.NormalizedRunSkillLevel.Value); if (_phase == RaceEventPhase.Racing) { ValheimUtil.Announce(raceSession.PlayerName + " has started the marathon!"); } } public void CompleteRace(RaceSession session, TrackConfig track, DateTime finishUtc, long playerId) { if (!session.Disqualified) { session.Finished = true; session.FinishTimeMs = session.ElapsedMs(finishUtc); RaceSkillUtil.RestoreRunLevel(ValheimUtil.FindPlayerById(playerId), session); RaceRecord raceRecord = session.ToCompletedRecord(finishUtc, track.CheckpointCount); Leaderboard.RecordCompletedRun(raceRecord); int num = BuildStandings(finishUtc).Find((RunnerSnapshot runner) => runner.PlayerId == playerId || string.Equals(runner.PlayerName, session.PlayerName, StringComparison.OrdinalIgnoreCase))?.Place ?? 0; string text = TimeFormatter.FormatOrdinal(num); ValheimUtil.Announce((num > 0) ? (session.PlayerName + " finished " + text + " in " + raceRecord.TotalTime + "!") : (session.PlayerName + " finished in " + raceRecord.TotalTime + "!")); if (num > 0) { RaceNetSync.SendYellowHudToPlayer(session.PlayerId, "You finished " + text + "!"); } RunningManPlugin.Log.LogInfo((object)("Race complete: " + session.PlayerName + " " + text + " in " + raceRecord.TotalTime)); BroadcastState(); if (num == 1) { RaceNetSync.SendRaceCue(session.PlayerId, 2); } TryEndEventWhenAllFinished(); } } private void TryEndEventWhenAllFinished() { if (_phase != RaceEventPhase.Racing || _activeSessions.Count == 0) { return; } foreach (RaceSession value in _activeSessions.Values) { if (!value.Finished && !value.Disqualified) { return; } } RestoreRunSkillsForAllSessions(); int num = _activeSessions.Values.Count((RaceSession s) => s.Finished && !s.Disqualified); _phase = RaceEventPhase.Idle; _registrationOpen = false; _registered.Clear(); _activeSessions.Clear(); _previousPositions.Clear(); _reportedPositions.Clear(); _reportedPositionAt.Clear(); BroadcastState(); if (num > 1) { ((MonoBehaviour)this).CancelInvoke("AnnounceAllFinishedDelayed"); ((MonoBehaviour)this).Invoke("AnnounceAllFinishedDelayed", 5f); } } private void AnnounceAllFinishedDelayed() { ValheimUtil.Announce("RunningMan: all runners finished!"); } private bool TryGetSession(long playerId, string playerName, out RaceSession session) { if (_activeSessions.TryGetValue(playerId, out session)) { return true; } if (string.IsNullOrWhiteSpace(playerName)) { session = null; return false; } foreach (RaceSession value in _activeSessions.Values) { if (string.Equals(value.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { session = value; return true; } } session = null; return false; } private bool IsRegisteredByName(string playerName) { if (string.IsNullOrWhiteSpace(playerName)) { return false; } foreach (RegisteredParticipant value in _registered.Values) { if (string.Equals(value.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public bool ResetActiveRun(long playerId) { if (!_activeSessions.TryGetValue(playerId, out var value)) { return false; } RaceSkillUtil.RestoreRunLevel(ValheimUtil.FindPlayerById(playerId), value); _activeSessions.Remove(playerId); BroadcastState(); return true; } private void RestoreRunSkillsForAllSessions() { foreach (RaceSession value in _activeSessions.Values) { RaceSkillUtil.RestoreRunLevel(ValheimUtil.FindPlayerById(value.PlayerId), value); } } public RaceSession GetActiveSession(long playerId) { _activeSessions.TryGetValue(playerId, out var value); return value; } public string FormatStatus(long playerId, DateTime nowUtc) { RaceSession activeSession = GetActiveSession(playerId); if (activeSession == null) { string arg = (_registered.ContainsKey(playerId) ? "registered" : "not registered"); return $"Event: {_phase}\nStatus: not running ({arg})"; } string text = TimeFormatter.FormatDurationMs(activeSession.ElapsedMs(nowUtc)); int place = GetPlace(playerId, nowUtc); int nextCheckpointIndex = activeSession.NextCheckpointIndex; int checkpointCount = JsonStorage.Track.CheckpointCount; return $"Event: {_phase}\nPlace: {place}\nElapsed: {text}\nNext checkpoint: {nextCheckpointIndex} / {checkpointCount + 1}"; } public int GetPlace(long playerId, DateTime nowUtc) { List list = BuildStandings(nowUtc); for (int i = 0; i < list.Count; i++) { if (list[i].PlayerId == playerId) { return list[i].Place; } } return 0; } public bool SetGateEndpoint(GateKind kind, int checkpointIndex, bool pointA, Vector3 position, Vector3? preferredForward = null) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) TrackConfig track = JsonStorage.Track; string id = TrackIdentity.GetId(track); RaceGate raceGate = null; switch (kind) { case GateKind.Start: raceGate = track.StartGate; break; case GateKind.Finish: raceGate = track.FinishGate; break; case GateKind.Checkpoint: raceGate = track.Checkpoints?.Find((Checkpoint item) => item.Index == checkpointIndex)?.Gate; break; } if (raceGate == null || !raceGate.IsConfigured()) { return false; } raceGate.SetEndpoint(pointA, position, preferredForward); FinishTrackEdit(id); return true; } public void RegisterStartGate(RaceGate gate) { string id = TrackIdentity.GetId(JsonStorage.Track); JsonStorage.Track.StartGate = gate; FinishTrackEdit(id); } public void RegisterFinishGate(RaceGate gate) { string id = TrackIdentity.GetId(JsonStorage.Track); JsonStorage.Track.FinishGate = gate; FinishTrackEdit(id); } public int RegisterCheckpoint(RaceGate gate) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) string id = TrackIdentity.GetId(JsonStorage.Track); TrackConfig track = JsonStorage.Track; if (track.Checkpoints == null) { track.Checkpoints = new List(); } track.Checkpoints.Sort((Checkpoint left, Checkpoint right) => left.Index.CompareTo(right.Index)); int num = TrackPath.FindCheckpointInsertIndex(track, gate.GetMidpoint()); num = Mathf.Clamp(num, 1, track.Checkpoints.Count + 1); track.Checkpoints.Insert(num - 1, new Checkpoint(num, gate)); RenumberCheckpoints(track); FinishTrackEdit(id); return num; } public void ReplaceCheckpoints(List checkpoints) { string id = TrackIdentity.GetId(JsonStorage.Track); JsonStorage.Track.Checkpoints = checkpoints; FinishTrackEdit(id); } public bool RemoveCheckpoint(int index) { TrackConfig track = JsonStorage.Track; if (track.Checkpoints == null || track.Checkpoints.Count == 0) { return false; } string id = TrackIdentity.GetId(track); if (track.Checkpoints.RemoveAll((Checkpoint item) => item.Index == index) == 0) { return false; } RenumberCheckpoints(track); FinishTrackEdit(id); return true; } public bool RemoveNearestCheckpoint(Vector3 origin) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) TrackConfig track = JsonStorage.Track; if (track.Checkpoints == null || track.Checkpoints.Count == 0) { return false; } Checkpoint checkpoint = null; float num = float.MaxValue; foreach (Checkpoint checkpoint2 in track.Checkpoints) { if (checkpoint2?.Gate != null && checkpoint2.Gate.IsConfigured()) { float num2 = Vector3.Distance(origin, checkpoint2.Gate.GetMidpoint()); if (!(num2 >= num)) { num = num2; checkpoint = checkpoint2; } } } if (checkpoint != null) { return RemoveCheckpoint(checkpoint.Index); } return false; } public bool RemoveLastCheckpoint() { TrackConfig track = JsonStorage.Track; if (track.Checkpoints == null || track.Checkpoints.Count == 0) { return false; } string id = TrackIdentity.GetId(track); track.Checkpoints.RemoveAt(track.Checkpoints.Count - 1); RenumberCheckpoints(track); FinishTrackEdit(id); return true; } public int ClearCheckpoints() { TrackConfig track = JsonStorage.Track; int num = track.Checkpoints?.Count ?? 0; if (num == 0) { return 0; } string id = TrackIdentity.GetId(track); track.Checkpoints.Clear(); FinishTrackEdit(id); return num; } public bool ClearStartGate() { if (!JsonStorage.Track.HasStart) { return false; } string id = TrackIdentity.GetId(JsonStorage.Track); JsonStorage.Track.StartGate = new RaceGate(); FinishTrackEdit(id); return true; } public bool ClearFinishGate() { if (!JsonStorage.Track.HasFinish) { return false; } string id = TrackIdentity.GetId(JsonStorage.Track); JsonStorage.Track.FinishGate = new RaceGate(); FinishTrackEdit(id); return true; } private void FinishTrackEdit(string previousTrackId) { JsonStorage.SaveTrack(); if (!string.IsNullOrEmpty(previousTrackId)) { string id = TrackIdentity.GetId(JsonStorage.Track); if (!string.Equals(previousTrackId, id, StringComparison.OrdinalIgnoreCase)) { int num = Leaderboard.ClearRecordsForTrackId(previousTrackId); if (num > 0) { RunningManPlugin.Log.LogInfo((object)$"Track layout changed; cleared {num} world record(s) for previous layout."); } } } SyncTrack(); BroadcastState(); } public void SetTrackName(string name) { JsonStorage.Track.Name = name?.Trim() ?? string.Empty; JsonStorage.SaveTrack(); SyncTrack(); BroadcastState(); } public int ClearTrackWorldRecords() { int result = Leaderboard.ClearWorldRecords(JsonStorage.Track); BroadcastState(); return result; } public int ClearAllWorldRecords() { int result = Leaderboard.ClearAllWorldRecords(); BroadcastState(); return result; } private static void RenumberCheckpoints(TrackConfig track) { for (int i = 0; i < track.Checkpoints.Count; i++) { track.Checkpoints[i].Index = i + 1; } } public void SetDebugMode(bool enabled) { _debugMode = enabled; ModConfig.DebugMode.Value = enabled; ModConfig.ConfigFile.Save(); BroadcastState(); } public RaceStateSnapshot BuildClientSnapshot(DateTime nowUtc) { _debugMode = ModConfig.DebugMode.Value; TrackConfig track = JsonStorage.Track; RaceStateSnapshot raceStateSnapshot = new RaceStateSnapshot { Phase = (int)_phase, CountdownEndUtcTicks = _countdownEndUtc.Ticks, DebugMode = _debugMode, RegistrationOpen = _registrationOpen, TotalCheckpoints = track.CheckpointCount, Registered = _registered.Values.ToList(), Runners = BuildStandings(nowUtc), AdminPlayerIds = ValheimUtil.BuildAdminPlayerIds(), TrackName = TrackIdentity.GetDisplayName(track), TrackId = TrackIdentity.GetId(track), WorldRecords = Leaderboard.BuildWorldRecordEntries(track, ModConfig.WorldRecordsLimit.Value) }; IReadOnlyList worldRecords = Leaderboard.GetWorldRecords(track, 1); if (worldRecords.Count > 0) { raceStateSnapshot.ParTotalTimeMs = worldRecords[0].TotalTimeMs; if (worldRecords[0].CheckpointTimesMs != null) { raceStateSnapshot.ParCheckpointTimesMs = new List(worldRecords[0].CheckpointTimesMs); } } raceStateSnapshot.AllowedGear = (JsonStorage.AllowedGear ?? AllowedGearRules.CreateDefaults()).Clone(); return raceStateSnapshot; } private List BuildStandings(DateTime nowUtc) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) TrackConfig track = JsonStorage.Track; List list = new List(); foreach (RaceSession value in _activeSessions.Values) { Vector3 position; Vector3 worldPosition = (value.HasPreviousPosition ? value.LastPosition : (ValheimUtil.TryGetParticipantPosition(value.PlayerId, out position) ? position : Vector3.zero)); float progress = TrackPath.GetProgress(track, worldPosition, value.NextCheckpointIndex, value.Finished); long lastCheckpointTimeMs = ((value.CheckpointTimesMs != null && value.CheckpointTimesMs.Count > 0) ? value.CheckpointTimesMs[value.CheckpointTimesMs.Count - 1] : 0); list.Add(new RunnerSnapshot { PlayerId = value.PlayerId, PlayerName = value.PlayerName, StartUtcTicks = value.StartUtc.Ticks, NextCheckpointIndex = value.NextCheckpointIndex, Finished = value.Finished, FinishTimeMs = ((value.Finished || value.Disqualified) ? value.FinishTimeMs : value.ElapsedMs(nowUtc)), PathProgress = progress, Disqualified = value.Disqualified, DisqualifiedReason = (value.DisqualifiedReason ?? string.Empty), FeatherCapeUnlocked = value.FeatherCapeUnlocked, LastCheckpointTimeMs = lastCheckpointTimeMs }); } List list2 = (from r in list where r.Finished orderby r.FinishTimeMs select r).ToList(); List list3 = (from r in list where !r.Finished && !r.Disqualified orderby r.PathProgress descending, r.FinishTimeMs select r).ToList(); List list4 = (from r in list where r.Disqualified && !r.Finished orderby r.PathProgress descending, r.FinishTimeMs select r).ToList(); List list5 = new List(); int num = 1; foreach (RunnerSnapshot item in list2) { item.Place = num++; list5.Add(item); } foreach (RunnerSnapshot item2 in list3) { item2.Place = num++; list5.Add(item2); } foreach (RunnerSnapshot item3 in list4) { item3.Place = num++; list5.Add(item3); } return list5; } public void BroadcastState() { if (ValheimUtil.IsServerAuthority()) { RaceNetSync.SendRaceState(BuildClientSnapshot(DateTime.UtcNow)); } } public void SyncTrack() { if (ValheimUtil.IsServerAuthority()) { RaceNetSync.SendTrack(JsonStorage.Track); BroadcastState(); } } private bool sessionHasPrevious(long playerId) { return _previousPositions.ContainsKey(playerId); } private Vector3 GetPreviousPosition(long playerId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return _previousPositions[playerId]; } private void StorePreviousPosition(long playerId, Vector3 position) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _previousPositions[playerId] = position; } } public sealed class RaceSession { public long PlayerId { get; } public string PlayerName { get; } public DateTime StartUtc { get; private set; } public int NextCheckpointIndex { get; private set; } = 1; public List CheckpointTimesMs { get; } = new List(); public List SplitTimesMs { get; } = new List(); public Vector3 LastPosition { get; set; } public bool HasPreviousPosition { get; set; } public bool Finished { get; set; } public long FinishTimeMs { get; set; } public bool FeatherCapeUnlocked { get; set; } public bool Disqualified { get; set; } public string DisqualifiedReason { get; set; } = string.Empty; public bool RunSkillNormalized { get; set; } public float? SavedRunSkillLevel { get; set; } public int LastMissedCheckpointNotified { get; set; } public bool IsRunning => StartUtc != default(DateTime); public RaceSession(long playerId, string playerName, DateTime startUtc) { PlayerId = playerId; PlayerName = playerName; StartUtc = startUtc; } public long ElapsedMs(DateTime nowUtc) { if (!IsRunning) { return 0L; } return (long)(nowUtc - StartUtc).TotalMilliseconds; } public void RecordCheckpoint(DateTime nowUtc) { long num = ElapsedMs(nowUtc); CheckpointTimesMs.Add(num); long num2 = ((CheckpointTimesMs.Count > 1) ? CheckpointTimesMs[CheckpointTimesMs.Count - 2] : 0); SplitTimesMs.Add(num - num2); NextCheckpointIndex++; } public RaceRecord ToCompletedRecord(DateTime finishUtc, int totalCheckpoints) { long num = (long)(finishUtc - StartUtc).TotalMilliseconds; return new RaceRecord { Player = PlayerName, PlayerId = PlayerId.ToString(), Date = finishUtc.ToString("yyyy-MM-dd"), FinishedAt = finishUtc.ToString("o"), TotalTimeMs = num, TotalTime = TimeFormatter.FormatDurationMs(num), CheckpointTimesMs = new List(CheckpointTimesMs), CheckpointTimes = FormatTimes(CheckpointTimesMs), SplitTimesMs = new List(SplitTimesMs), SplitTimes = FormatTimes(SplitTimesMs), CheckpointCount = totalCheckpoints, TrackId = TrackIdentity.GetId(JsonStorage.Track) }; } private static List FormatTimes(List timesMs) { List list = new List(timesMs.Count); foreach (long timesM in timesMs) { list.Add(TimeFormatter.FormatDurationMs(timesM)); } return list; } } [Serializable] public sealed class RaceRecord { public string Player; public string PlayerId; public string Date; public string FinishedAt; public long TotalTimeMs; public string TotalTime; public List CheckpointTimesMs = new List(); public List CheckpointTimes = new List(); public List SplitTimesMs = new List(); public List SplitTimes = new List(); public int CheckpointCount; public string TrackId = string.Empty; } public sealed class RaceSkillNormalizer : MonoBehaviour { private float _savedRunLevel = -1f; private bool _normalized; public static RaceSkillNormalizer Instance { get; private set; } public static bool IsActive { get { if ((Object)(object)Instance != (Object)null && Instance._normalized) { return ModConfig.NormalizeRunSkill.Value; } return false; } } public static float TargetLevel => Mathf.Clamp(ModConfig.NormalizedRunSkillLevel.Value, 0f, 100f); public static bool ShouldBlockRunSkillXp(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !ModConfig.NormalizeRunSkill.Value) { return false; } return IsActive; } private void Awake() { Instance = this; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !ModConfig.NormalizeRunSkill.Value) { RestoreIfNeeded(null); } else if (!ShouldNormalizeLocal()) { RestoreIfNeeded(localPlayer); } else { ApplyNormalizedRun(localPlayer); } } private static bool ShouldNormalizeLocal() { RaceStateSnapshot clientState = RaceNetSync.ClientState; if (clientState == null) { return false; } if (clientState.Phase == 3) { RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); if (localRunner != null && localRunner.StartUtcTicks > 0 && !localRunner.Disqualified) { return true; } return RaceNetSync.IsLocalRegistered(); } return false; } private void ApplyNormalizedRun(Player player) { Skills skills = ((Character)player).GetSkills(); Skill val = ((skills != null) ? skills.GetSkill((SkillType)102) : null); if (val != null) { if (!_normalized) { _savedRunLevel = val.m_level; _normalized = true; RunningManPlugin.Log.LogInfo((object)$"Run skill normalized: {_savedRunLevel:0.#} → {TargetLevel:0.#}"); } val.m_level = TargetLevel; val.m_accumulator = 0f; } } private void RestoreIfNeeded(Player player) { if (_normalized && !(_savedRunLevel < 0f)) { if (player == null) { player = Player.m_localPlayer; } object obj; if (player == null) { obj = null; } else { Skills skills = ((Character)player).GetSkills(); obj = ((skills != null) ? skills.GetSkill((SkillType)102) : null); } Skill val = (Skill)obj; if (val != null) { val.m_level = _savedRunLevel; RunningManPlugin.Log.LogInfo((object)$"Run skill restored to {_savedRunLevel:0.#}"); } _normalized = false; _savedRunLevel = -1f; } } } public static class RaceSkillUtil { public static bool TrySaveAndSetRunLevel(Player player, RaceSession session, float targetLevel) { if ((Object)(object)player == (Object)null || session == null || !ModConfig.NormalizeRunSkill.Value) { return false; } Skill runSkill = GetRunSkill(player); if (runSkill == null) { return false; } if (!session.RunSkillNormalized) { session.SavedRunSkillLevel = runSkill.m_level; session.RunSkillNormalized = true; } runSkill.m_level = Mathf.Clamp(targetLevel, 0f, 100f); return true; } public static void EnforceRunLevel(Player player, float targetLevel) { if ((Object)(object)player == (Object)null || !ModConfig.NormalizeRunSkill.Value) { return; } Skill runSkill = GetRunSkill(player); if (runSkill != null) { float num = Mathf.Clamp(targetLevel, 0f, 100f); if (Mathf.Abs(runSkill.m_level - num) > 0.05f) { runSkill.m_level = num; } } } public static void RestoreRunLevel(Player player, RaceSession session) { if (!((Object)(object)player == (Object)null) && session != null && session.RunSkillNormalized && session.SavedRunSkillLevel.HasValue) { Skill runSkill = GetRunSkill(player); if (runSkill != null) { runSkill.m_level = session.SavedRunSkillLevel.Value; } session.RunSkillNormalized = false; session.SavedRunSkillLevel = null; } } private static Skill GetRunSkill(Player player) { Skills val = ((player != null) ? ((Character)player).GetSkills() : null); if ((Object)(object)val == (Object)null) { return null; } return val.GetSkill((SkillType)102); } } public sealed class RaceSoundMonitor : MonoBehaviour { private static RaceSoundMonitor _instance; private int _lastCountdownRemaining = -1; private bool _wasCountdownActive; private int _lastPhase = -1; private bool _playedStartSound; private bool _playedCountdownSound; private int _lastCheckpointIndex; private bool _trackingRunner; private bool _localWasFinished; private bool _preloadedSounds; private bool _checkpointCueHandled; private bool _finishCueHandled; private void Awake() { _instance = this; } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } public static void NotifyCheckpointCue() { if (!((Object)(object)_instance == (Object)null)) { _instance._checkpointCueHandled = true; RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); if (localRunner != null && localRunner.NextCheckpointIndex > _instance._lastCheckpointIndex) { _instance._lastCheckpointIndex = localRunner.NextCheckpointIndex; } else { _instance._lastCheckpointIndex++; } } } public static void NotifyFinishCue() { if (!((Object)(object)_instance == (Object)null)) { _instance._finishCueHandled = true; _instance._localWasFinished = true; } } private void Update() { if (!((Object)(object)Player.m_localPlayer == (Object)null) && ModConfig.EnableRaceSounds.Value) { if (!_preloadedSounds) { _preloadedSounds = true; RaceSoundPlayer.PreloadCustomSounds(); } TrackCountdownStart(); TrackPhaseStart(); TrackRunnerEvents(); } } private void TrackCountdownStart() { bool flag = RaceNetSync.IsCountdownActive(); int countdownRemainingSeconds = RaceNetSync.GetCountdownRemainingSeconds(); if (flag && !_wasCountdownActive) { _playedCountdownSound = false; TryPlayCountdown(); } if (flag && !_playedCountdownSound) { TryPlayCountdown(); } if (flag && _lastCountdownRemaining > 0 && countdownRemainingSeconds <= 0) { TryPlayRaceStart(); } if (_wasCountdownActive && !flag && _lastCountdownRemaining == 0) { TryPlayRaceStart(); } _wasCountdownActive = flag; if (flag) { _lastCountdownRemaining = countdownRemainingSeconds; return; } _lastCountdownRemaining = -1; _playedCountdownSound = false; } private void TrackPhaseStart() { RaceStateSnapshot clientState = RaceNetSync.ClientState; if (clientState != null) { if (_lastPhase == 2 && clientState.Phase == 3) { TryPlayRaceStart(); } if (clientState.Phase == 0 || clientState.Phase == 1 || clientState.Phase == 4) { _playedStartSound = false; _trackingRunner = false; _lastCheckpointIndex = 0; _localWasFinished = false; _checkpointCueHandled = false; _finishCueHandled = false; } _lastPhase = clientState.Phase; } } private void TrackRunnerEvents() { RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); if (localRunner == null || localRunner.StartUtcTicks <= 0) { _trackingRunner = false; _lastCheckpointIndex = 0; _localWasFinished = false; return; } if (!_trackingRunner) { _trackingRunner = true; _lastCheckpointIndex = localRunner.NextCheckpointIndex; _localWasFinished = localRunner.Finished; return; } if (localRunner.NextCheckpointIndex > _lastCheckpointIndex) { if (!_checkpointCueHandled) { RaceSoundPlayer.PlayCheckpoint(); } _checkpointCueHandled = false; } if (localRunner.Finished && !_localWasFinished && localRunner.Place == 1) { if (!_finishCueHandled) { RaceSoundPlayer.PlayFirstPlaceFinish(); } _finishCueHandled = false; } _lastCheckpointIndex = localRunner.NextCheckpointIndex; _localWasFinished = localRunner.Finished; } private void TryPlayCountdown() { if (!_playedCountdownSound) { _playedCountdownSound = true; RaceSoundPlayer.PlayCountdown(); } } private void TryPlayRaceStart() { if (!_playedStartSound) { _playedStartSound = true; RaceSoundPlayer.PlayRaceStart(); } } } public static class RaceSoundPlayer { private static MonoBehaviour _host; private static readonly HashSet MissingPrefabs = new HashSet(StringComparer.Ordinal); public static void Initialize(MonoBehaviour host) { _host = host; RaceCustomAudio.Initialize(host); } public static void PreloadCustomSounds() { RaceCustomAudio.PreloadAll(); } public static void PlayRaceStart() { if (ModConfig.EnableRaceSounds.Value) { float value = ModConfig.RaceSoundVolume.Value; if (!RaceCustomAudio.TryPlay(RaceSoundSlot.Start, value, PlayRaceStartFallback)) { PlayRaceStartFallback(); } } } public static void PlayCountdown() { if (ModConfig.EnableRaceSounds.Value) { float value = ModConfig.RaceSoundVolume.Value; RaceCustomAudio.TryPlayStream(RaceSoundSlot.Countdown, value); } } public static void PlayFalseStart() { if (ModConfig.EnableRaceSounds.Value) { RaceCustomAudio.StopStream(); float value = ModConfig.RaceSoundVolume.Value; RaceCustomAudio.TryPlay(RaceSoundSlot.FalseStart, value, null); } } public static void PlayCheckpoint() { if (ModConfig.EnableRaceSounds.Value) { float value = ModConfig.RaceSoundVolume.Value; if (!RaceCustomAudio.TryPlay(RaceSoundSlot.Checkpoint, value, PlayCheckpointFallback)) { PlayCheckpointFallback(); } } } public static void PlayFirstPlaceFinish() { if (ModConfig.EnableRaceSounds.Value) { float value = ModConfig.RaceSoundVolume.Value; if (!RaceCustomAudio.TryPlay(RaceSoundSlot.Finish, value, PlayFirstPlaceFinishFallback)) { PlayFirstPlaceFinishFallback(); } } } private static void PlayRaceStartFallback() { PlaySequence(ModConfig.RaceStartSounds.Value, 0.35f); } private static void PlayCheckpointFallback() { PlaySingle(ModConfig.CheckpointSound.Value); } private static void PlayFirstPlaceFinishFallback() { PlaySequence(ModConfig.FirstPlaceFinishSounds.Value, 0.18f); } private static void PlaySingle(string prefabName) { if (!string.IsNullOrWhiteSpace(prefabName)) { PlayPrefab(prefabName.Trim(), ModConfig.RaceSoundVolume.Value); } } private static void PlaySequence(string prefabList, float delaySeconds) { if (!((Object)(object)_host == (Object)null) && !string.IsNullOrWhiteSpace(prefabList)) { string[] array = prefabList.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length != 0) { _host.StartCoroutine(PlaySequenceRoutine(array, delaySeconds)); } } } private static IEnumerator PlaySequenceRoutine(string[] prefabNames, float delaySeconds) { float volume = ModConfig.RaceSoundVolume.Value; for (int i = 0; i < prefabNames.Length; i++) { PlayPrefab(prefabNames[i].Trim(), volume); if (i < prefabNames.Length - 1 && delaySeconds > 0f) { yield return (object)new WaitForSeconds(delaySeconds); } } } private static void PlayPrefab(string prefabName, float volumeMultiplier) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(prefabName) || (Object)(object)ZNetScene.instance == (Object)null) { return; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { if (MissingPrefabs.Add(prefabName)) { RunningManPlugin.Log.LogWarning((object)("RunningMan sound prefab not found: " + prefabName)); } } else { Vector3 listenPosition = GetListenPosition(); ApplyVolume(Object.Instantiate(prefab, listenPosition, Quaternion.identity), volumeMultiplier); } } private static void ApplyVolume(GameObject instance, float volumeMultiplier) { if ((Object)(object)instance == (Object)null) { return; } volumeMultiplier = Mathf.Clamp(volumeMultiplier, 0f, 2f); AudioSource[] componentsInChildren = instance.GetComponentsInChildren(true); foreach (AudioSource val in componentsInChildren) { val.volume = Mathf.Clamp01(val.volume * volumeMultiplier); if ((Object)(object)val.clip != (Object)null && !val.isPlaying) { val.Play(); } } } private static Vector3 GetListenPosition() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer != (Object)null) { return ((Component)Player.m_localPlayer).transform.position; } if ((Object)(object)Camera.main != (Object)null) { return ((Component)Camera.main).transform.position; } return Vector3.zero; } } [Serializable] public sealed class RaceStateSnapshot { public int Phase; public long CountdownEndUtcTicks; public bool DebugMode; public bool RegistrationOpen; public int TotalCheckpoints; public List Registered = new List(); public List Runners = new List(); public List AdminPlayerIds = new List(); public string TrackName = string.Empty; public string TrackId = string.Empty; public List WorldRecords = new List(); public List ParCheckpointTimesMs = new List(); public long ParTotalTimeMs; public AllowedGearRules AllowedGear; } [Serializable] public sealed class RegisteredParticipant { public long PlayerId; public string PlayerName; } [Serializable] public sealed class RunnerSnapshot { public long PlayerId; public string PlayerName; public long StartUtcTicks; public int NextCheckpointIndex; public bool Finished; public long FinishTimeMs; public int Place; public float PathProgress; public bool Disqualified; public string DisqualifiedReason = string.Empty; public bool FeatherCapeUnlocked; public long LastCheckpointTimeMs; } [Serializable] public sealed class WorldRecordEntry { public int Place; public string PlayerName; public string Time; public long TimeMs; public string Date; } public enum RaceBulletinKind { Records, Rules } public static class RaceWrBoards { public const float MatchDistance = 2.5f; public const float RefreshInterval = 1f; private static readonly HashSet ConfiguredLayouts = new HashSet(); private static readonly Dictionary BaselineTextSizes = new Dictionary(); private static readonly Color SignTextColor = Color.white; public static List GetBoardPositions(RaceBulletinKind kind) { if (ValheimUtil.IsServerAuthority()) { if (kind != RaceBulletinKind.Rules) { return JsonStorage.WrBoards; } return JsonStorage.RulesBoards; } if (kind != RaceBulletinKind.Rules) { return RaceNetSync.ClientWrBoards; } return RaceNetSync.ClientRulesBoards; } public static bool TryGetBulletinKind(Sign sign, out RaceBulletinKind kind) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) kind = RaceBulletinKind.Records; if ((Object)(object)sign == (Object)null) { return false; } Vector3 position = ((Component)sign).transform.position; int index; bool flag = TryFindBoardIndex(RaceBulletinKind.Records, position, 2.5f, out index); int index2; bool flag2 = TryFindBoardIndex(RaceBulletinKind.Rules, position, 2.5f, out index2); if (!flag && !flag2) { return false; } if (flag && !flag2) { kind = RaceBulletinKind.Records; return true; } if (flag2 && !flag) { kind = RaceBulletinKind.Rules; return true; } float num = Vector3.Distance(position, ValheimUtil.FromData(GetBoardPositions(RaceBulletinKind.Records)[index])); float num2 = Vector3.Distance(position, ValheimUtil.FromData(GetBoardPositions(RaceBulletinKind.Rules)[index2])); kind = ((num2 < num) ? RaceBulletinKind.Rules : RaceBulletinKind.Records); return true; } public static bool IsMarkedSign(Sign sign) { RaceBulletinKind kind; return TryGetBulletinKind(sign, out kind); } public static bool TryFindBoardIndex(RaceBulletinKind kind, Vector3 position, float maxDistance, out int index) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) index = -1; List boardPositions = GetBoardPositions(kind); if (boardPositions == null || boardPositions.Count == 0) { return false; } float num = float.MaxValue; for (int i = 0; i < boardPositions.Count; i++) { Vector3Data vector3Data = boardPositions[i]; if (vector3Data != null) { float num2 = Vector3.Distance(position, ValheimUtil.FromData(vector3Data)); if (num2 <= maxDistance && num2 < num) { num = num2; index = i; } } } return index >= 0; } public static string BuildSignText(RaceBulletinKind kind) { if (kind != RaceBulletinKind.Rules) { return BuildRecordsSignText(); } return BuildRulesSignText(); } public static string BuildRecordsSignText() { StringBuilder stringBuilder = new StringBuilder(180); RaceStateSnapshot clientState = RaceNetSync.ClientState; string text = clientState?.TrackName; if (string.IsNullOrWhiteSpace(text)) { text = TrackIdentity.GetDisplayName(RaceNetSync.GetActiveTrack()); } if (string.IsNullOrWhiteSpace(text)) { text = "Marathon"; } stringBuilder.Append(""); stringBuilder.Append(Truncate(text.Trim(), 18)); stringBuilder.Append('\n'); string value = FormatTrackLength(); if (!string.IsNullOrEmpty(value)) { stringBuilder.Append(value); stringBuilder.Append('\n'); } List list = clientState?.WorldRecords; if (list == null || list.Count == 0) { stringBuilder.Append("No records yet"); stringBuilder.Append(""); return stringBuilder.ToString(); } int num = Math.Min(list.Count, ModConfig.WorldRecordsLimit?.Value ?? 5); for (int i = 0; i < num; i++) { WorldRecordEntry worldRecordEntry = list[i]; string value2 = Truncate(worldRecordEntry.PlayerName ?? "?", 10); string value3 = worldRecordEntry.Time ?? "?"; stringBuilder.Append(worldRecordEntry.Place); stringBuilder.Append('.'); stringBuilder.Append(value2); stringBuilder.Append(' '); stringBuilder.Append(value3); if (i < num - 1) { stringBuilder.Append('\n'); } } stringBuilder.Append(""); return stringBuilder.ToString(); } public static string BuildRulesSignText() { AllowedGearRules allowedGearRules = GearValidator.GetActiveRules() ?? AllowedGearRules.CreateDefaults(); StringBuilder stringBuilder = new StringBuilder(220); stringBuilder.Append(""); stringBuilder.Append("RULES\n"); stringBuilder.Append(ShortArmorLine(allowedGearRules)); stringBuilder.Append('\n'); stringBuilder.Append(ShortCapeLine(allowedGearRules)); stringBuilder.Append('\n'); stringBuilder.Append(ShortConsumableLine(allowedGearRules.RequiredAntiSting, "Anti-Sting")); stringBuilder.Append('\n'); stringBuilder.Append(ShortConsumableLine(allowedGearRules.RequiredRatatosk, "Ratatosk")); stringBuilder.Append('\n'); stringBuilder.Append(ShortConsumableLine(allowedGearRules.RequiredSalad, "Salad")); stringBuilder.Append('\n'); stringBuilder.Append(ShortConsumableLine(allowedGearRules.RequiredBloodPudding, "BloodPudding")); stringBuilder.Append('\n'); stringBuilder.Append(ShortConsumableLine(allowedGearRules.RequiredMushroomOmelette, "Omelette")); stringBuilder.Append('\n'); stringBuilder.Append("No gear swaps\n"); stringBuilder.Append("Pass between CP torches\n"); stringBuilder.Append("Meads OK anytime\n"); stringBuilder.Append("Only race foods\n"); stringBuilder.Append("Any Forsaken power\n"); stringBuilder.Append("Get Rested!"); stringBuilder.Append(""); return stringBuilder.ToString(); } public static void ApplySignText(Sign sign) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sign == (Object)null) && !((Object)(object)sign.m_textWidget == (Object)null) && TryGetBulletinKind(sign, out var kind)) { ConfigureTextLayout(sign, kind); string text = BuildSignText(kind); if (sign.m_characterLimit < text.Length) { sign.m_characterLimit = Math.Max(500, text.Length); } if (!string.Equals(((TMP_Text)sign.m_textWidget).text, text, StringComparison.Ordinal)) { ((TMP_Text)sign.m_textWidget).text = text; } sign.m_currentText = text; ((Graphic)sign.m_textWidget).color = SignTextColor; } } public static string GetHoverLabel(RaceBulletinKind kind) { if (kind != RaceBulletinKind.Rules) { return "Sign\nRunningMan world records"; } return "Sign\nRunningMan RULES"; } private static void ConfigureTextLayout(Sign sign, RaceBulletinKind kind) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI textWidget = sign.m_textWidget; if ((Object)(object)textWidget == (Object)null) { return; } ((Graphic)textWidget).color = SignTextColor; ((TMP_Text)textWidget).enableAutoSizing = true; ((TMP_Text)textWidget).fontSizeMin = 1.5f; ((TMP_Text)textWidget).fontSizeMax = ((kind == RaceBulletinKind.Rules) ? 9f : 10f); ((TMP_Text)textWidget).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)textWidget).overflowMode = (TextOverflowModes)0; ((TMP_Text)textWidget).alignment = (TextAlignmentOptions)514; ((TMP_Text)textWidget).margin = Vector4.zero; RectTransform rectTransform = ((TMP_Text)textWidget).rectTransform; if ((Object)(object)rectTransform == (Object)null) { return; } int instanceID = ((Object)sign).GetInstanceID(); if (!BaselineTextSizes.TryGetValue(instanceID, out var value)) { Rect rect = rectTransform.rect; float width = ((Rect)(ref rect)).width; rect = rectTransform.rect; float height = ((Rect)(ref rect)).height; if (width <= 0.001f || height <= 0.001f) { return; } ((Vector2)(ref value))..ctor(width, height); BaselineTextSizes[instanceID] = value; } float num = ((kind == RaceBulletinKind.Rules) ? 3.4f : 1.55f); float num2 = ((kind == RaceBulletinKind.Rules) ? 6.2f : 1.45f); rectTransform.SetSizeWithCurrentAnchors((Axis)0, value.x * num); rectTransform.SetSizeWithCurrentAnchors((Axis)1, value.y * num2); ConfiguredLayouts.Add(instanceID); } public static bool TryGetLookedAtSign(out Sign sign, out Vector3 position) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) sign = null; position = Vector3.zero; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } RaycastHit[] array = Physics.RaycastAll(new Ray(((Character)localPlayer).GetEyePoint(), ((Character)localPlayer).GetLookDir()), 12f); Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); RaycastHit[] array2 = array; for (int num = 0; num < array2.Length; num++) { RaycastHit val = array2[num]; if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null)) { Sign componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { sign = componentInParent; position = ((Component)componentInParent).transform.position; return true; } } } return false; } private static string FormatTrackLength() { TrackPath.PathData pathData = TrackPath.Build(RaceNetSync.GetActiveTrack()); if (pathData == null || pathData.TotalLength < 1f) { return string.Empty; } if (pathData.TotalLength >= 1000f) { return $"{pathData.TotalLength / 1000f:0.##} km"; } return $"{Mathf.RoundToInt(pathData.TotalLength)} m"; } private static string ShortArmorLine(AllowedGearRules rules) { if (IsTrollSet(rules)) { return "Armor: Troll set"; } return "Armor: see /run loadout"; } private static string ShortCapeLine(AllowedGearRules rules) { if (string.IsNullOrWhiteSpace(rules.Cape)) { return "Cape: none"; } if (string.Equals(rules.Cape, "CapeFeather", StringComparison.OrdinalIgnoreCase)) { return "Cape: Feather"; } return "Cape: " + Truncate(rules.Cape, 14); } private static string ShortConsumableLine(int count, string label) { return $"{Math.Max(0, count)}x {label}"; } private static bool IsTrollSet(AllowedGearRules rules) { if (string.Equals(rules.Helmet, "HelmetTrollLeather", StringComparison.OrdinalIgnoreCase) && string.Equals(rules.Chest, "ArmorTrollLeatherChest", StringComparison.OrdinalIgnoreCase)) { return string.Equals(rules.Legs, "ArmorTrollLeatherLegs", StringComparison.OrdinalIgnoreCase); } return false; } private static string Truncate(string value, int maxChars) { if (string.IsNullOrEmpty(value) || value.Length <= maxChars) { return value ?? string.Empty; } return value.Substring(0, Math.Max(1, maxChars - 1)) + "..."; } } public sealed class RaceWrBoardHud : MonoBehaviour { private float _nextRefresh; private void Update() { if (!(Time.time < _nextRefresh)) { _nextRefresh = Time.time + 1f; RefreshNearbySigns(); } } private static void RefreshNearbySigns() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || ((RaceWrBoards.GetBoardPositions(RaceBulletinKind.Records)?.Count ?? 0) <= 0 && (RaceWrBoards.GetBoardPositions(RaceBulletinKind.Rules)?.Count ?? 0) <= 0)) { return; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; Sign[] array = Object.FindObjectsByType((FindObjectsSortMode)0); if (array == null || array.Length == 0) { return; } Sign[] array2 = array; foreach (Sign val in array2) { if (!((Object)(object)val == (Object)null) && !(Vector3.Distance(position, ((Component)val).transform.position) > 64f) && RaceWrBoards.IsMarkedSign(val)) { RaceWrBoards.ApplySignText(val); } } } } [BepInPlugin("com.runningman.valheim", "RunningMan", "1.5.10")] public sealed class RunningManPlugin : BaseUnityPlugin { public const string PluginGuid = "com.runningman.valheim"; public const string PluginName = "RunningMan"; public const string PluginVersion = "1.5.10"; private Harmony _harmony; internal static RunningManPlugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Initialize(((BaseUnityPlugin)this).Config); JsonStorage.Initialize(Paths.ConfigPath); _harmony = Harmony.CreateAndPatchAll(typeof(GameStartPatch).Assembly, "com.runningman.valheim"); Commands.Register(); RaceManager.Initialize(this); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); RaceSoundPlayer.Initialize((MonoBehaviour)(object)((Component)this).gameObject.AddComponent()); Log.LogInfo((object)"RunningMan v1.5.10 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public static class TrackIdentity { public static string GetId(TrackConfig track) { if (track == null || !track.HasStart) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); AppendGate(stringBuilder, track.StartGate); AppendGate(stringBuilder, track.FinishGate); if (track.Checkpoints != null) { foreach (Checkpoint checkpoint in track.Checkpoints) { AppendGate(stringBuilder, checkpoint?.Gate); } } return stringBuilder.ToString().GetHashCode().ToString("X8", CultureInfo.InvariantCulture); } public static string GetDisplayName(TrackConfig track) { if (track == null) { return "Unconfigured track"; } if (!string.IsNullOrWhiteSpace(track.Name)) { return track.Name.Trim(); } string id = GetId(track); if (!string.IsNullOrEmpty(id)) { return "Track " + id; } return "Unconfigured track"; } private static void AppendGate(StringBuilder builder, RaceGate gate) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (gate != null && gate.IsConfigured()) { AppendPoint(builder, gate.GetPointA()); AppendPoint(builder, gate.GetPointB()); AppendPoint(builder, gate.GetForward()); } } private static void AppendPoint(StringBuilder builder, Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) builder.Append(Mathf.RoundToInt(point.x * 10f).ToString(CultureInfo.InvariantCulture)); builder.Append('|'); builder.Append(Mathf.RoundToInt(point.y * 10f).ToString(CultureInfo.InvariantCulture)); builder.Append('|'); builder.Append(Mathf.RoundToInt(point.z * 10f).ToString(CultureInfo.InvariantCulture)); builder.Append(';'); } } public static class TrackPath { public sealed class PathData { public readonly List Points = new List(); public readonly List Cumulative = new List(); public float TotalLength; } public static PathData Build(TrackConfig track) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) PathData pathData = new PathData(); if (track == null) { return pathData; } if (track.HasStart) { AddPoint(pathData, track.StartGate.GetMidpoint()); } if (track.Checkpoints != null) { List list = new List(track.Checkpoints); list.Sort((Checkpoint left, Checkpoint right) => left.Index.CompareTo(right.Index)); foreach (Checkpoint item in list) { if (item?.Gate != null && item.Gate.IsConfigured()) { AddPoint(pathData, item.Gate.GetMidpoint()); } } } if (track.HasFinish) { AddPoint(pathData, track.FinishGate.GetMidpoint()); } return pathData; } public static float GetProgress(TrackConfig track, Vector3 worldPosition, int nextCheckpointIndex, bool finished) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) PathData pathData = Build(track); if (pathData.Points.Count < 2) { return 0f; } if (finished) { return pathData.TotalLength; } int num = Mathf.Clamp(nextCheckpointIndex - 1, 0, pathData.Points.Count - 2); int index = num + 1; float num2 = pathData.Cumulative[num]; float num3 = ProjectOntoSegmentXZ(worldPosition, pathData.Points[num], pathData.Points[index]); return num2 + num3; } public static int FindCheckpointInsertIndex(TrackConfig track, Vector3 position) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) int valueOrDefault = (track?.Checkpoints?.Count).GetValueOrDefault(); if (valueOrDefault == 0) { return 1; } PathData pathData = Build(track); if (pathData.Points.Count < 2) { return valueOrDefault + 1; } int num = 0; float num2 = float.MaxValue; for (int i = 0; i < pathData.Points.Count - 1; i++) { float num3 = DistanceToSegmentXZ(position, pathData.Points[i], pathData.Points[i + 1]); if (!(num3 >= num2)) { num2 = num3; num = i; } } if (track.HasStart) { return Mathf.Clamp(num + 1, 1, valueOrDefault + 1); } if (track.HasFinish && num >= valueOrDefault - 1) { return valueOrDefault + 1; } return Mathf.Clamp(num + 2, 1, valueOrDefault + 1); } private static void AddPoint(PathData path, Vector3 point) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (path.Points.Count == 0) { path.Points.Add(point); path.Cumulative.Add(0f); path.TotalLength = 0f; return; } Vector3 val = path.Points[path.Points.Count - 1]; Vector3 val2 = point - val; val2.y = 0f; float magnitude = ((Vector3)(ref val2)).magnitude; path.Points.Add(point); path.TotalLength += magnitude; path.Cumulative.Add(path.TotalLength); } private static float DistanceToSegmentXZ(Vector3 point, Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) Vector3 val = point; val.y = 0f; Vector3 val2 = a; val2.y = 0f; Vector3 val3 = b; val3.y = 0f; Vector3 val4 = val3 - val2; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude < 0.0001f) { return Vector3.Distance(val, val2); } float num = Mathf.Clamp01(Vector3.Dot(val - val2, val4) / sqrMagnitude); Vector3 val5 = val2 + val4 * num; return Vector3.Distance(val, val5); } private static float ProjectOntoSegmentXZ(Vector3 point, Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) Vector3 val = point; val.y = 0f; Vector3 val2 = a; val2.y = 0f; Vector3 val3 = b; val3.y = 0f; Vector3 val4 = val3 - val2; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude < 0.0001f) { return 0f; } float num = Mathf.Clamp01(Vector3.Dot(val - val2, val4) / sqrMagnitude); return Mathf.Sqrt(sqrMagnitude) * num; } } public static class TriggerDetector { public static bool DidCrossForward(Vector3 previousPosition, Vector3 currentPosition, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance = 8f) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (gate == null || !gate.IsConfigured()) { return false; } if (!IsWithinGateVolume(currentPosition, gate, maxHorizontalDistance, maxVerticalDistance)) { return false; } Vector3 pointA = gate.GetPointA(); Vector3 pointB = gate.GetPointB(); Vector3 gateForward = GetGateForward(gate, pointA, pointB); Vector3 midpoint = gate.GetMidpoint(); float num = Vector3.Dot(Flatten(previousPosition - midpoint), gateForward); float num2 = Vector3.Dot(Flatten(currentPosition - midpoint), gateForward); if (num <= 0f) { return num2 > 0f; } return false; } public static bool DidCrossLine(Vector3 previousPosition, Vector3 currentPosition, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance = 8f) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (gate == null || !gate.IsConfigured()) { return false; } if (!IsWithinGateVolume(currentPosition, gate, maxHorizontalDistance, maxVerticalDistance)) { return false; } Vector3 pointA = gate.GetPointA(); Vector3 pointB = gate.GetPointB(); Vector3 gateForward = GetGateForward(gate, pointA, pointB); Vector3 midpoint = gate.GetMidpoint(); float num = Vector3.Dot(Flatten(previousPosition - midpoint), gateForward); float num2 = Vector3.Dot(Flatten(currentPosition - midpoint), gateForward); if (Mathf.Approximately(num, 0f) || Mathf.Approximately(num2, 0f)) { return false; } return num * num2 < 0f; } public static bool DidEnterGateVolume(Vector3 previousPosition, Vector3 currentPosition, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (gate == null || !gate.IsConfigured()) { return false; } bool num = IsWithinGateVolume(previousPosition, gate, maxHorizontalDistance, maxVerticalDistance); bool flag = IsWithinGateVolume(currentPosition, gate, maxHorizontalDistance, maxVerticalDistance); return !num && flag; } public static bool DidTriggerGate(Vector3 previousPosition, Vector3 currentPosition, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (gate == null || !gate.IsConfigured()) { return false; } if (DidCrossForward(previousPosition, currentPosition, gate, maxHorizontalDistance, maxVerticalDistance) || DidCrossLine(previousPosition, currentPosition, gate, maxHorizontalDistance, maxVerticalDistance) || DidEnterGateVolume(previousPosition, currentPosition, gate, maxHorizontalDistance, maxVerticalDistance) || DidCrossGatePlane(previousPosition, currentPosition, gate, maxHorizontalDistance, maxVerticalDistance) || DidTriggerGateAlongPath(previousPosition, currentPosition, gate, maxHorizontalDistance, maxVerticalDistance)) { return true; } bool flag = IsWithinGateVolume(previousPosition, gate, maxHorizontalDistance * 1.5f, maxVerticalDistance); if (IsWithinGateVolume(currentPosition, gate, maxHorizontalDistance, maxVerticalDistance)) { return !flag; } return false; } public static bool DidCrossGatePlane(Vector3 previousPosition, Vector3 currentPosition, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if (gate == null || !gate.IsConfigured()) { return false; } Vector3 pointA = gate.GetPointA(); Vector3 pointB = gate.GetPointB(); Vector3 gateForward = GetGateForward(gate, pointA, pointB); Vector3 midpoint = gate.GetMidpoint(); float num = Vector3.Dot(Flatten(previousPosition - midpoint), gateForward); float num2 = Vector3.Dot(Flatten(currentPosition - midpoint), gateForward); if (Mathf.Approximately(num, 0f)) { return IsWithinGateVolume(previousPosition, gate, maxHorizontalDistance, maxVerticalDistance); } if (Mathf.Approximately(num2, 0f)) { return IsWithinGateVolume(currentPosition, gate, maxHorizontalDistance, maxVerticalDistance); } if (num * num2 > 0f) { return SegmentIntersectsGateVolume(previousPosition, currentPosition, gate, maxHorizontalDistance, maxVerticalDistance); } float num3 = num / (num - num2); if (num3 < 0f || num3 > 1f) { return false; } return IsWithinGateVolume(Vector3.Lerp(previousPosition, currentPosition, num3), gate, maxHorizontalDistance, maxVerticalDistance); } public static bool SegmentIntersectsGateVolume(Vector3 start, Vector3 end, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.5f, ModConfig.TriggerSweepStepDistance.Value); float num2 = Vector3.Distance(Flatten(start), Flatten(end)); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); for (int i = 0; i <= num3; i++) { if (IsWithinGateVolume(Vector3.Lerp(start, end, (float)i / (float)num3), gate, maxHorizontalDistance, maxVerticalDistance)) { return true; } } return false; } private static bool DidTriggerGateAlongPath(Vector3 previousPosition, Vector3 currentPosition, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.5f, ModConfig.TriggerSweepStepDistance.Value); float num2 = Vector3.Distance(Flatten(previousPosition), Flatten(currentPosition)); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); for (int i = 1; i <= num3; i++) { float num4 = ((float)i - 1f) / (float)num3; float num5 = (float)i / (float)num3; Vector3 previousPosition2 = Vector3.Lerp(previousPosition, currentPosition, num4); Vector3 currentPosition2 = Vector3.Lerp(previousPosition, currentPosition, num5); if (DidCrossForward(previousPosition2, currentPosition2, gate, maxHorizontalDistance, maxVerticalDistance) || DidCrossLine(previousPosition2, currentPosition2, gate, maxHorizontalDistance, maxVerticalDistance) || DidEnterGateVolume(previousPosition2, currentPosition2, gate, maxHorizontalDistance, maxVerticalDistance) || DidCrossGatePlane(previousPosition2, currentPosition2, gate, maxHorizontalDistance, maxVerticalDistance)) { return true; } } return false; } public static bool IsWithinGateVolume(Vector3 point, RaceGate gate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if (gate == null || !gate.IsConfigured()) { return false; } Vector3 pointA = gate.GetPointA(); Vector3 pointB = gate.GetPointB(); Vector3 val = Flatten(point); Vector3 val2 = Flatten(pointA); Vector3 val3 = Flatten(pointB) - val2; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < 0.0001f) { if (Vector3.Distance(val, val2) <= maxHorizontalDistance) { return Mathf.Abs(point.y - pointA.y) <= maxVerticalDistance; } return false; } float num = Vector3.Dot(val - val2, val3) / sqrMagnitude; float num2 = Mathf.Max(0f, ModConfig.GateEndPadding.Value) / Mathf.Sqrt(sqrMagnitude); if (num < 0f - num2 || num > 1f + num2) { return false; } num = Mathf.Clamp01(num); Vector3 val4 = val2 + val3 * num; if (Vector3.Distance(val, val4) > maxHorizontalDistance) { return false; } Vector3 val5 = Vector3.Lerp(pointA, pointB, num); return Mathf.Abs(point.y - val5.y) <= maxVerticalDistance; } public static float DistancePointToSegmentXZ(Vector3 point, Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Flatten(point); Vector3 val2 = Flatten(a); Vector3 val3 = Flatten(b) - val2; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < 0.0001f) { return Vector3.Distance(val, val2); } float num = Mathf.Clamp01(Vector3.Dot(val - val2, val3) / sqrMagnitude); Vector3 val4 = val2 + val3 * num; return Vector3.Distance(val, val4); } private static Vector3 GetGateForward(RaceGate gate, Vector3 pointA, Vector3 pointB) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) Vector3 result = gate.GetForward(); result.y = 0f; if (((Vector3)(ref result)).sqrMagnitude < 0.01f) { Vector3 val = pointB - pointA; val.y = 0f; result = Vector3.Cross(Vector3.up, ((Vector3)(ref val)).normalized); } else { ((Vector3)(ref result)).Normalize(); } return result; } private static Vector3 Flatten(Vector3 vector) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) vector.y = 0f; return vector; } public static bool IsInStartingArea(Vector3 position, RaceGate startGate, float offsetMeters, float depthMeters, float sidePadding, float maxVertical) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (startGate == null || !startGate.IsConfigured()) { return false; } Vector3 pointA = startGate.GetPointA(); Vector3 pointB = startGate.GetPointB(); Vector3 gateForward = GetGateForward(startGate, pointA, pointB); Vector3 midpoint = startGate.GetMidpoint(); Vector3 val = Flatten(pointB - pointA); float num = ((Vector3)(ref val)).magnitude * 0.5f + Mathf.Max(0f, sidePadding); if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { ((Vector3)(ref val)).Normalize(); } else { val = Vector3.Cross(Vector3.up, gateForward); } Vector3 val2 = Flatten(position - midpoint); float num2 = Vector3.Dot(val2, gateForward); float num3 = Mathf.Abs(Vector3.Dot(val2, val)); float num4 = Mathf.Abs(position.y - midpoint.y); float num5 = Mathf.Max(0.1f, offsetMeters); float num6 = Mathf.Max(0.5f, depthMeters) * 0.5f; float num7 = 0f - num5 - num6; float num8 = Mathf.Min(0.35f, 0f - num5 + num6); if (num2 >= num7 && num2 <= num8 && num3 <= num) { return num4 <= maxVertical; } return false; } public static bool HasFalseStarted(Vector3 previousPosition, Vector3 currentPosition, RaceGate startGate, float maxHorizontalDistance, float maxVerticalDistance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (startGate == null || !startGate.IsConfigured()) { return false; } if (DidCrossForward(previousPosition, currentPosition, startGate, maxHorizontalDistance, maxVerticalDistance)) { return true; } Vector3 pointA = startGate.GetPointA(); Vector3 pointB = startGate.GetPointB(); Vector3 gateForward = GetGateForward(startGate, pointA, pointB); Vector3 midpoint = startGate.GetMidpoint(); if (Vector3.Dot(Flatten(currentPosition - midpoint), gateForward) <= 0.75f) { return false; } return DistancePointToSegmentXZ(currentPosition, pointA, pointB) <= maxHorizontalDistance + 2f; } public static void GetStartingAreaCorners(RaceGate startGate, float offsetMeters, float depthMeters, float sidePadding, out Vector3 c0, out Vector3 c1, out Vector3 c2, out Vector3 c3) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) c0 = (c1 = (c2 = (c3 = Vector3.zero))); if (startGate != null && startGate.IsConfigured()) { Vector3 pointA = startGate.GetPointA(); Vector3 pointB = startGate.GetPointB(); Vector3 gateForward = GetGateForward(startGate, pointA, pointB); Vector3 midpoint = startGate.GetMidpoint(); Vector3 val = Flatten(pointB - pointA); float num = ((Vector3)(ref val)).magnitude * 0.5f + Mathf.Max(0f, sidePadding); if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { ((Vector3)(ref val)).Normalize(); } else { val = Vector3.Cross(Vector3.up, gateForward); } float num2 = Mathf.Max(0.1f, offsetMeters); float num3 = Mathf.Max(0.5f, depthMeters) * 0.5f; float num4 = 0f - num2 + num3; float num5 = 0f - num2 - num3; num4 = Mathf.Min(0.35f, num4); Vector3 val2 = val * num; Vector3 val3 = -val * num; Vector3 val4 = Vector3.up * 0.2f; c0 = midpoint + gateForward * num4 + val2 + val4; c1 = midpoint + gateForward * num4 + val3 + val4; c2 = midpoint + gateForward * num5 + val3 + val4; c3 = midpoint + gateForward * num5 + val2 + val4; } } public static List DetectTorchCheckpoints(Vector3 origin, float searchRadius, float minPairDistance, float maxPairDistance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) List list = FindPrefabPositions("piece_groundtorch", origin, searchRadius); List list2 = new List(); HashSet hashSet = new HashSet(); for (int i = 0; i < list.Count; i++) { if (hashSet.Contains(i)) { continue; } int num = -1; float num2 = float.MaxValue; for (int j = i + 1; j < list.Count; j++) { if (!hashSet.Contains(j)) { float num3 = Vector3.Distance(list[i], list[j]); if (!(num3 < minPairDistance) && !(num3 > maxPairDistance) && num3 < num2) { num2 = num3; num = j; } } } if (num >= 0) { hashSet.Add(i); hashSet.Add(num); Vector3 val = list[i]; Vector3 val2 = list[num]; Vector3 up = Vector3.up; Vector3 val3 = val2 - val; Vector3 forward = Vector3.Cross(up, ((Vector3)(ref val3)).normalized); list2.Add(new Checkpoint(list2.Count + 1, new RaceGate(val, val2, forward))); } } list2.Sort(delegate(Checkpoint left, Checkpoint right) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) float num5 = Vector3.Distance(origin, left.Gate.GetMidpoint()); float value = Vector3.Distance(origin, right.Gate.GetMidpoint()); return num5.CompareTo(value); }); for (int num4 = 0; num4 < list2.Count; num4++) { list2[num4].Index = num4 + 1; } return list2; } public static RaceGate DetectNearestTorchPair(Vector3 origin, float searchRadius, float minPairDistance, float maxPairDistance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) List list = FindPrefabPositions("piece_groundtorch", origin, searchRadius); if (list.Count < 2) { return null; } list.Sort((Vector3 left, Vector3 right) => Vector3.Distance(origin, left).CompareTo(Vector3.Distance(origin, right))); Vector3 val = list[0]; Vector3 val2 = list[1]; float num = Vector3.Distance(val, val2); Vector3 val3; if (num >= minPairDistance && num <= maxPairDistance) { Vector3 up = Vector3.up; val3 = val2 - val; Vector3 forward = Vector3.Cross(up, ((Vector3)(ref val3)).normalized); return new RaceGate(val, val2, forward); } RaceGate result = null; float num2 = float.MaxValue; for (int num3 = 0; num3 < list.Count; num3++) { for (int num4 = num3 + 1; num4 < list.Count; num4++) { float num5 = Vector3.Distance(list[num3], list[num4]); if (!(num5 < minPairDistance) && !(num5 > maxPairDistance)) { float num6 = Vector3.Distance(origin, list[num3]) + Vector3.Distance(origin, list[num4]); if (!(num6 >= num2)) { num2 = num6; Vector3 up2 = Vector3.up; val3 = list[num4] - list[num3]; Vector3 forward2 = Vector3.Cross(up2, ((Vector3)(ref val3)).normalized); result = new RaceGate(list[num3], list[num4], forward2); } } } } return result; } public static bool TryFindLookSnapTarget(Vector3 origin, Vector3 lookDirection, float searchRadius, float maxAngleDegrees, out Vector3 position, out string prefabHint) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; prefabHint = null; lookDirection.y = 0f; if (((Vector3)(ref lookDirection)).sqrMagnitude < 0.0001f) { lookDirection = Vector3.forward; } else { ((Vector3)(ref lookDirection)).Normalize(); } List list = new List(); list.AddRange(FindPrefabPositions("piece_groundtorch", origin, searchRadius)); int count = list.Count; list.AddRange(FindPrefabPositions("piece_groundtorch_wood", origin, searchRadius)); list.AddRange(FindPrefabPositions("piece_groundtorch_green", origin, searchRadius)); list.AddRange(FindPrefabPositions("piece_groundtorch_blue", origin, searchRadius)); Vector3? val = null; float num = float.MaxValue; for (int i = 0; i < list.Count; i++) { Vector3 val2 = list[i]; Vector3 val3 = val2 - origin; val3.y = 0f; float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude < 0.05f || magnitude > searchRadius) { continue; } float num2 = Vector3.Angle(lookDirection, val3); if (!(num2 > maxAngleDegrees)) { float num3 = ((i < count) ? 0f : 1.5f); float num4 = num2 * 1.5f + magnitude + num3; if (!(num4 >= num)) { num = num4; val = val2; prefabHint = ((i < count) ? "Standing Iron Torch" : "torch"); } } } if (!val.HasValue) { return false; } position = val.Value; return true; } public static RaceGate DetectGraustenGate(Vector3 origin, float searchRadius) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) List list = new List(); list.AddRange(FindPrefabPositions("Piece_grausten_pillar_arch", origin, searchRadius)); list.AddRange(FindPrefabPositions("piece_grausten_pillar_arch", origin, searchRadius)); list.AddRange(FindPrefabPositions("Piece_grausten_pillarbeam_medium", origin, searchRadius)); list.AddRange(FindPrefabPositions("piece_grausten_pillarbeam_medium", origin, searchRadius)); if (list.Count < 2) { return null; } Vector3 val = list[0]; Vector3 val2 = list[1]; float num = 0f; for (int i = 0; i < list.Count; i++) { for (int j = i + 1; j < list.Count; j++) { float num2 = Vector3.Distance(list[i], list[j]); if (num2 > num) { num = num2; val = list[i]; val2 = list[j]; } } } Vector3 up = Vector3.up; Vector3 val3 = val2 - val; Vector3 forward = Vector3.Cross(up, ((Vector3)(ref val3)).normalized); return new RaceGate(val, val2, forward); } public static List FindPrefabPositions(string prefabName, Vector3 origin, float radius) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if ((Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null) { return list; } int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName); float num = radius * radius; foreach (KeyValuePair item in ZDOMan.instance.m_objectsByID) { ZDO value = item.Value; if (value != null && value.IsValid() && value.GetPrefab() == stableHashCode) { Vector3 position = value.GetPosition(); Vector3 val = position - origin; if (((Vector3)(ref val)).sqrMagnitude <= num) { list.Add(position); } } } return list; } } public static class TimeFormatter { public static string FormatDuration(double totalSeconds) { if (totalSeconds < 0.0) { totalSeconds = 0.0; } long num = (long)Math.Round(totalSeconds * 1000.0); long num2 = num / 60000; long num3 = num % 60000 / 1000; long num4 = num % 1000 / 100; if (num2 >= 60) { long num5 = num2 / 60; num2 %= 60; return string.Format(CultureInfo.InvariantCulture, "{0}:{1:00}:{2:00}.{3}", num5, num2, num3, num4); } return string.Format(CultureInfo.InvariantCulture, "{0}:{1:00}.{2}", num2, num3, num4); } public static string FormatDurationMs(long milliseconds) { return FormatDuration((double)milliseconds / 1000.0); } public static string FormatSignedDeltaMs(long deltaMs) { return ((deltaMs > 0) ? "+" : ((deltaMs < 0) ? "-" : "")) + FormatDurationMs(Math.Abs(deltaMs)); } public static string FormatOrdinal(int place) { if (place <= 0) { return "-"; } int num = place % 10; if (place % 100 / 10 == 1) { return place + "th"; } return num switch { 1 => place + "st", 2 => place + "nd", 3 => place + "rd", _ => place + "th", }; } } public readonly struct CommandParticipant { public long PlayerId { get; } public string PlayerName { get; } public Player Player { get; } public bool IsValid { get { if (PlayerId != 0L) { return !string.IsNullOrWhiteSpace(PlayerName); } return false; } } public CommandParticipant(long playerId, string playerName, Player player) { PlayerId = playerId; PlayerName = playerName; Player = player; } } public static class ValheimUtil { public static bool IsServerAuthority() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 if ((Object)(object)ZNet.instance != (Object)null) { if (!ZNet.instance.IsServer()) { return (int)SystemInfo.graphicsDeviceType == 4; } return true; } return false; } public static Player FindPlayerById(long playerId) { foreach (Player allPlayer in Player.GetAllPlayers()) { if (allPlayer.GetPlayerID() == playerId) { return allPlayer; } } return null; } public static Player FindPlayerFromPeer(ZNetPeer peer) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) if (peer == null) { return null; } Player val = FindPlayerById(peer.m_uid); if ((Object)(object)val != (Object)null) { return val; } if ((Object)(object)ZNet.instance != (Object)null) { foreach (Player allPlayer in Player.GetAllPlayers()) { ZNetPeer peer2 = ZNet.instance.GetPeer(allPlayer.GetPlayerID()); if (peer2 != null && (peer2.m_uid == peer.m_uid || peer2.m_characterID == peer.m_characterID)) { return allPlayer; } } } foreach (Player allPlayer2 in Player.GetAllPlayers()) { ZNetView val2 = ((Character)(allPlayer2?)).m_nview; if (!((Object)(object)val2 == (Object)null) && val2.IsValid()) { ZDO zDO = val2.GetZDO(); if (zDO != null && zDO.m_uid == peer.m_characterID) { return allPlayer2; } } } if ((Object)(object)ZNet.instance != (Object)null) { foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { if (!(player.m_characterID != peer.m_characterID)) { Player val3 = FindPlayerByName(player.m_name); if ((Object)(object)val3 != (Object)null) { return val3; } } } } if (!string.IsNullOrEmpty(peer.m_playerName)) { return FindPlayerByName(peer.m_playerName); } return null; } public static bool IsClientLocalPlayerCommand(string argLine) { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { return false; } if (string.IsNullOrWhiteSpace(argLine)) { return false; } switch (argLine.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant()) { case "gearcheck": case "gear": case "loadout": return true; default: return false; } } public static RaceGate CreateRegistrationGate(Player player, ZNetPeer peer, float width) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)null) { return RaceGate.FromPlayerPosition(player, width); } if (CommandContext.TryGetOrigin(out var position, out var forward)) { return RaceGate.FromPositionAndForward(position, forward, width); } if (peer != null) { return RaceGate.FromPeerPosition(peer, width); } return null; } public static Player FindPlayerByName(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } foreach (Player allPlayer in Player.GetAllPlayers()) { if (string.Equals(allPlayer.GetPlayerName(), name, StringComparison.OrdinalIgnoreCase)) { return allPlayer; } } return null; } public static bool TryResolveCommandParticipant(out CommandParticipant participant) { ZNetPeer val = ResolveCommandPeer(); if (val != null) { participant = new CommandParticipant(val.m_uid, val.m_playerName, FindPlayerFromPeer(val)); return participant.IsValid; } if ((Object)(object)Player.m_localPlayer != (Object)null) { participant = new CommandParticipant(Player.m_localPlayer.GetPlayerID(), Player.m_localPlayer.GetPlayerName(), Player.m_localPlayer); return participant.IsValid; } participant = default(CommandParticipant); return false; } public static Player ResolvePlayerForGearCheck() { if (TryResolveCommandParticipant(out var participant) && (Object)(object)participant.Player != (Object)null) { return participant.Player; } if (TryResolveCommandParticipant(out participant)) { Player val = FindPlayerById(participant.PlayerId); if ((Object)(object)val != (Object)null) { return val; } Player val2 = FindPlayerByName(participant.PlayerName); if ((Object)(object)val2 != (Object)null) { return val2; } } return Player.m_localPlayer; } public static bool IsAdmin(ZRpc rpc) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } if ((Object)(object)Player.m_localPlayer != (Object)null && !CommandContext.ExecutingFromRemote && ZNet.instance.LocalPlayerIsAdminOrHost()) { long playerID = Player.m_localPlayer.GetPlayerID(); if (CommandContext.SenderPeerId == 0L || CommandContext.SenderPeerId == playerID) { return true; } } ZNetPeer val = ResolveCommandPeer(rpc); if (val != null && IsAdminPeer(val)) { return true; } ISocket val2 = ((rpc != null) ? rpc.GetSocket() : null) ?? val?.m_socket; if (val2 != null) { if (val2.IsHost()) { return true; } if (IsAdminHostName(val2.GetHostName())) { return true; } } if (rpc == null && CommandContext.SenderPeerId == 0L && (Object)(object)Player.m_localPlayer == (Object)null) { return true; } string text = DescribePeerIdentity(val, val2); RunningManPlugin.Log.LogWarning((object)("RunningMan admin check failed for " + text + ".")); return false; } public static ZNetPeer ResolveCommandPeer(ZRpc rpc = null) { if ((Object)(object)ZNet.instance == (Object)null) { return null; } if (rpc == null) { rpc = CommandContext.CurrentRpc; } if (rpc != null) { ZNetPeer peer = ZNet.instance.GetPeer(rpc); if (peer != null) { return peer; } } if (CommandContext.SenderPeerId != 0L) { return ZNet.instance.GetPeer(CommandContext.SenderPeerId); } return null; } public static bool IsAdminPeer(ZNetPeer peer) { if (peer?.m_socket == null || (Object)(object)ZNet.instance == (Object)null) { return false; } if (IsAdminHostName(peer.m_socket.GetHostName())) { return true; } if (IsIdOnAdminList(GetPlatformUserIdString(peer))) { return true; } return false; } public static bool IsConnectedPeerAdmin(ZNetPeer peer) { if (peer == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } if (IsAdminPeer(peer)) { return true; } if (peer.m_socket != null) { return peer.m_socket.IsHost(); } return false; } private static bool IsIdOnAdminList(string id) { id = NormalizePlatformUserId(id); if (string.IsNullOrEmpty(id) || (Object)(object)ZNet.instance == (Object)null) { return false; } List adminList = ZNet.instance.GetAdminList(); if (adminList != null) { foreach (string item in adminList) { if (string.Equals(item, id, StringComparison.OrdinalIgnoreCase)) { return true; } } } if (ZNet.instance.IsAdmin(id)) { return true; } if (id.IndexOf("_", StringComparison.Ordinal) < 0 && ZNet.instance.IsAdmin("Steam_" + id)) { return true; } int num = id.IndexOf("_", StringComparison.Ordinal); if (num > 0 && ZNet.instance.IsAdmin(id.Substring(num + 1))) { return true; } return false; } public unsafe static string GetPlatformUserIdString(ZNetPeer peer) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (peer == null || (Object)(object)ZNet.instance == (Object)null) { return null; } foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { if (player.m_characterID == peer.m_characterID) { CrossNetworkUserInfo userInfo = player.m_userInfo; return NormalizePlatformUserId(((object)(*(CrossNetworkUserInfo*)(&userInfo))/*cast due to .constrained prefix*/).ToString()); } } return null; } public static string NormalizePlatformUserId(string platformUserId) { if (string.IsNullOrEmpty(platformUserId)) { return platformUserId; } int num = platformUserId.LastIndexOf('('); int num2 = platformUserId.LastIndexOf(')'); if (num >= 0 && num2 > num) { return platformUserId.Substring(num + 1, num2 - num - 1).Trim(); } return platformUserId.Trim(); } public static string FormatAdminIdentity(ZNetPeer peer, ZRpc rpc) { ISocket socket = peer?.m_socket ?? ((rpc != null) ? rpc.GetSocket() : null); string platformUserId = ((peer != null) ? GetPlatformUserIdString(peer) : null); return DescribePeerIdentity(peer, socket, platformUserId); } private static bool IsAdminHostName(string hostName) { if (string.IsNullOrEmpty(hostName) || (Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsAdmin(hostName)) { return true; } if (hostName.IndexOf("_", StringComparison.Ordinal) < 0 && ZNet.instance.IsAdmin("Steam_" + hostName)) { return true; } return false; } private static string DescribePeerIdentity(ZNetPeer peer, ISocket socket, string platformUserId = null) { if (platformUserId == null) { platformUserId = ((peer != null) ? GetPlatformUserIdString(peer) : null); } object obj; if (peer == null) { obj = null; } else { ISocket socket2 = peer.m_socket; obj = ((socket2 != null) ? socket2.GetHostName() : null); } if (obj == null) { obj = ((socket != null) ? socket.GetHostName() : null) ?? "(unknown)"; } string text = (string)obj; string text2 = platformUserId ?? "(no platform id)"; return "hostName=" + text + ", platformId=" + text2; } public static List BuildAdminPlayerIds() { List list = new List(); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return list; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer?.m_rpc == null || !IsAdminPeer(peer)) { continue; } if (peer.m_uid != 0L && !list.Contains(peer.m_uid)) { list.Add(peer.m_uid); } Player val = FindPlayerFromPeer(peer); if ((Object)(object)val != (Object)null) { long playerID = val.GetPlayerID(); if (playerID != 0L && !list.Contains(playerID)) { list.Add(playerID); } } } return list; } public static bool TryGetParticipantPosition(long playerId, out Vector3 position) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) Player val = FindPlayerById(playerId); if ((Object)(object)val != (Object)null) { position = ((Component)val).transform.position; return true; } if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(playerId); if (peer != null && TryGetPeerWorldPosition(peer, out position)) { return true; } foreach (ZNetPeer peer2 in ZNet.instance.GetPeers()) { if (peer2 != null && peer2.m_uid == playerId && TryGetPeerWorldPosition(peer2, out position)) { return true; } } } position = Vector3.zero; return false; } public static bool TryGetPeerWorldPosition(ZNetPeer peer, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if (peer == null) { return false; } Player val = FindPlayerFromPeer(peer); if ((Object)(object)val != (Object)null) { position = ((Component)val).transform.position; return true; } if (TryGetCharacterZdoPosition(peer.m_characterID, out position)) { return true; } position = peer.m_refPos; return true; } public static bool TryGetCharacterZdoPosition(ZDOID characterId, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if (ZDOMan.instance == null || ((ZDOID)(ref characterId)).IsNone()) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(characterId); if (zDO == null || !zDO.IsValid()) { return false; } position = zDO.GetPosition(); return true; } public static void Reply(Terminal context, string message) { if (!string.IsNullOrEmpty(message)) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && CommandContext.SenderPeerId != 0L) { CommandContext.QueueReply(message); } ZRpc val = CommandContext.CurrentRpc; if (val == null && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && CommandContext.SenderPeerId != 0L) { val = ZNet.instance.GetPeer(CommandContext.SenderPeerId)?.m_rpc; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && val != null) { ZNet.instance.RemotePrint(val, message); } if ((Object)(object)context != (Object)null) { context.AddString(message); } else if ((Object)(object)Console.instance != (Object)null) { Console.instance.Print(message); } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { RaceGuiLog.Add(message); } } } public static void Broadcast(string message) { if (ModConfig.EnableBroadcasts.Value && !string.IsNullOrEmpty(message)) { if ((Object)(object)Chat.instance != (Object)null) { Chat.instance.SendText((Type)2, message); } else { RunningManPlugin.Log.LogInfo((object)message); } } } public static void Announce(string message) { if (!string.IsNullOrEmpty(message)) { Broadcast(message); RaceNetSync.SendYellowHud(message); } } public static void ReplyHud(string message) { if (!string.IsNullOrEmpty(message)) { string message2 = message.Replace("\r\n", "\n").Replace("\n", " · "); if (CommandContext.SenderPeerId != 0L) { RaceNetSync.SendYellowHudToPlayer(CommandContext.SenderPeerId, message2); } else { RaceGui.ShowYellowHud(message2); } } } public static bool IsLocalPlayerAdmin() { if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (ZNet.instance.IsServer() && (Object)(object)Player.m_localPlayer == (Object)null) { return true; } if (ZNet.instance.LocalPlayerIsAdminOrHost()) { return true; } if (RaceNetSync.ClientIsAdmin) { return true; } return IsLocalPlayerInSyncedAdminList(); } public static bool IsLocalPlayerInSyncedAdminList() { List list = RaceNetSync.ClientState?.AdminPlayerIds; if (list == null || list.Count == 0) { return false; } foreach (long item in EnumerateLocalAdminLookupIds()) { if (item != 0L && list.Contains(item)) { return true; } } return false; } private static IEnumerable EnumerateLocalAdminLookupIds() { if ((Object)(object)Player.m_localPlayer != (Object)null) { long playerId = Player.m_localPlayer.GetPlayerID(); if (playerId != 0L) { yield return playerId; } if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(playerId); if (peer != null && peer.m_uid != 0L && peer.m_uid != playerId) { yield return peer.m_uid; } } } if ((Object)(object)ZNet.instance == (Object)null) { yield break; } string localName = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : null); if (string.IsNullOrWhiteSpace(localName)) { yield break; } foreach (ZNetPeer peer2 in ZNet.instance.GetPeers()) { if (peer2 != null && peer2.m_uid != 0L && string.Equals(peer2.m_playerName, localName, StringComparison.OrdinalIgnoreCase)) { yield return peer2.m_uid; } } } public static void RunCommand(string args) { //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) CommandContext.ClearOrigin(); CaptureClientOrigin(args); if (IsClientLocalPlayerCommand(args)) { Commands.Execute(args, null); return; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { ZRpc rpc = null; long senderPeerId = 0L; if ((Object)(object)Player.m_localPlayer != (Object)null) { long playerID = Player.m_localPlayer.GetPlayerID(); ZNetPeer val = ZNet.instance.GetPeer(playerID); if (val == null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if ((Object)(object)FindPlayerFromPeer(peer) == (Object)(object)Player.m_localPlayer) { val = peer; break; } } } rpc = val?.m_rpc; senderPeerId = val?.m_uid ?? playerID; } CommandContext.SenderPeerId = senderPeerId; try { Commands.Execute(args, rpc); return; } finally { RaceNetSync.SyncToCommandSender(); RaceNetSync.FlushCommandReplies(CommandContext.SenderPeerId); CommandContext.Clear(); } } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { RaceNetSync.SendRunCommand(args, CommandContext.HasOrigin ? new Vector3?(CommandContext.OriginPosition) : ((Vector3?)null), CommandContext.HasOrigin ? new Vector3?(CommandContext.OriginForward) : ((Vector3?)null)); } else { Commands.Execute(args, null); } } private static void CaptureClientOrigin(string args) { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || string.IsNullOrWhiteSpace(args)) { return; } string[] array = args.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return; } string text = array[0].ToLowerInvariant(); if (text == null) { return; } Sign sign; Vector3 position; switch (text.Length) { default: return; case 8: { char c = text[3]; if ((uint)c <= 105u) { if (c != 'e') { if (c != 'i' || !(text == "register")) { return; } break; } if (!(text == "rulesign")) { return; } } else { if (c != 'l') { if (c != 'p' || !(text == "setpoint")) { return; } break; } if (!(text == "bulletin")) { return; } } goto IL_010a; } case 10: { char c = text[0]; if (c != 'a') { if (c != 'r' || !(text == "rulesboard")) { return; } goto IL_010a; } if (!(text == "autodetect")) { return; } break; } case 7: if (!(text == "wrboard")) { return; } goto IL_010a; case 9: return; IL_010a: if (RaceWrBoards.TryGetLookedAtSign(out sign, out position)) { Vector3 forward = ((Component)Player.m_localPlayer).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { forward = Vector3.forward; } else { ((Vector3)(ref forward)).Normalize(); } CommandContext.SetOrigin(position, forward); } return; } ? position2 = ((??)RaceGateEditor.PendingPlacePosition) ?? ((Component)Player.m_localPlayer).transform.position; RaceGateEditor.PendingPlacePosition = null; Vector3 forward2 = ((Component)Player.m_localPlayer).transform.forward; forward2.y = 0f; if (((Vector3)(ref forward2)).sqrMagnitude < 0.0001f) { forward2 = Vector3.forward; } else { ((Vector3)(ref forward2)).Normalize(); } CommandContext.SetOrigin((Vector3)position2, forward2); } public static Vector3Data ToData(Vector3 vector) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new Vector3Data(vector.x, vector.y, vector.z); } public static Vector3 FromData(Vector3Data data) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(data.X, data.Y, data.Z); } } [Serializable] public sealed class Vector3Data { public float X; public float Y; public float Z; public Vector3Data() { } public Vector3Data(float x, float y, float z) { X = x; Y = y; Z = z; } } } namespace RunningMan.Patches { [HarmonyPatch(typeof(Chat), "SendInput")] public static class ChatSendInputPatch { private static FieldInfo _inputField; private static bool Prefix(Chat __instance) { string inputText = GetInputText(__instance); if (string.IsNullOrEmpty(inputText)) { return true; } if (!inputText.StartsWith("/run", StringComparison.OrdinalIgnoreCase)) { return true; } string args = ((inputText.Length > 4) ? inputText.Substring(4).Trim() : string.Empty); ClearInput(__instance); ValheimUtil.RunCommand(args); return false; } private static string GetInputText(Chat chat) { if ((object)_inputField == null) { _inputField = AccessTools.Field(typeof(Chat), "m_input"); } if (_inputField?.GetValue(chat) == null) { return null; } object value = _inputField.GetValue(chat); return value.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(value) as string; } private static void ClearInput(Chat chat) { object obj = _inputField?.GetValue(chat); obj?.GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.SetValue(obj, string.Empty); } } [HarmonyPatch(typeof(Game), "Start")] public static class GameStartPatch { private static void Postfix() { Commands.Register(); RaceNetSync.Register(); } } [HarmonyPatch(typeof(Humanoid), "UseItem")] public static class HumanoidUseItemPatch { private static void Prefix(Humanoid __instance, ItemData item, bool fromInventoryGui) { if (!ModConfig.EnableGearCheck.Value || (Object)(object)__instance == (Object)null || item == null) { return; } Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val == null || !GearValidator.IsConsumableItem(item) || GearValidator.IsAllowedRaceConsumable(item)) { return; } string text = GearValidator.GetPrefabName(item); if (string.IsNullOrEmpty(text)) { text = (GearValidator.IsFoodItem(item) ? "food" : "consumable"); } string text2 = (GearValidator.IsFoodItem(item) ? ("illegal food: " + text) : ("illegal consumable: " + text)); if (ValheimUtil.IsServerAuthority() && (Object)(object)RaceManager.Instance != (Object)null) { RaceManager.Instance.TryFlagIllegalConsumable(val, item); } else { if ((Object)(object)val != (Object)(object)Player.m_localPlayer) { return; } RaceStateSnapshot clientState = RaceNetSync.ClientState; if (clientState != null && clientState.Phase == 3) { RunnerSnapshot localRunner = RaceNetSync.GetLocalRunner(); if (localRunner != null && localRunner.StartUtcTicks > 0 && !localRunner.Finished && !localRunner.Disqualified) { localRunner.Disqualified = true; localRunner.DisqualifiedReason = text2; RaceGui.ShowYellowHud("DISQUALIFIED: " + text2); RaceGui.ShowInfoPanel("Disqualified", text2, 12f); RaceNetSync.SendGearViolation(text2); } } } } } [HarmonyPatch(typeof(Player), "TakeInput")] public static class PlayerTakeInputPatch { private static void Postfix(ref bool __result) { if (RaceGui.IsOpen) { __result = false; } } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] public static class PlayerControllerTakeInputPatch { private static void Postfix(ref bool __result) { if (RaceGui.IsOpen) { __result = false; } } } [HarmonyPatch] public static class UnityScrollAxisBlockPatch { private static IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(Input), "GetAxis", new Type[1] { typeof(string) }, (Type[])null); yield return AccessTools.Method(typeof(Input), "GetAxisRaw", new Type[1] { typeof(string) }, (Type[])null); } private static void Postfix(string axisName, ref float __result) { if (RaceGui.IsOpen && !string.IsNullOrEmpty(axisName) && axisName.IndexOf("Scroll", StringComparison.OrdinalIgnoreCase) >= 0) { __result = 0f; } } } [HarmonyPatch] public static class ZInputScrollBlockPatch { private static bool Prepare() { return AccessTools.TypeByName("ZInput") != null; } private static IEnumerable TargetMethods() { Type type = AccessTools.TypeByName("ZInput"); if (type == null) { yield break; } foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { if (!(declaredMethod.ReturnType != typeof(float)) && (declaredMethod.Name.IndexOf("Scroll", StringComparison.OrdinalIgnoreCase) >= 0 || declaredMethod.Name == "GetAxis" || declaredMethod.Name == "GetAxisRaw")) { yield return declaredMethod; } } } private static void Postfix(MethodBase __originalMethod, object[] __args, ref float __result) { if (RaceGui.IsOpen && !(__originalMethod == null)) { if (__originalMethod.Name.IndexOf("Scroll", StringComparison.OrdinalIgnoreCase) >= 0) { __result = 0f; } else if ((__originalMethod.Name == "GetAxis" || __originalMethod.Name == "GetAxisRaw") && __args != null && __args.Length != 0 && __args[0] is string text && text.IndexOf("Scroll", StringComparison.OrdinalIgnoreCase) >= 0) { __result = 0f; } } } } [HarmonyPatch] public static class GameCameraScrollBlockPatch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("GameCamera"); if (!(type == null)) { return AccessTools.Method(type, "UpdateCamera", (Type[])null, (Type[])null); } return null; } private static bool Prepare() { return TargetMethod() != null; } private static void Prefix() { if (RaceGui.IsOpen) { Input.ResetInputAxes(); } } } [HarmonyPatch(typeof(Sign), "UpdateText")] public static class SignUpdateTextWrBoardPatch { private static void Postfix(Sign __instance) { if (!((Object)(object)__instance == (Object)null) && RaceWrBoards.IsMarkedSign(__instance)) { RaceWrBoards.ApplySignText(__instance); } } } [HarmonyPatch] public static class SignInteractWrBoardPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Sign), "Interact", (Type[])null, (Type[])null); } private static bool Prefix(Sign __instance, ref bool __result) { if ((Object)(object)__instance == (Object)null || !RaceWrBoards.IsMarkedSign(__instance)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Sign), "GetHoverText")] public static class SignHoverTextWrBoardPatch { private static void Postfix(Sign __instance, ref string __result) { if (!((Object)(object)__instance == (Object)null) && RaceWrBoards.TryGetBulletinKind(__instance, out var kind)) { __result = RaceWrBoards.GetHoverLabel(kind); } } } [HarmonyPatch(typeof(Sign), "SetText")] public static class SignSetTextWrBoardPatch { private static bool Prefix(Sign __instance) { if (!((Object)(object)__instance == (Object)null)) { return !RaceWrBoards.IsMarkedSign(__instance); } return true; } } [HarmonyPatch(typeof(Skills), "GetSkillLevel")] public static class SkillsGetSkillLevelPatch { private static void Postfix(SkillType skillType, ref float __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)skillType == 102 && RaceSkillNormalizer.IsActive) { __result = RaceSkillNormalizer.TargetLevel; } } } [HarmonyPatch(typeof(Skills), "GetSkillFactor")] public static class SkillsGetSkillFactorPatch { private static void Postfix(Skills __instance, SkillType skillType, ref float __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)skillType == 102 && RaceSkillNormalizer.IsActive && !((Object)(object)__instance == (Object)null)) { __result = RaceSkillNormalizer.TargetLevel / 100f; } } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] public static class SkillsRaiseSkillPatch { private static void Prefix(Skills __instance, SkillType skillType, ref float factor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)skillType == 102 && !(factor <= 0f) && (Object)(object)__instance?.m_player != (Object)null && RaceSkillNormalizer.ShouldBlockRunSkillXp(__instance.m_player)) { factor = 0f; } } } [HarmonyPatch(typeof(Terminal), "Awake")] public static class TerminalAwakePatch { private static void Postfix() { Commands.Register(); } } [HarmonyPatch(typeof(ConsoleCommand), "RunAction")] public static class TerminalConsoleCommandPatch { private static void Prefix(ConsoleCommand __instance) { if (CommandContext.CurrentRpc == null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && __instance.IsNetwork && !((Object)(object)Player.m_localPlayer == (Object)null)) { ZNetPeer peer = ZNet.instance.GetPeer(Player.m_localPlayer.GetPlayerID()); if (peer?.m_rpc != null) { CommandContext.CurrentRpc = peer.m_rpc; CommandContext.SenderPeerId = peer.m_uid; } } } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] public static class TerminalPatch { private static void Postfix() { Commands.Register(); } } [HarmonyPatch(typeof(ZNet), "InternalCommand")] public static class ZNetInternalCommandPatch { private static void Prefix(ZRpc rpc) { CommandContext.CurrentRpc = rpc; CommandContext.SenderPeerId = 0L; if (rpc != null && (Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(rpc); if (peer != null) { CommandContext.SenderPeerId = peer.m_uid; } } } private static void Postfix() { CommandContext.Clear(); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class ZNetPeerInfoPatch { private static void Postfix(ZRpc rpc) { if (ValheimUtil.IsServerAuthority() && !((Object)(object)RaceManager.Instance == (Object)null) && rpc != null) { ZNet instance = ZNet.instance; long valueOrDefault = ((instance == null) ? ((long?)null) : instance.GetPeer(rpc)?.m_uid).GetValueOrDefault(); if (valueOrDefault != 0L) { RaceNetSync.SendTrackToPeer(valueOrDefault, JsonStorage.Track); RaceNetSync.SendBulletinsToPeer(valueOrDefault); RaceNetSync.SendStateToPeer(valueOrDefault, RaceManager.Instance.BuildClientSnapshot(DateTime.UtcNow)); RaceNetSync.SendAdminStatusToPeer(valueOrDefault); } } } } } namespace RunningMan.Net { public static class RaceNetSync { public const string RpcRaceState = "RunningMan_RaceState"; public const string RpcTrack = "RunningMan_Track"; public const string RpcRunCommand = "RunningMan_RunCommand"; public const string RpcCommandReply = "RunningMan_CommandReply"; public const string RpcCountdownStart = "RunningMan_CountdownStart"; public const string RpcRaceCue = "RunningMan_RaceCue"; public const string RpcCenterMessage = "RunningMan_CenterMessage"; public const string RpcGearViolation = "RunningMan_GearViolation"; public const string RpcRunnerPosition = "RunningMan_RunnerPos"; public const string RpcWrBoards = "RunningMan_WrBoards"; public const string RpcAdminStatus = "RunningMan_AdminStatus"; public const byte CueCheckpoint = 1; public const byte CueFinishFirst = 2; public const byte CueFalseStart = 3; public static RaceStateSnapshot ClientState { get; private set; } = new RaceStateSnapshot(); public static TrackConfig ClientTrack { get; private set; } = new TrackConfig(); public static AllowedGearRules ClientAllowedGear { get; private set; } = AllowedGearRules.CreateDefaults(); public static List ClientWrBoards { get; private set; } = new List(); public static List ClientRulesBoards { get; private set; } = new List(); public static bool ClientIsAdmin { get; private set; } public static DateTime? LocalCountdownEndUtc { get; private set; } public static event Action StateUpdated; public static event Action TrackUpdated; public static bool IsDebugModeActive() { RaceStateSnapshot clientState = ClientState; if (clientState != null && clientState.DebugMode) { return true; } if (ValheimUtil.IsServerAuthority()) { return ModConfig.DebugMode.Value; } return false; } public static int GetTotalCheckpoints() { int num = ClientState?.TotalCheckpoints ?? 0; if (num > 0) { return num; } return ClientTrack?.CheckpointCount ?? 0; } public static void ApplyOptimisticDebug(bool enabled) { EnsureClientState(); ClientState.DebugMode = enabled; RaceNetSync.StateUpdated?.Invoke(); } public static void SetLocalCountdown(DateTime endUtc) { LocalCountdownEndUtc = endUtc; RaceNetSync.StateUpdated?.Invoke(); } public static void ClearLocalCountdown() { if (LocalCountdownEndUtc.HasValue) { LocalCountdownEndUtc = null; RaceNetSync.StateUpdated?.Invoke(); } } public static bool IsCountdownActive() { if (LocalCountdownEndUtc.HasValue) { return DateTime.UtcNow < LocalCountdownEndUtc.Value; } return false; } public static int GetCountdownRemainingSeconds() { if (!LocalCountdownEndUtc.HasValue) { return 0; } return Math.Max(0, (int)Math.Ceiling((LocalCountdownEndUtc.Value - DateTime.UtcNow).TotalSeconds)); } public static void ApplyOptimisticCancel() { EnsureClientState(); ClientState.Phase = 0; ClientState.RegistrationOpen = false; ClientState.Registered.Clear(); ClientState.Runners.Clear(); ClientState.CountdownEndUtcTicks = 0L; ClearLocalCountdown(); } public static bool CanJoinRace() { RaceStateSnapshot clientState = ClientState; if (clientState == null || !clientState.RegistrationOpen) { return false; } RaceEventPhase phase = (RaceEventPhase)clientState.Phase; if (phase == RaceEventPhase.Racing || phase == RaceEventPhase.Countdown) { return false; } return !IsLocalRegistered(); } public static void ApplyOptimisticRegistrationOpened() { EnsureClientState(); ClientState.RegistrationOpen = true; ClientState.Phase = 1; RaceNetSync.StateUpdated?.Invoke(); } public static void ApplyOptimisticRegistrationClosed() { EnsureClientState(); ClientState.RegistrationOpen = false; if (ClientState.Phase == 1) { ClientState.Phase = ((ClientState.Registered != null && ClientState.Registered.Count > 0) ? 4 : 0); } RaceNetSync.StateUpdated?.Invoke(); } public static TrackConfig GetActiveTrack() { if (ValheimUtil.IsServerAuthority()) { return JsonStorage.Track; } return ClientTrack ?? new TrackConfig(); } public static void SyncToCommandSender() { if (ValheimUtil.IsServerAuthority() && !((Object)(object)RaceManager.Instance == (Object)null)) { long senderPeerId = CommandContext.SenderPeerId; if (senderPeerId == 0L) { RaceManager.Instance.BroadcastState(); RaceManager.Instance.SyncTrack(); SendBulletins(); BroadcastAdminStatus(); } else { SendStateToPeer(senderPeerId, RaceManager.Instance.BuildClientSnapshot(DateTime.UtcNow)); SendTrackToPeer(senderPeerId, JsonStorage.Track); SendBulletinsToPeer(senderPeerId); SendAdminStatusToPeer(senderPeerId); } } } public static void ApplyOptimisticTrackCommand(string[] parts) { if (parts == null || parts.Length == 0 || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())) { return; } EnsureClientTrack(); string text = parts[0].ToLowerInvariant(); if (!(text == "clear")) { if (text == "remove") { ApplyOptimisticRemove(parts); } } else { ApplyOptimisticClear(parts); } RaceNetSync.TrackUpdated?.Invoke(); if (ClientState != null) { ClientState.TotalCheckpoints = ClientTrack.CheckpointCount; } RaceNetSync.StateUpdated?.Invoke(); } private static void EnsureClientTrack() { if (ClientTrack == null) { ClientTrack = new TrackConfig(); } if (ClientTrack.Checkpoints == null) { ClientTrack.Checkpoints = new List(); } } private static void ApplyOptimisticClear(string[] parts) { if (parts.Length >= 2) { switch (parts[1].ToLowerInvariant()) { case "start": ClientTrack.StartGate = new RaceGate(); break; case "finish": ClientTrack.FinishGate = new RaceGate(); break; case "checkpoint": case "checkpoints": ClientTrack.Checkpoints.Clear(); break; case "track": ClientTrack.StartGate = new RaceGate(); ClientTrack.FinishGate = new RaceGate(); ClientTrack.Checkpoints.Clear(); break; } } } private static void ApplyOptimisticRemove(string[] parts) { if (parts.Length < 2) { return; } switch (parts[1].ToLowerInvariant()) { case "start": ClientTrack.StartGate = new RaceGate(); break; case "finish": ClientTrack.FinishGate = new RaceGate(); break; case "checkpoint": { int index; if (parts.Length >= 3 && parts[2].Equals("last", StringComparison.OrdinalIgnoreCase) && ClientTrack.Checkpoints.Count > 0) { ClientTrack.Checkpoints.RemoveAt(ClientTrack.Checkpoints.Count - 1); } else if (parts.Length >= 3 && int.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out index)) { ClientTrack.Checkpoints.RemoveAll((Checkpoint item) => item.Index == index); } break; } } } private static bool ShouldReceiveClientSync() { if (ValheimUtil.IsServerAuthority()) { return (Object)(object)Player.m_localPlayer != (Object)null; } return true; } private static bool IsLocalPeer(long peerId) { if (peerId == 0L || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return false; } long playerID = Player.m_localPlayer.GetPlayerID(); if (peerId == playerID) { return true; } ZNetPeer peer = ZNet.instance.GetPeer(playerID); if (peer != null) { return peer.m_uid == peerId; } return false; } public static void RequestStateRefresh() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { RaceManager.Instance?.BroadcastState(); RaceManager.Instance?.SyncTrack(); } else { SendRunCommand("sync"); } } private static void EnsureClientState() { if (ClientState == null) { ClientState = new RaceStateSnapshot(); } } public static void Register() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("RunningMan_RaceState", (Action)ReceiveRaceState); ZRoutedRpc.instance.Register("RunningMan_Track", (Action)ReceiveTrack); ZRoutedRpc.instance.Register("RunningMan_WrBoards", (Action)ReceiveWrBoards); ZRoutedRpc.instance.Register("RunningMan_RunCommand", (Action)ReceiveRunCommand); ZRoutedRpc.instance.Register("RunningMan_CommandReply", (Action)ReceiveCommandReply); ZRoutedRpc.instance.Register("RunningMan_CountdownStart", (Action)ReceiveCountdownStart); ZRoutedRpc.instance.Register("RunningMan_RaceCue", (Action)ReceiveRaceCue); ZRoutedRpc.instance.Register("RunningMan_CenterMessage", (Action)ReceiveCenterMessage); ZRoutedRpc.instance.Register("RunningMan_GearViolation", (Action)ReceiveGearViolation); ZRoutedRpc.instance.Register("RunningMan_RunnerPos", (Action)ReceiveRunnerPosition); ZRoutedRpc.instance.Register("RunningMan_AdminStatus", (Action)ReceiveAdminStatus); } } public static void SendRunnerPosition(long playerId, Vector3 position) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance != null) { if (ValheimUtil.IsServerAuthority()) { RaceManager.Instance?.ReportRunnerPosition(playerId, position); return; } ZPackage val = new ZPackage(); val.Write(playerId); val.Write(position.x); val.Write(position.y); val.Write(position.z); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "RunningMan_RunnerPos", new object[1] { val }); } } private static void ReceiveRunnerPosition(long sender, ZPackage package) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (!ValheimUtil.IsServerAuthority()) { return; } try { long num = package.ReadLong(); Vector3 position = default(Vector3); ((Vector3)(ref position))..ctor(package.ReadSingle(), package.ReadSingle(), package.ReadSingle()); RaceManager.Instance?.ReportRunnerPosition(sender, position); if (num != 0L && num != sender) { RaceManager.Instance?.ReportRunnerPosition(num, position); } } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read runner position: " + ex.Message)); } } public static void SendYellowHud(string message) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown RaceGui.ShowYellowHud(message); if (ValheimUtil.IsServerAuthority() && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(1); val.Write(string.Empty); val.Write(message ?? string.Empty); val.Write(6f); ZRoutedRpc.instance.InvokeRoutedRPC("RunningMan_CenterMessage", new object[2] { ZRoutedRpc.Everybody, val }); } } public static void SendYellowHudToPlayer(long playerId, string message) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown if (string.IsNullOrEmpty(message)) { return; } if (IsLocalPlayerTarget(playerId)) { RaceGui.ShowYellowHud(message); } else if (ValheimUtil.IsServerAuthority() && ZRoutedRpc.instance != null) { long num = ResolvePeerId(playerId); if (num != 0L) { ZPackage val = new ZPackage(); val.Write(1); val.Write(string.Empty); val.Write(message); val.Write(8f); ZRoutedRpc.instance.InvokeRoutedRPC(num, "RunningMan_CenterMessage", new object[1] { val }); } } } public static void SendGearViolation(string reason) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown if (string.IsNullOrEmpty(reason) || ZRoutedRpc.instance == null) { return; } if (ValheimUtil.IsServerAuthority()) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { RaceManager.Instance?.ReportGearViolation(localPlayer.GetPlayerID(), reason); } } else { ZPackage val = new ZPackage(); val.Write(reason); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "RunningMan_GearViolation", new object[1] { val }); } } private static void ReceiveGearViolation(long sender, ZPackage package) { if (!ValheimUtil.IsServerAuthority()) { return; } try { string reason = package.ReadString(); long playerId = sender; if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null) { Player val = ValheimUtil.FindPlayerFromPeer(peer); if ((Object)(object)val != (Object)null) { playerId = val.GetPlayerID(); } else if (peer.m_uid != 0L) { playerId = peer.m_uid; } } } RaceManager.Instance?.ReportGearViolation(playerId, reason); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read gear violation: " + ex.Message)); } } private static void ReceiveCenterMessage(long sender, ZPackage package) { if (ValheimUtil.IsServerAuthority()) { return; } try { int num = package.ReadInt(); string title = package.ReadString(); string text = package.ReadString(); float seconds = package.ReadSingle(); if (num == 1) { RaceGui.ShowYellowHud(text); } else { RaceGui.ShowInfoPanel(title, text, seconds); } } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read center message: " + ex.Message)); } } public static void SendRaceCue(long playerId, byte cue) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown if (!ValheimUtil.IsServerAuthority()) { return; } if (IsLocalPlayerTarget(playerId)) { PlayRaceCue(cue); } else if (ZRoutedRpc.instance != null) { long num = ResolvePeerId(playerId); if (num != 0L) { ZPackage val = new ZPackage(); val.Write(cue); ZRoutedRpc.instance.InvokeRoutedRPC(num, "RunningMan_RaceCue", new object[1] { val }); } } } private static void ReceiveRaceCue(long sender, ZPackage package) { try { PlayRaceCue(package.ReadByte()); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read race cue: " + ex.Message)); } } private static void PlayRaceCue(byte cue) { switch (cue) { case 1: RaceSoundPlayer.PlayCheckpoint(); RaceSoundMonitor.NotifyCheckpointCue(); break; case 2: RaceSoundPlayer.PlayFirstPlaceFinish(); RaceSoundMonitor.NotifyFinishCue(); break; case 3: RaceSoundPlayer.PlayFalseStart(); break; } } private static bool IsLocalPlayerTarget(long playerId) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } if (Player.m_localPlayer.GetPlayerID() == playerId) { return true; } if ((Object)(object)ZNet.instance == (Object)null) { return false; } ZNetPeer peer = ZNet.instance.GetPeer(Player.m_localPlayer.GetPlayerID()); if (peer != null) { return peer.m_uid == playerId; } return false; } private static long ResolvePeerId(long playerId) { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } ZNetPeer peer = ZNet.instance.GetPeer(playerId); if (peer != null) { return peer.m_uid; } foreach (ZNetPeer peer2 in ZNet.instance.GetPeers()) { if (peer2 != null) { if (peer2.m_uid == playerId) { return peer2.m_uid; } Player val = ValheimUtil.FindPlayerFromPeer(peer2); if ((Object)(object)val != (Object)null && val.GetPlayerID() == playerId) { return peer2.m_uid; } } } return 0L; } public static void SendCountdownStart(DateTime endUtc) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown SetLocalCountdown(endUtc); if (ValheimUtil.IsServerAuthority() && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(endUtc.Ticks); ZRoutedRpc.instance.InvokeRoutedRPC("RunningMan_CountdownStart", new object[2] { ZRoutedRpc.Everybody, val }); } } private static void ReceiveCountdownStart(long sender, ZPackage package) { if (ValheimUtil.IsServerAuthority()) { return; } try { SetLocalCountdown(new DateTime(package.ReadLong(), DateTimeKind.Utc)); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read countdown sync: " + ex.Message)); } } public static void FlushCommandReplies(long targetPeerId) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if (!ValheimUtil.IsServerAuthority() || targetPeerId == 0L || ZRoutedRpc.instance == null) { return; } IReadOnlyList readOnlyList = CommandContext.TakePendingReplies(); if (readOnlyList.Count == 0) { return; } ZPackage val = new ZPackage(); val.Write(readOnlyList.Count); foreach (string item in readOnlyList) { val.Write(item ?? string.Empty); } ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "RunningMan_CommandReply", new object[1] { val }); } public static void SendTrackToPeer(long peerId, TrackConfig track) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (ValheimUtil.IsServerAuthority() && track != null && peerId != 0L) { if (IsLocalPeer(peerId)) { ApplyLocalTrack(track); } if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); WriteTrack(val, track); ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "RunningMan_Track", new object[1] { val }); } } } public static void SendStateToPeer(long peerId, RaceStateSnapshot snapshot) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (ValheimUtil.IsServerAuthority() && snapshot != null && peerId != 0L) { if (IsLocalPeer(peerId)) { ApplyLocalSnapshot(snapshot); } if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); WriteSnapshot(val, snapshot); ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "RunningMan_RaceState", new object[1] { val }); } } } private static void ReceiveCommandReply(long sender, ZPackage package) { if (ValheimUtil.IsServerAuthority()) { return; } try { int num = package.ReadInt(); for (int i = 0; i < num; i++) { string text = package.ReadString(); RaceGuiLog.Add(text); if (!string.IsNullOrEmpty(text) && (text.IndexOf("starting area", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Not all participants", StringComparison.OrdinalIgnoreCase) >= 0)) { RaceGui.ShowYellowHud(text); } } } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read command reply: " + ex.Message)); } } public static void SendRunCommand(string args, Vector3? originPosition = null, Vector3? originForward = null) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(args ?? string.Empty); bool flag = originPosition.HasValue && originForward.HasValue; val.Write(flag); if (flag) { val.Write(originPosition.Value.x); val.Write(originPosition.Value.y); val.Write(originPosition.Value.z); val.Write(originForward.Value.x); val.Write(originForward.Value.y); val.Write(originForward.Value.z); } ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "RunningMan_RunCommand", new object[1] { val }); } } private static void ReceiveRunCommand(long sender, ZPackage package) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!ValheimUtil.IsServerAuthority()) { return; } string text = package.ReadString(); if (package.ReadBool()) { Vector3 position = new Vector3(package.ReadSingle(), package.ReadSingle(), package.ReadSingle()); Vector3 forward = default(Vector3); ((Vector3)(ref forward))..ctor(package.ReadSingle(), package.ReadSingle(), package.ReadSingle()); CommandContext.SetOrigin(position, forward); } RunningManPlugin.Log.LogInfo((object)$"RunningMan RPC command from peer {sender}: run {text}"); try { Commands.ExecuteFromRemote(sender, text); } catch (Exception arg) { RunningManPlugin.Log.LogError((object)$"RunningMan command RPC failed: {arg}"); } finally { SyncToCommandSender(); FlushCommandReplies(sender); CommandContext.ExecutingFromRemote = false; CommandContext.Clear(); } } public static void SendRaceState(RaceStateSnapshot snapshot) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (ValheimUtil.IsServerAuthority() && snapshot != null) { ApplyLocalSnapshot(snapshot); if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); WriteSnapshot(val, snapshot); ZRoutedRpc.instance.InvokeRoutedRPC("RunningMan_RaceState", new object[2] { ZRoutedRpc.Everybody, val }); BroadcastAdminStatus(); } } } public static void BroadcastAdminStatus() { if (!ValheimUtil.IsServerAuthority() || (Object)(object)ZNet.instance == (Object)null) { return; } if (ZNet.instance.IsServer()) { ClientIsAdmin = ZNet.instance.LocalPlayerIsAdminOrHost() || (Object)(object)Player.m_localPlayer == (Object)null; } if (ZRoutedRpc.instance == null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_uid != 0L) { SendAdminStatusToPeer(peer.m_uid); } } } public static void SendAdminStatusToPeer(long peerId, bool? forceAdmin = null) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown if (!ValheimUtil.IsServerAuthority() || peerId == 0L) { return; } bool flag = forceAdmin == true; if (!forceAdmin.HasValue && (Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(peerId); if (peer != null) { flag = ValheimUtil.IsConnectedPeerAdmin(peer); } else if (IsLocalPeer(peerId)) { flag = ZNet.instance.LocalPlayerIsAdminOrHost(); } } if (IsLocalPeer(peerId)) { ClientIsAdmin = flag; } if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(flag); ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "RunningMan_AdminStatus", new object[1] { val }); } } private static void ReceiveAdminStatus(long sender, ZPackage package) { try { ClientIsAdmin = package.ReadBool(); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read admin status: " + ex.Message)); } } public static void ApplyLocalSnapshot(RaceStateSnapshot snapshot) { if (snapshot != null) { ClientState = snapshot; if (snapshot.AllowedGear != null) { ClientAllowedGear = snapshot.AllowedGear.Clone(); } UpdateLocalCountdownFromSnapshot(snapshot); RaceNetSync.StateUpdated?.Invoke(); } } private static void UpdateLocalCountdownFromSnapshot(RaceStateSnapshot snapshot) { if (snapshot != null) { if (snapshot.Phase == 2 && snapshot.CountdownEndUtcTicks > 0) { SetLocalCountdown(new DateTime(snapshot.CountdownEndUtcTicks, DateTimeKind.Utc)); } else if (snapshot.Phase == 3 || snapshot.Phase == 0) { ClearLocalCountdown(); } } } public static void ApplyLocalTrack(TrackConfig track) { if (track != null) { ClientTrack = track; RaceNetSync.TrackUpdated?.Invoke(); } } public static void SendTrack(TrackConfig track) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (ValheimUtil.IsServerAuthority() && track != null) { ApplyLocalTrack(track); if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); WriteTrack(val, track); ZRoutedRpc.instance.InvokeRoutedRPC("RunningMan_Track", new object[2] { ZRoutedRpc.Everybody, val }); } } } public static void ApplyLocalWrBoards(List boards) { ClientWrBoards = ((boards != null) ? boards.Select((Vector3Data b) => new Vector3Data(b.X, b.Y, b.Z)).ToList() : new List()); } public static void ApplyLocalRulesBoards(List boards) { ClientRulesBoards = ((boards != null) ? boards.Select((Vector3Data b) => new Vector3Data(b.X, b.Y, b.Z)).ToList() : new List()); } public static void SendBulletins() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (ValheimUtil.IsServerAuthority()) { ApplyLocalWrBoards(JsonStorage.WrBoards); ApplyLocalRulesBoards(JsonStorage.RulesBoards); if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); WriteWrBoards(val, JsonStorage.WrBoards); WriteWrBoards(val, JsonStorage.RulesBoards); ZRoutedRpc.instance.InvokeRoutedRPC("RunningMan_WrBoards", new object[2] { ZRoutedRpc.Everybody, val }); } } } public static void SendBulletinsToPeer(long peerId) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if (ValheimUtil.IsServerAuthority() && peerId != 0L) { if (IsLocalPeer(peerId)) { ApplyLocalWrBoards(JsonStorage.WrBoards); ApplyLocalRulesBoards(JsonStorage.RulesBoards); } if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); WriteWrBoards(val, JsonStorage.WrBoards); WriteWrBoards(val, JsonStorage.RulesBoards); ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "RunningMan_WrBoards", new object[1] { val }); } } } private static void ReceiveWrBoards(long sender, ZPackage package) { if (ValheimUtil.IsServerAuthority()) { return; } try { ApplyLocalWrBoards(ReadWrBoards(package)); try { ApplyLocalRulesBoards(ReadWrBoards(package)); } catch { ApplyLocalRulesBoards(new List()); } } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read bulletin sync: " + ex.Message)); } } private static void WriteWrBoards(ZPackage package, List boards) { package.Write(boards?.Count ?? 0); if (boards == null) { return; } foreach (Vector3Data board in boards) { WriteVector(package, board); } } private static List ReadWrBoards(ZPackage package) { List list = new List(); int num = package.ReadInt(); for (int i = 0; i < num; i++) { list.Add(ReadVector(package)); } return list; } public static RunnerSnapshot GetLocalRunner() { if ((Object)(object)Player.m_localPlayer == (Object)null || ClientState?.Runners == null) { return null; } long playerID = Player.m_localPlayer.GetPlayerID(); string playerName = Player.m_localPlayer.GetPlayerName(); foreach (RunnerSnapshot runner in ClientState.Runners) { if (runner.PlayerId == playerID) { return runner; } } if (!string.IsNullOrWhiteSpace(playerName)) { foreach (RunnerSnapshot runner2 in ClientState.Runners) { if (string.Equals(runner2.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { return runner2; } } } if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(playerID); if (peer != null) { foreach (RunnerSnapshot runner3 in ClientState.Runners) { if (runner3.PlayerId == peer.m_uid) { return runner3; } } } } return null; } public static bool IsLocalRegistered() { if ((Object)(object)Player.m_localPlayer == (Object)null || ClientState?.Registered == null) { return false; } long playerID = Player.m_localPlayer.GetPlayerID(); string playerName = Player.m_localPlayer.GetPlayerName(); foreach (RegisteredParticipant item in ClientState.Registered) { if (item.PlayerId == playerID) { return true; } } if (!string.IsNullOrWhiteSpace(playerName)) { foreach (RegisteredParticipant item2 in ClientState.Registered) { if (string.Equals(item2.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } private static void ReceiveRaceState(long sender, ZPackage package) { if (!ShouldReceiveClientSync()) { return; } try { ClientState = ReadSnapshot(package); if (ClientState?.AllowedGear != null) { ClientAllowedGear = ClientState.AllowedGear.Clone(); } UpdateLocalCountdownFromSnapshot(ClientState); RaceNetSync.StateUpdated?.Invoke(); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read race state sync: " + ex.Message)); } } private static void ReceiveTrack(long sender, ZPackage package) { if (!ShouldReceiveClientSync()) { return; } try { ClientTrack = ReadTrack(package); RaceNetSync.TrackUpdated?.Invoke(); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("RunningMan failed to read track sync: " + ex.Message)); } } private static void WriteSnapshot(ZPackage package, RaceStateSnapshot snapshot) { package.Write(snapshot.Phase); package.Write(snapshot.CountdownEndUtcTicks); package.Write(snapshot.DebugMode); package.Write(snapshot.RegistrationOpen); package.Write(snapshot.TotalCheckpoints); package.Write(snapshot.Registered.Count); foreach (RegisteredParticipant item in snapshot.Registered) { package.Write(item.PlayerId); package.Write(item.PlayerName ?? string.Empty); } package.Write(snapshot.Runners.Count); foreach (RunnerSnapshot runner in snapshot.Runners) { package.Write(runner.PlayerId); package.Write(runner.PlayerName ?? string.Empty); package.Write(runner.StartUtcTicks); package.Write(runner.NextCheckpointIndex); package.Write(runner.Finished); package.Write(runner.FinishTimeMs); package.Write(runner.Place); package.Write(runner.Disqualified); package.Write(runner.DisqualifiedReason ?? string.Empty); package.Write(runner.FeatherCapeUnlocked); package.Write(runner.LastCheckpointTimeMs); } package.Write(snapshot.AdminPlayerIds?.Count ?? 0); if (snapshot.AdminPlayerIds != null) { foreach (long adminPlayerId in snapshot.AdminPlayerIds) { package.Write(adminPlayerId); } } package.Write(snapshot.TrackName ?? string.Empty); package.Write(snapshot.TrackId ?? string.Empty); package.Write(snapshot.WorldRecords?.Count ?? 0); if (snapshot.WorldRecords != null) { foreach (WorldRecordEntry worldRecord in snapshot.WorldRecords) { package.Write(worldRecord.Place); package.Write(worldRecord.PlayerName ?? string.Empty); package.Write(worldRecord.Time ?? string.Empty); package.Write(worldRecord.TimeMs); package.Write(worldRecord.Date ?? string.Empty); } } package.Write(snapshot.ParTotalTimeMs); package.Write(snapshot.ParCheckpointTimesMs?.Count ?? 0); if (snapshot.ParCheckpointTimesMs != null) { foreach (long parCheckpointTimesM in snapshot.ParCheckpointTimesMs) { package.Write(parCheckpointTimesM); } } WriteAllowedGear(package, snapshot.AllowedGear ?? AllowedGearRules.CreateDefaults()); } private static RaceStateSnapshot ReadSnapshot(ZPackage package) { RaceStateSnapshot raceStateSnapshot = new RaceStateSnapshot { Phase = package.ReadInt(), CountdownEndUtcTicks = package.ReadLong(), DebugMode = package.ReadBool(), RegistrationOpen = package.ReadBool(), TotalCheckpoints = package.ReadInt() }; int num = package.ReadInt(); for (int i = 0; i < num; i++) { raceStateSnapshot.Registered.Add(new RegisteredParticipant { PlayerId = package.ReadLong(), PlayerName = package.ReadString() }); } int num2 = package.ReadInt(); for (int j = 0; j < num2; j++) { raceStateSnapshot.Runners.Add(new RunnerSnapshot { PlayerId = package.ReadLong(), PlayerName = package.ReadString(), StartUtcTicks = package.ReadLong(), NextCheckpointIndex = package.ReadInt(), Finished = package.ReadBool(), FinishTimeMs = package.ReadLong(), Place = package.ReadInt(), Disqualified = package.ReadBool(), DisqualifiedReason = (package.ReadString() ?? string.Empty), FeatherCapeUnlocked = package.ReadBool(), LastCheckpointTimeMs = package.ReadLong() }); } int num3 = package.ReadInt(); for (int k = 0; k < num3; k++) { raceStateSnapshot.AdminPlayerIds.Add(package.ReadLong()); } try { raceStateSnapshot.TrackName = package.ReadString(); raceStateSnapshot.TrackId = package.ReadString(); int num4 = package.ReadInt(); for (int l = 0; l < num4; l++) { raceStateSnapshot.WorldRecords.Add(new WorldRecordEntry { Place = package.ReadInt(), PlayerName = package.ReadString(), Time = package.ReadString(), TimeMs = package.ReadLong(), Date = package.ReadString() }); } raceStateSnapshot.ParTotalTimeMs = package.ReadLong(); int num5 = package.ReadInt(); for (int m = 0; m < num5; m++) { raceStateSnapshot.ParCheckpointTimesMs.Add(package.ReadLong()); } raceStateSnapshot.AllowedGear = TryReadAllowedGear(package); } catch { raceStateSnapshot.TrackName = string.Empty; raceStateSnapshot.TrackId = string.Empty; raceStateSnapshot.WorldRecords = new List(); raceStateSnapshot.ParCheckpointTimesMs = new List(); raceStateSnapshot.ParTotalTimeMs = 0L; raceStateSnapshot.AllowedGear = null; } return raceStateSnapshot; } private static void WriteAllowedGear(ZPackage package, AllowedGearRules rules) { package.Write(rules?.Helmet ?? string.Empty); package.Write(rules?.Chest ?? string.Empty); package.Write(rules?.Legs ?? string.Empty); package.Write(rules?.Cape ?? string.Empty); package.Write(rules?.AllowedHandItems ?? string.Empty); package.Write(rules?.AntiStingPrefab ?? string.Empty); package.Write(rules?.RatatoskPrefab ?? string.Empty); package.Write(string.Empty); package.Write(rules?.RequiredAntiSting ?? 0); package.Write(rules?.RequiredRatatosk ?? 0); package.Write(0); package.Write(rules?.SaladPrefab ?? string.Empty); package.Write(rules?.BloodPuddingPrefab ?? string.Empty); package.Write(rules?.MushroomOmelettePrefab ?? string.Empty); package.Write(rules?.RequiredSalad ?? 0); package.Write(rules?.RequiredBloodPudding ?? 0); package.Write(rules?.RequiredMushroomOmelette ?? 0); } private static AllowedGearRules TryReadAllowedGear(ZPackage package) { try { AllowedGearRules allowedGearRules = new AllowedGearRules { Helmet = (package.ReadString() ?? string.Empty), Chest = (package.ReadString() ?? string.Empty), Legs = (package.ReadString() ?? string.Empty), Cape = (package.ReadString() ?? string.Empty), AllowedHandItems = (package.ReadString() ?? string.Empty), AntiStingPrefab = (package.ReadString() ?? string.Empty), RatatoskPrefab = (package.ReadString() ?? string.Empty) }; package.ReadString(); allowedGearRules.RequiredAntiSting = package.ReadInt(); allowedGearRules.RequiredRatatosk = package.ReadInt(); package.ReadInt(); try { allowedGearRules.SaladPrefab = package.ReadString() ?? "Salad"; allowedGearRules.BloodPuddingPrefab = package.ReadString() ?? "BloodPudding"; allowedGearRules.MushroomOmelettePrefab = package.ReadString() ?? "MushroomOmelette"; allowedGearRules.RequiredSalad = package.ReadInt(); allowedGearRules.RequiredBloodPudding = package.ReadInt(); allowedGearRules.RequiredMushroomOmelette = package.ReadInt(); } catch { allowedGearRules.SaladPrefab = "Salad"; allowedGearRules.BloodPuddingPrefab = "BloodPudding"; allowedGearRules.MushroomOmelettePrefab = "MushroomOmelette"; allowedGearRules.RequiredSalad = 2; allowedGearRules.RequiredBloodPudding = 2; allowedGearRules.RequiredMushroomOmelette = 2; } return allowedGearRules; } catch { return null; } } private static void WriteTrack(ZPackage package, TrackConfig track) { WriteGate(package, track.StartGate); WriteGate(package, track.FinishGate); package.Write(track.Checkpoints?.Count ?? 0); if (track.Checkpoints != null) { foreach (Checkpoint checkpoint in track.Checkpoints) { package.Write(checkpoint.Index); WriteGate(package, checkpoint.Gate); } } package.Write(track.Name ?? string.Empty); } private static TrackConfig ReadTrack(ZPackage package) { TrackConfig trackConfig = new TrackConfig { StartGate = ReadGate(package), FinishGate = ReadGate(package) }; int num = package.ReadInt(); for (int i = 0; i < num; i++) { trackConfig.Checkpoints.Add(new Checkpoint(package.ReadInt(), ReadGate(package))); } try { trackConfig.Name = package.ReadString() ?? string.Empty; } catch { trackConfig.Name = string.Empty; } return trackConfig; } private static void WriteGate(ZPackage package, RaceGate gate) { WriteVector(package, gate?.PointA); WriteVector(package, gate?.PointB); WriteVector(package, gate?.Forward); } private static RaceGate ReadGate(ZPackage package) { return new RaceGate { PointA = ReadVector(package), PointB = ReadVector(package), Forward = ReadVector(package) }; } private static void WriteVector(ZPackage package, Vector3Data vector) { package.Write(vector?.X ?? 0f); package.Write(vector?.Y ?? 0f); package.Write(vector?.Z ?? 0f); } private static Vector3Data ReadVector(ZPackage package) { return new Vector3Data(package.ReadSingle(), package.ReadSingle(), package.ReadSingle()); } } } namespace RunningMan.Storage { [Serializable] public sealed class AllowedGearRules { public string Helmet = "HelmetTrollLeather"; public string Chest = "ArmorTrollLeatherChest"; public string Legs = "ArmorTrollLeatherLegs"; public string Cape = "CapeFeather"; public string AllowedHandItems = string.Empty; public string AntiStingPrefab = "MeadBugRepellent"; public string RatatoskPrefab = "MeadHasty"; public int RequiredAntiSting = 1; public int RequiredRatatosk = 2; public string SaladPrefab = "Salad"; public string BloodPuddingPrefab = "BloodPudding"; public string MushroomOmelettePrefab = "MushroomOmelette"; public int RequiredSalad = 2; public int RequiredBloodPudding = 2; public int RequiredMushroomOmelette = 2; public static AllowedGearRules CreateDefaults() { return new AllowedGearRules { Helmet = "HelmetTrollLeather", Chest = "ArmorTrollLeatherChest", Legs = "ArmorTrollLeatherLegs", Cape = "CapeFeather", AllowedHandItems = (ModConfig.AllowedHandItems?.Value ?? string.Empty), AntiStingPrefab = "MeadBugRepellent", RatatoskPrefab = "MeadHasty", RequiredAntiSting = (ModConfig.RequiredAntiStingCount?.Value ?? 1), RequiredRatatosk = (ModConfig.RequiredRatatoskCount?.Value ?? 2), SaladPrefab = "Salad", BloodPuddingPrefab = "BloodPudding", MushroomOmelettePrefab = "MushroomOmelette", RequiredSalad = 2, RequiredBloodPudding = 2, RequiredMushroomOmelette = 2 }; } public AllowedGearRules Clone() { return new AllowedGearRules { Helmet = (Helmet ?? string.Empty), Chest = (Chest ?? string.Empty), Legs = (Legs ?? string.Empty), Cape = (Cape ?? string.Empty), AllowedHandItems = (AllowedHandItems ?? string.Empty), AntiStingPrefab = (AntiStingPrefab ?? string.Empty), RatatoskPrefab = (RatatoskPrefab ?? string.Empty), RequiredAntiSting = RequiredAntiSting, RequiredRatatosk = RequiredRatatosk, SaladPrefab = (SaladPrefab ?? string.Empty), BloodPuddingPrefab = (BloodPuddingPrefab ?? string.Empty), MushroomOmelettePrefab = (MushroomOmelettePrefab ?? string.Empty), RequiredSalad = RequiredSalad, RequiredBloodPudding = RequiredBloodPudding, RequiredMushroomOmelette = RequiredMushroomOmelette }; } public string Format() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Allowed gear:"); stringBuilder.AppendLine(" Helmet: " + DisplaySlot(Helmet)); stringBuilder.AppendLine(" Chest: " + DisplaySlot(Chest)); stringBuilder.AppendLine(" Legs: " + DisplaySlot(Legs)); stringBuilder.AppendLine(" Cape: " + (string.IsNullOrWhiteSpace(Cape) ? "(none allowed)" : (Cape + " (or no cape)"))); stringBuilder.AppendLine(" Hands: any (not checked)"); stringBuilder.AppendLine($" Meads: {RequiredAntiSting}x {AntiStingPrefab}, {RequiredRatatosk}x {RatatoskPrefab}"); stringBuilder.AppendLine($" Food: {RequiredSalad}x {SaladPrefab}, {RequiredBloodPudding}x {BloodPuddingPrefab}, {RequiredMushroomOmelette}x {MushroomOmelettePrefab}"); return stringBuilder.ToString().TrimEnd(Array.Empty()); } private static string DisplaySlot(string prefab) { if (!string.IsNullOrWhiteSpace(prefab)) { return prefab; } return "(must be empty)"; } } public static class JsonStorage { private static string _rootPath; private static TrackConfig _track = new TrackConfig(); private static RaceDatabase _database = new RaceDatabase(); private static AllowedGearRules _allowedGear = AllowedGearRules.CreateDefaults(); private static List _wrBoards = new List(); private static List _rulesBoards = new List(); public static TrackConfig Track => _track; public static RaceDatabase Database => _database; public static AllowedGearRules AllowedGear => _allowedGear; public static List WrBoards => _wrBoards; public static List RulesBoards => _rulesBoards; public static string TrackFilePath => Path.Combine(_rootPath, "track.json"); public static string DatabaseFilePath => Path.Combine(_rootPath, "races.json"); public static string AllowedGearFilePath => Path.Combine(_rootPath, "allowed_gear.json"); public static string WrBoardsFilePath => Path.Combine(_rootPath, "wr_boards.json"); public static string RulesBoardsFilePath => Path.Combine(_rootPath, "rules_boards.json"); public static string ExportFilePath => Path.Combine(_rootPath, "export.json"); public static void Initialize(string bepInExConfigPath) { string text = ModConfig.SavePath.Value?.Trim() ?? "RunningMan/"; _rootPath = Path.Combine(bepInExConfigPath, text.Replace('/', Path.DirectorySeparatorChar)); Directory.CreateDirectory(_rootPath); Reload(); } public static void Reload() { _track = LoadTrack() ?? new TrackConfig(); _database = LoadDatabase() ?? new RaceDatabase(); _allowedGear = LoadAllowedGear() ?? AllowedGearRules.CreateDefaults(); _wrBoards = LoadWrBoards() ?? new List(); _rulesBoards = LoadRulesBoards() ?? new List(); RunningManPlugin.Log.LogInfo((object)("RunningMan data loaded from " + _rootPath)); } public static void SaveWrBoards() { WriteJson(WrBoardsFilePath, new WrBoardsFile { Boards = _wrBoards }); } public static void SaveRulesBoards() { WriteJson(RulesBoardsFilePath, new WrBoardsFile { Boards = _rulesBoards }); } public static void SetWrBoards(List boards, bool save = true) { _wrBoards = CloneBoardList(boards); if (save) { SaveWrBoards(); } } public static void SetRulesBoards(List boards, bool save = true) { _rulesBoards = CloneBoardList(boards); if (save) { SaveRulesBoards(); } } private static List CloneBoardList(List boards) { if (boards == null) { return new List(); } return (from b in boards where b != null select new Vector3Data(b.X, b.Y, b.Z)).ToList(); } public static void SaveTrack() { WriteJson(TrackFilePath, _track); } public static void SaveDatabase() { WriteJson(DatabaseFilePath, _database); } public static void SaveAllowedGear() { WriteJson(AllowedGearFilePath, _allowedGear); } public static void SetAllowedGear(AllowedGearRules rules, bool save = true) { if (rules != null) { _allowedGear = rules.Clone(); if (save) { SaveAllowedGear(); } } } public static void ResetAllowedGearToDefaults() { _allowedGear = AllowedGearRules.CreateDefaults(); SaveAllowedGear(); } public static void ExportAll() { ExportBundle data = new ExportBundle { ExportedAt = DateTime.UtcNow.ToString("o"), Track = _track, Races = _database.Runs, PersonalBests = _database.PersonalBests, AllowedGear = _allowedGear }; WriteJson(ExportFilePath, data); } private static TrackConfig LoadTrack() { return ReadJsonFile(TrackFilePath); } private static RaceDatabase LoadDatabase() { return ReadJsonFile(DatabaseFilePath); } private static AllowedGearRules LoadAllowedGear() { return ReadJsonFile(AllowedGearFilePath); } private static List LoadWrBoards() { return ReadJsonFile(WrBoardsFilePath)?.Boards ?? new List(); } private static List LoadRulesBoards() { return ReadJsonFile(RulesBoardsFilePath)?.Boards ?? new List(); } private static T ReadJsonFile(string path) where T : class { if (!File.Exists(path)) { return null; } try { return TypedJson.Deserialize(File.ReadAllText(path, Encoding.UTF8)); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("Failed to read " + path + ": " + ex.Message)); return null; } } private static void WriteJson(string path, object data) { try { string contents = TypedJson.Serialize(data); File.WriteAllText(path, contents, Encoding.UTF8); } catch (Exception ex) { RunningManPlugin.Log.LogError((object)("Failed to write " + path + ": " + ex.Message)); } } } [Serializable] public sealed class RaceDatabase { public List Runs = new List(); public Dictionary PersonalBests = new Dictionary(StringComparer.Ordinal); public Dictionary LastRuns = new Dictionary(StringComparer.Ordinal); } [Serializable] public sealed class ExportBundle { public string ExportedAt; public TrackConfig Track; public List Races; public Dictionary PersonalBests; public AllowedGearRules AllowedGear; } [Serializable] public sealed class WrBoardsFile { public List Boards = new List(); } internal static class TypedJson { public static string Serialize(object value) { return JsonWriter.Write(value); } public static T Deserialize(string json) where T : class { if (string.IsNullOrWhiteSpace(json)) { return null; } return ConvertTo(JsonParser.Parse(json)); } private static T ConvertTo(object value) where T : class { if (value == null) { return null; } if (typeof(T) == typeof(TrackConfig)) { return (T)(object)ParseTrackConfig(value as Dictionary); } if (typeof(T) == typeof(RaceDatabase)) { return (T)(object)ParseRaceDatabase(value as Dictionary); } if (typeof(T) == typeof(AllowedGearRules)) { return (T)(object)ParseAllowedGear(value as Dictionary); } if (typeof(T) == typeof(WrBoardsFile)) { return (T)(object)ParseWrBoardsFile(value as Dictionary); } return null; } private static WrBoardsFile ParseWrBoardsFile(Dictionary dict) { WrBoardsFile wrBoardsFile = new WrBoardsFile(); if (dict == null) { return wrBoardsFile; } foreach (object item in GetList(dict, "Boards")) { Vector3Data vector3Data = ParseVector(item as Dictionary); if (vector3Data != null) { wrBoardsFile.Boards.Add(vector3Data); } } return wrBoardsFile; } private static AllowedGearRules ParseAllowedGear(Dictionary dict) { AllowedGearRules allowedGearRules = AllowedGearRules.CreateDefaults(); if (dict == null) { return allowedGearRules; } allowedGearRules.Helmet = GetString(dict, "Helmet") ?? allowedGearRules.Helmet; allowedGearRules.Chest = GetString(dict, "Chest") ?? allowedGearRules.Chest; allowedGearRules.Legs = GetString(dict, "Legs") ?? allowedGearRules.Legs; allowedGearRules.Cape = GetString(dict, "Cape") ?? allowedGearRules.Cape; allowedGearRules.AllowedHandItems = GetString(dict, "AllowedHandItems") ?? allowedGearRules.AllowedHandItems; allowedGearRules.AntiStingPrefab = GetString(dict, "AntiStingPrefab") ?? allowedGearRules.AntiStingPrefab; allowedGearRules.RatatoskPrefab = GetString(dict, "RatatoskPrefab") ?? allowedGearRules.RatatoskPrefab; if (dict.ContainsKey("RequiredAntiSting")) { allowedGearRules.RequiredAntiSting = GetInt(dict, "RequiredAntiSting"); } if (dict.ContainsKey("RequiredRatatosk")) { allowedGearRules.RequiredRatatosk = GetInt(dict, "RequiredRatatosk"); } allowedGearRules.SaladPrefab = GetString(dict, "SaladPrefab") ?? allowedGearRules.SaladPrefab; allowedGearRules.BloodPuddingPrefab = GetString(dict, "BloodPuddingPrefab") ?? allowedGearRules.BloodPuddingPrefab; allowedGearRules.MushroomOmelettePrefab = GetString(dict, "MushroomOmelettePrefab") ?? allowedGearRules.MushroomOmelettePrefab; if (dict.ContainsKey("RequiredSalad")) { allowedGearRules.RequiredSalad = GetInt(dict, "RequiredSalad"); } if (dict.ContainsKey("RequiredBloodPudding")) { allowedGearRules.RequiredBloodPudding = GetInt(dict, "RequiredBloodPudding"); } if (dict.ContainsKey("RequiredMushroomOmelette")) { allowedGearRules.RequiredMushroomOmelette = GetInt(dict, "RequiredMushroomOmelette"); } return allowedGearRules; } private static TrackConfig ParseTrackConfig(Dictionary dict) { if (dict == null) { return new TrackConfig(); } return new TrackConfig { Name = (GetString(dict, "Name") ?? string.Empty), StartGate = ParseGate(GetDict(dict, "StartGate")), FinishGate = ParseGate(GetDict(dict, "FinishGate")), Checkpoints = ParseCheckpoints(GetList(dict, "Checkpoints")) }; } private static RaceDatabase ParseRaceDatabase(Dictionary dict) { RaceDatabase raceDatabase = new RaceDatabase(); if (dict == null) { return raceDatabase; } foreach (object item in GetList(dict, "Runs")) { RaceRecord raceRecord = ParseRaceRecord(item as Dictionary); if (raceRecord != null) { raceDatabase.Runs.Add(raceRecord); } } foreach (KeyValuePair item2 in GetDict(dict, "PersonalBests")) { raceDatabase.PersonalBests[item2.Key] = ParseRaceRecord(item2.Value as Dictionary); } foreach (KeyValuePair item3 in GetDict(dict, "LastRuns")) { raceDatabase.LastRuns[item3.Key] = ParseRaceRecord(item3.Value as Dictionary); } return raceDatabase; } private static RaceRecord ParseRaceRecord(Dictionary dict) { if (dict == null) { return null; } return new RaceRecord { Player = GetString(dict, "Player"), PlayerId = GetString(dict, "PlayerId"), Date = GetString(dict, "Date"), FinishedAt = GetString(dict, "FinishedAt"), TotalTimeMs = GetLong(dict, "TotalTimeMs"), TotalTime = GetString(dict, "TotalTime"), CheckpointTimesMs = GetLongList(dict, "CheckpointTimesMs"), CheckpointTimes = GetStringList(dict, "CheckpointTimes"), SplitTimesMs = GetLongList(dict, "SplitTimesMs"), SplitTimes = GetStringList(dict, "SplitTimes"), CheckpointCount = GetInt(dict, "CheckpointCount"), TrackId = (GetString(dict, "TrackId") ?? string.Empty) }; } private static List ParseCheckpoints(List list) { List list2 = new List(); if (list == null) { return list2; } foreach (object item in list) { if (item is Dictionary dict) { list2.Add(new Checkpoint(GetInt(dict, "Index"), ParseGate(GetDict(dict, "Gate")))); } } return list2; } private static RaceGate ParseGate(Dictionary dict) { if (dict == null) { return new RaceGate(); } return new RaceGate { PointA = ParseVector(GetDict(dict, "PointA")), PointB = ParseVector(GetDict(dict, "PointB")), Forward = ParseVector(GetDict(dict, "Forward")) }; } private static Vector3Data ParseVector(Dictionary dict) { if (dict == null) { return new Vector3Data(); } return new Vector3Data((float)GetDouble(dict, "X"), (float)GetDouble(dict, "Y"), (float)GetDouble(dict, "Z")); } private static Dictionary GetDict(Dictionary dict, string key) { if (dict == null || !dict.TryGetValue(key, out var value)) { return null; } return value as Dictionary; } private static List GetList(Dictionary dict, string key) { if (dict == null || !dict.TryGetValue(key, out var value)) { return null; } return value as List; } private static string GetString(Dictionary dict, string key) { if (dict == null || !dict.TryGetValue(key, out var value)) { return null; } return value as string; } private static int GetInt(Dictionary dict, string key) { if (dict == null || !dict.TryGetValue(key, out var value)) { return 0; } return Convert.ToInt32(value); } private static long GetLong(Dictionary dict, string key) { if (dict == null || !dict.TryGetValue(key, out var value)) { return 0L; } return Convert.ToInt64(value); } private static double GetDouble(Dictionary dict, string key) { if (dict == null || !dict.TryGetValue(key, out var value)) { return 0.0; } return Convert.ToDouble(value); } private static List GetLongList(Dictionary dict, string key) { List list = GetList(dict, key); if (list == null) { return new List(); } return list.Select(Convert.ToInt64).ToList(); } private static List GetStringList(Dictionary dict, string key) { List list = GetList(dict, key); if (list == null) { return new List(); } return (from item in list select item as string into item where item != null select item).ToList(); } } internal static class JsonWriter { public static string Write(object value) { StringBuilder stringBuilder = new StringBuilder(); WriteValue(stringBuilder, value, 0); stringBuilder.AppendLine(); return stringBuilder.ToString(); } private static void WriteValue(StringBuilder builder, object value, int indent) { if (value != null) { if (!(value is string value2)) { if (!(value is bool flag)) { if (!(value is int value3)) { if (!(value is long value4)) { if (!(value is float num)) { if (!(value is double num2)) { if (!(value is IEnumerable source)) { if (!(value is IEnumerable source2)) { if (!(value is IEnumerable source3)) { if (!(value is IEnumerable source4)) { if (!(value is IEnumerable source5)) { if (!(value is IDictionary source6)) { if (value is IDictionary map) { WriteObject(builder, map, indent); } else { WriteObject(builder, FieldsToDictionary(value), indent); } return; } WriteObject(builder, ((IEnumerable>)source6).ToDictionary((Func, string>)((KeyValuePair pair) => pair.Key), (Func, object>)((KeyValuePair pair) => pair.Value)), indent); } else { WriteArray(builder, source5.Cast(), indent); } } else { WriteArray(builder, source4.Cast(), indent); } } else { WriteArray(builder, source3.Cast(), indent); } } else { WriteArray(builder, source2.Cast(), indent); } } else { WriteArray(builder, source.Cast(), indent); } } else { builder.Append(num2.ToString(CultureInfo.InvariantCulture)); } } else { builder.Append(num.ToString(CultureInfo.InvariantCulture)); } } else { builder.Append(value4); } } else { builder.Append(value3); } } else { builder.Append(flag ? "true" : "false"); } } else { builder.Append('"').Append(Escape(value2)).Append('"'); } } else { builder.Append("null"); } } private static void WriteArray(StringBuilder builder, IEnumerable items, int indent) { builder.AppendLine("["); List list = items.ToList(); for (int i = 0; i < list.Count; i++) { builder.Append(' ', (indent + 1) * 2); WriteValue(builder, list[i], indent + 1); builder.Append((i < list.Count - 1) ? "," : string.Empty); builder.AppendLine(); } builder.Append(' ', indent * 2); builder.Append(']'); } private static void WriteObject(StringBuilder builder, IDictionary map, int indent) { builder.AppendLine("{"); List list = map.Keys.ToList(); for (int i = 0; i < list.Count; i++) { string text = list[i]; builder.Append(' ', (indent + 1) * 2); builder.Append('"').Append(Escape(text)).Append('"') .Append(": "); WriteValue(builder, map[text], indent + 1); builder.Append((i < list.Count - 1) ? "," : string.Empty); builder.AppendLine(); } builder.Append(' ', indent * 2); builder.Append('}'); } private static Dictionary FieldsToDictionary(object value) { Dictionary dictionary = new Dictionary(StringComparer.Ordinal); FieldInfo[] fields = value.GetType().GetFields(); foreach (FieldInfo fieldInfo in fields) { dictionary[fieldInfo.Name] = fieldInfo.GetValue(value); } return dictionary; } private static string Escape(string value) { return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); } } internal static class JsonParser { private sealed class Reader { private readonly string _text; private int _index; public Reader(string text) { _text = text; } public object ParseValue() { SkipWhitespace(); switch (Peek()) { case '{': return ParseObject(); case '[': return ParseArray(); case '"': return ParseString(); case 'f': case 't': return ParseBool(); default: return ParseNumber(); } } private Dictionary ParseObject() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Expect('{'); SkipWhitespace(); if (Peek() == '}') { Read(); return dictionary; } while (true) { SkipWhitespace(); string key = ParseString(); SkipWhitespace(); Expect(':'); dictionary[key] = ParseValue(); SkipWhitespace(); if (Peek() == '}') { break; } Expect(','); } Read(); return dictionary; } private List ParseArray() { List list = new List(); Expect('['); SkipWhitespace(); if (Peek() == ']') { Read(); return list; } while (true) { list.Add(ParseValue()); SkipWhitespace(); if (Peek() == ']') { break; } Expect(','); } Read(); return list; } private string ParseString() { Expect('"'); StringBuilder stringBuilder = new StringBuilder(); while (_index < _text.Length) { char c = Read(); switch (c) { case '\\': { char c2 = Read(); StringBuilder stringBuilder2 = stringBuilder; stringBuilder2.Append(c2 switch { '"' => '"', '\\' => '\\', 'n' => '\n', 'r' => '\r', 't' => '\t', _ => c2, }); continue; } default: stringBuilder.Append(c); continue; case '"': break; } break; } return stringBuilder.ToString(); } private bool ParseBool() { if (Match("true")) { return true; } Match("false"); return false; } private object ParseNumber() { int index = _index; while (_index < _text.Length && Enumerable.Contains("0123456789+-.", Peek())) { Read(); } string text = _text.Substring(index, _index - index); if (text.Contains(".")) { return double.Parse(text, CultureInfo.InvariantCulture); } if (long.TryParse(text, out var result)) { return result; } return int.Parse(text, CultureInfo.InvariantCulture); } private void SkipWhitespace() { while (_index < _text.Length && char.IsWhiteSpace(Peek())) { _index++; } } private char Peek() { return _text[_index]; } private char Read() { return _text[_index++]; } private void Expect(char ch) { SkipWhitespace(); if (Read() != ch) { throw new FormatException($"Expected '{ch}' at {_index}."); } } private bool Match(string token) { SkipWhitespace(); if (_index + token.Length > _text.Length) { return false; } if (_text.Substring(_index, token.Length) != token) { return false; } _index += token.Length; return true; } } public static object Parse(string json) { return new Reader(json).ParseValue(); } } }