using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.MemoryMappedFiles; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using EventsIO; using ModHelper; using ModHelper.Data; using ModHelperUnity; using ModHelperUnity.UtilityGUI; using ModUnityTools; using Sons.Loading; using TheForest; using TheForest.Utils; using UnityEngine; using bft_mod.Command; using bft_mod.HelperBFT; using bft_mod.Service; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BFT")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f5c7ea763761c9fdef962aa9be063765a07b7ed1")] [assembly: AssemblyProduct("Sons of The Forest mod")] [assembly: AssemblyTitle("InteractiveMod")] [assembly: AssemblyVersion("1.0.0.0")] namespace bft_mod { public sealed class InteractiveMod : MonoBehaviour { private HelperCommandManagerFormatted commandManager; private EventsDataParser eventsParser; private EventsReaderOnFrame eventsReader; private EventsReaderOnKey eventsReaderByKey; private TypedNotifyOnGUI notifyManager; private const int UPDATE_ERRORS_LIMIT = 50; private int updateErrorsLimit = 50; private int memoryReaderLimit = 50; private string keyEventsPath; private bool isInitiated; private bool memoryReaderIsUsed; public UtilityConsole console; public void Awake() { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //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_0130: Expected O, but got Unknown //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown try { UtilitySettings.BaseFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); Console.WriteLine(UtilitySettings.BaseFolder); } catch (Exception value) { LoggerFactory.Logger.Log($"Awake {value}"); } if (isInitiated) { LoggerFactory.Logger.Log("InteractiveMod is already initiated"); return; } LoggerFactory.Logger.Log("InteractiveMod Awake"); isInitiated = true; LoggerFactory.Logger.Log("init console"); console = new UtilityConsole((HelperDebugLog)null, (List)null); try { LoggerFactory.Logger.Log("init data parser"); eventsParser = new EventsDataParser((Action)LoggerFactory.Logger.Log); LoggerFactory.Logger.Log("init reader"); eventsReader = new EventsReaderOnFrame(CreateEventsFile(), (Action)ProcessEvent, eventsParser, (Action)LoggerFactory.Logger.Log, (Action)LoggerFactory.Logger.Log, 1f, 0.1f, 0.5f) { IsDelayEveryProcessing = true }; keyEventsPath = Path.Combine(UtilitySettings.BaseFolder, "events_key.txt"); eventsReaderByKey = new EventsReaderOnKey((Action)ProcessEvent, eventsParser, (Action)LoggerFactory.Logger.Log, (Action)LoggerFactory.Logger.Log); InitOthersManagers(); InitCommands(isNeedValidate: true); } catch (Exception value2) { LoggerFactory.Logger.Log($"init error: {value2}"); } LoggerFactory.Logger.Log("InteractiveMod init end"); } private string CreateEventsFile() { try { string text = Path.Combine(Path.GetTempPath(), "chaos_tricks"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } text = Path.Combine(text, "sons_of_the_forest.txt"); File.CreateText(text).Close(); return text; } catch (Exception value) { LoggerFactory.Logger.Log($"InteractiveMod create events source error: {value}"); return Path.Combine(Path.GetTempPath(), "chaos_tricks", "sons_of_the_forest.txt"); } } public void OnDestroy() { LoggerFactory.Logger.Log("InteractiveMod OnDestroy"); } public void OnApplicationQuit() { LoggerFactory.Logger.Log("InteractiveMod OnApplicationQuit"); } public void InitOthersManagers() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown LoggerFactory.Logger.Log("init HelperLanguages"); TranslationFactory.Reinit(); TranslationFactory.Instance.InitFromFile(Path.Combine(UtilitySettings.BaseFolder, "langs.data")); LoggerFactory.Logger.Log("init ScheduleManager"); ScheduleFactory.Reinit(); updateErrorsLimit = 50; memoryReaderLimit = 50; if (notifyManager == null) { notifyManager = new TypedNotifyOnGUI(); ((TypedNotifyBase)notifyManager).ForcePostInit(UtilitySettings.BaseFolder, (HelperLog)null, (Func)null); } } public void InitCommands(bool isNeedValidate) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown console.RegisterCommand((ConsoleCommand)(object)new GameConsoleCommand()); console.RegisterCommand((ConsoleCommand)(object)new Immortality()); console.RegisterCommand((ConsoleCommand)(object)new InfiniteAmmo()); console.RegisterCommand((ConsoleCommand)(object)new InfiniteEnergy()); console.RegisterCommand((ConsoleCommand)(object)new SpeedyRun()); console.RegisterCommand((ConsoleCommand)(object)new SuperJump()); console.RegisterCommand((ConsoleCommand)(object)new InvertLook()); console.RegisterCommand((ConsoleCommand)(object)new AnimSpeedAI()); console.RegisterCommand((ConsoleCommand)(object)new Gravity()); console.RegisterCommand((ConsoleCommand)(object)new TimeScale()); console.RegisterCommand((ConsoleCommand)(object)new StopCommandTimer()); console.RegisterCommand((ConsoleCommand)(object)new MouseSensitivity()); commandManager = new HelperCommandManagerFormatted((HelperLog)null); commandManager.InitFromFile(Path.Combine(UtilitySettings.BaseFolder, "commands.data")); if (!isNeedValidate) { return; } LoggerFactory.Logger.Log("init HelperCommandManager"); string[] commands = commandManager.GetCommands(); foreach (string text in commands) { if (text != null && !console.IsValidCommandLine(text.Trim())) { LoggerFactory.Logger.Log("command line '" + text + "' invalid"); } } } private int GetRndNumberFromArgGroup(string group) { if (string.IsNullOrEmpty(group)) { return 1; } string[] array = group.Split(new char[2] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); if (array != null && array.Length >= 2) { int.TryParse(array[0], out var result); int.TryParse(array[1], out var result2); int num = Random.Range(result, result2); LoggerFactory.Logger.Log($"GetRndNumberFromArgGroup get value: {num}"); return num; } LoggerFactory.Logger.Log("GetRndNumberFromArgGroup wrong group size"); return 1; } private string ProcessCommandCustomArgs(string cmd) { if (string.IsNullOrEmpty(cmd)) { return ""; } int num; while ((num = cmd.IndexOf("rnd(")) != -1) { int num2 = cmd.IndexOf(")"); if (num2 == -1 || num2 <= num - "rnd(".Length) { break; } string group = cmd.Substring(num + "rnd(".Length, num2 - num - "rnd(".Length); string value = cmd.Substring(0, num); string value2 = cmd.Substring(num2 + 1); cmd = $"{value}{GetRndNumberFromArgGroup(group)}{value2}"; } return cmd; } private void ProcessEvent(string username, string cost, string eventID, string lang, string extraInfo) { if (string.IsNullOrEmpty(eventID)) { return; } LoggerFactory.Logger.Log("try to execute event: '" + eventID + "'"); try { string cmd = commandManager.GetCommandData(eventID, true)?.Trim(); string commandLine = ProcessCommandCustomArgs(cmd); ProcessCommands(commandLine, username, cost, eventID, lang, extraInfo); } catch (Exception value) { LoggerFactory.Logger.Log($"execute event unexpected error: {value}"); } } private void ProcessCommands(string commandLine, string username, string cost, string eventID, string lang, string extraInfo) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0013: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown EventsData val = new EventsData { Username = username, Cost = cost, EventID = eventID, Lang = lang, ExtraInfo = extraInfo }; try { LoggerFactory.Logger.Log("command: " + commandLine); if (console.RunCommand(commandLine, val, (Action)null)) { LoggerFactory.Logger.Log("event '" + val.EventID + "' executing is completed"); TypedNotifyOnGUI obj = notifyManager; if (obj != null) { ((TypedNotifyBase)obj).AddNotifyToQueue(val, TranslationFactory.Instance); } } else { LoggerFactory.Logger.Log("error: event '" + val.EventID + "' executing is failed"); } } catch (Exception value) { LoggerFactory.Logger.Log($"ProcessCommands '{val.EventID}' unexpected error: {value}"); } } public void Update() { try { float deltaTime = Time.deltaTime; if (GameStateServiceFactory.Instance.IsReadyForCheckEvent(deltaTime, OnCheckEventsDisabled)) { memoryReaderIsUsed |= ReadSharedMemoryEvents(); if (!memoryReaderIsUsed) { eventsReader.ReadAndProcessAllWithDelayOnFrame(deltaTime); } DebugHotkeysOnUpdate(deltaTime); } ScheduleFactory.Instance.OnUpdate(deltaTime); ((TypedNotifyBase)notifyManager).Update(); } catch (Exception value) { if (updateErrorsLimit > 0) { updateErrorsLimit--; LoggerFactory.Logger.Log($"OnUpdate error: {value}"); } } } public void OnGUI() { if (GameStateServiceFactory.Instance.IsPlayerReady && notifyManager != null) { ((TypedNotifyBase)notifyManager).OnGUI(); } } private bool ReadSharedMemoryEvents() { if (eventsParser == null) { return false; } try { bool mutexExists; string[] array = SharedMemoryReader.ReadAndErase(out mutexExists); foreach (string text in array) { EventsData val = eventsParser.ParseEvent(text); ProcessEvent(val.Username, val.Cost, val.EventID, val.Lang, val.ExtraInfo); } return mutexExists; } catch (Exception value) { if (memoryReaderLimit > 0) { memoryReaderLimit--; LoggerFactory.Logger.Log($"Read shared events error: {value}"); } } return false; } public void DebugHotkeysOnUpdate(float dt) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown try { if (UnityHelper.IsDebugMode()) { if (Input.GetKeyDown((KeyCode)281)) { LoggerFactory.Logger.Log("DebugHotkeysOnUpdate reinit mod"); ScheduleFactory.Instance.FinishAllImmediatly(); console = new UtilityConsole((HelperDebugLog)null, (List)null); InitOthersManagers(); InitCommands(isNeedValidate: true); } if (GameStateServiceFactory.Instance.IsReadyForCheckEvent(dt, OnCheckEventsDisabled) && Input.GetKeyDown((KeyCode)104)) { LoggerFactory.Logger.Log("read events by key"); eventsReaderByKey.ReadAndProcessAllOnKey(keyEventsPath, false); } } } catch (Exception value) { if (updateErrorsLimit > 0) { updateErrorsLimit--; LoggerFactory.Logger.Log($"DebugHotkeysOnUpdate error: {value}"); } } } private void OnCheckEventsDisabled() { LoggerFactory.Logger.Log("OnCheckEventsDisabled"); ScheduleFactory.Instance.FinishAllImmediatly(); if (notifyManager != null) { ((TypedNotifyBase)notifyManager).ClearNotifyQueue(); ((TypedNotifyBase)notifyManager).StopCurrentNotify(); LoggerFactory.Logger.Log("Notify is cleared"); } } } } namespace bft_mod.Service { public class GameStateService { private const int CHECK_EVENT_ERRORS_LIMIT = 50; private int checkEventErrorsLimit = 50; private const float CHECK_READY_FOR_EVENT_PERIOD = 0.2f; private float checkReadyForEventTimer; private bool lastIsReady; public bool IsPlayerReady { get { LocalPlayer instance = LocalPlayer._instance; if ((Object)(object)instance != (Object)null) { return ((Behaviour)instance).isActiveAndEnabled; } return false; } } public bool IsReadyForCheckEvent(float dt, Action onEventIsNotReady) { if (checkReadyForEventTimer >= 0f) { checkReadyForEventTimer -= dt; } else { checkReadyForEventTimer = 0.2f; } try { LocalPlayer instance = LocalPlayer._instance; bool flag = (Object)(object)instance != (Object)null && ((Behaviour)instance).isActiveAndEnabled && !SonsSceneManager.UsedLoadingScreen; if (lastIsReady && !flag) { try { onEventIsNotReady?.Invoke(); } catch (Exception value) { LoggerFactory.Logger.Log($"onEventIsNotReady error: {value}"); } } lastIsReady = flag; return flag; } catch (Exception value2) { if (checkEventErrorsLimit > 0) { checkEventErrorsLimit--; LoggerFactory.Logger.Log($"checkEventErrorsLimit error: {value2}"); } return false; } } } } namespace bft_mod.HelperBFT { public class CommandArgsReaderFactory { private static readonly Lazy instance = new Lazy((Func)(() => new CommandArgsReader((HelperLog)null))); public static CommandArgsReader Reader => instance.Value; } public class CommandArgsValidatorFactory { private static readonly Lazy instance = new Lazy((Func)(() => new CommandArgsValidator((HelperLog)null))); public static CommandArgsValidator Validator => instance.Value; } public static class Game { private static float fallDamage; public static void RunConsoleCommand(string command) { try { if ((Object)(object)DebugConsole.Instance != (Object)null && GameStateServiceFactory.Instance.IsPlayerReady) { DebugConsole.Instance.SendCommand(command); LoggerFactory.Logger.Log("RunConsoleCommand " + command + " ends"); } else { LoggerFactory.Logger.Log("Warning: GameConsole is null or player is not ready, the event activation will be skipped"); } } catch (Exception value) { LoggerFactory.Logger.Log($"RunConsoleCommand error: {value}"); } } public static void EnableFallDamage(bool isEnable) { try { LocalPlayer instance = LocalPlayer._instance; if (GameStateServiceFactory.Instance.IsPlayerReady && !((Object)(object)instance._fpc == (Object)null)) { if (isEnable && fallDamage == 0f) { fallDamage = instance._fpc._baseFallDamage; } instance._fpc._baseFallDamage = (isEnable ? 0f : fallDamage); } } catch (Exception value) { LoggerFactory.Logger.Log($"setting up fall damage error: {value}"); } } public static string GetOnOff(bool isActivate) { if (!isActivate) { return "off"; } return "on"; } public static string GetCommandWithOnOff(string cmd, bool isActivate) { return cmd + " " + GetOnOff(isActivate); } public static string GetOneZero(bool isActivate) { if (!isActivate) { return "0"; } return "1"; } public static string GetCommandWithOneZero(string cmd, bool isActivate) { return cmd + " " + GetOneZero(isActivate); } } public static class GameStateServiceFactory { private static readonly Lazy instance = new Lazy(); public static GameStateService Instance => instance.Value; } public static class LoggerFactory { public class ConsoleLogger { public virtual void Log(object obj) { Console.WriteLine(obj); } } private static readonly ConsoleLogger logger = new ConsoleLogger(); public static ConsoleLogger Logger => logger; } public static class ScheduleFactory { private static Lazy instance = GenerateLazyObject(); public static ScheduleManager Instance => instance.Value; private static Lazy GenerateLazyObject() { return new Lazy((Func)(() => new ScheduleManager((HelperLog)null))); } public static void Reinit() { if (instance.IsValueCreated) { Instance.FinishAllImmediatly(); instance = GenerateLazyObject(); } } } public static class SharedMemoryReader { private const string fname = "chaostricks_events_file"; private const string mutexName = "chaostricks_events_mutex"; private const char CODE_EOF = '\0'; private const char CODE_EVENT_MSG = '1'; private static readonly string[] emptyRes = Array.Empty(); public static string[] ReadAndErase(out bool mutexExists) { if (!Mutex.TryOpenExisting("chaostricks_events_mutex", out Mutex result)) { mutexExists = false; return Array.Empty(); } mutexExists = true; if (!result.WaitOne(0)) { return emptyRes; } try { return InternalReadAndErase().ToArray(); } catch (FileNotFoundException) { return emptyRes; } catch (EndOfStreamException) { return emptyRes; } catch (Exception value) { LoggerFactory.Logger.Log($"SharedMemoryReader error: {value}"); return emptyRes; } finally { result.ReleaseMutex(); } } private static List InternalReadAndErase() { using MemoryMappedFile memoryMappedFile = MemoryMappedFile.OpenExisting("chaostricks_events_file"); using MemoryMappedViewStream memoryMappedViewStream = memoryMappedFile.CreateViewStream(); List list = new List(); BinaryReader binaryReader = new BinaryReader(memoryMappedViewStream, Encoding.UTF8); int num; while ((num = binaryReader.PeekChar()) != -1 && num != 0) { char c = binaryReader.ReadChar(); int count = binaryReader.ReadInt32(); byte[] bytes = binaryReader.ReadBytes(count); if (c == '1') { list.Add(Encoding.UTF8.GetString(bytes)); } } memoryMappedViewStream.Seek(0L, SeekOrigin.Begin); BinaryWriter binaryWriter = new BinaryWriter(memoryMappedViewStream); binaryWriter.Write('\0'); binaryWriter.Flush(); return list; } } public static class TranslationFactory { private static Lazy instance = GenerateLazyObject(); public static HelperLanguagesFormatted Instance => instance.Value; private static Lazy GenerateLazyObject() { return new Lazy((Func)(() => new HelperLanguagesFormatted((HelperLog)null))); } public static void Reinit(bool isForceCreate = true) { if (instance.IsValueCreated) { instance = GenerateLazyObject(); } if (isForceCreate) { ForceCreate(); } } public static void ForceCreate() { _ = Instance; } } public static class UnityHelper { private static Dictionary storedNames; private static bool isDebugInited; private static bool isDebug; public static float GetCustomSize(string objectName) { if (objectName == null) { LoggerFactory.Logger.Log("GetCustomSize warning: invalid value"); return 1f; } if (storedNames == null || IsDebugMode()) { try { string text = Path.Combine(UtilitySettings.BaseFolder, "custom_sizes.data"); storedNames = UtilityJson.Generate((HelperLog)null).FromFile>(text); } catch (Exception value) { LoggerFactory.Logger.Log($"GetCustomSize error: {value}"); } if (storedNames == null) { storedNames = new Dictionary(); } } if (storedNames.ContainsKey(objectName)) { return storedNames[objectName]; } LoggerFactory.Logger.Log("GetCustomSize warning: missed value " + objectName); return 1f; } public static bool IsDebugMode() { if (isDebugInited) { return isDebug; } isDebugInited = true; isDebug = File.Exists(Path.Combine(UtilitySettings.BaseFolder, ".debug")); return isDebug; } } } namespace bft_mod.Command { public class AnimSpeedAI : BaseTimerCommandDefaultParam { public override string Id => "aianimspeed"; public AnimSpeedAI() { commands = new string[1] { "aianimspeed" }; } } public class BaseTimerCommand : ConsoleCommand { public override string Id => "base_timer_command"; public override bool Execute(IEnumerable args) { IEnumerator enumerator = args?.GetEnumerator(); int timer = CommandArgsReaderFactory.Reader.ReadInt(enumerator, 30); ActivateTimer(timer); return true; } public override bool IsValidCommandArgs(IEnumerable args) { return CommandArgsValidatorFactory.Validator.IsValidInt(args?.GetEnumerator(), 0, 500, false); } public virtual void SetEffectState(bool isActivate) { } public virtual void ActivateTimer(int timer) { //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) //IL_001d: 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_003d: Expected O, but got Unknown SetEffectState(isActivate: true); ScheduleFactory.Instance.AppendTimerTask(new TimerTaskData { id = ((ConsoleCommand)this).Id, duration = timer, finishAction = delegate { SetEffectState(isActivate: false); } }, false); } } public class BaseTimerCommandDefaultParam : BaseTimerCommand { protected float newValue; protected float oldValue; protected string[] commands; public override string Id => "base_timer_command_with_default"; public override bool Execute(IEnumerable args) { IEnumerator enumerator = args?.GetEnumerator(); CommandArgsReader reader = CommandArgsReaderFactory.Reader; int timer = reader.ReadInt(enumerator, 30); newValue = reader.ReadFloat(enumerator, 1f); oldValue = reader.ReadFloat(enumerator, 1f); ActivateTimer(timer); return true; } public override bool IsValidCommandArgs(IEnumerable args) { IEnumerator enumerator = args?.GetEnumerator(); if (CommandArgsValidatorFactory.Validator.IsValidInt(enumerator, 0, 500, false) && CommandArgsValidatorFactory.Validator.IsValidFloat(enumerator, -500f, 500f, false)) { return CommandArgsValidatorFactory.Validator.IsValidFloat(enumerator, -500f, 500f, false); } return false; } public override void SetEffectState(bool isActivate) { if (commands != null) { float value = (isActivate ? newValue : oldValue); string[] array = commands; foreach (string value2 in array) { Game.RunConsoleCommand($"{value2} {value}"); } } } } public class GameConsoleCommand : ConsoleCommand { public override string Id => "console_command"; public override bool Execute(IEnumerable args) { Game.RunConsoleCommand(string.Join(" ", args)); return true; } public override bool IsValidCommandArgs(IEnumerable args) { return true; } } public class Gravity : BaseTimerCommandDefaultParam { public override string Id => "gravity"; public Gravity() { commands = new string[1] { "gravity" }; } } public class Immortality : BaseTimerCommand { public override string Id => "god_mode"; public override void SetEffectState(bool isActivate) { Game.RunConsoleCommand(Game.GetCommandWithOnOff("godmode", isActivate)); } } public class InfiniteAmmo : BaseTimerCommand { public override string Id => "infinite_ammo"; public override void SetEffectState(bool isActivate) { Game.RunConsoleCommand(Game.GetCommandWithOnOff("ammohack", isActivate)); } } public class InfiniteEnergy : BaseTimerCommand { public override string Id => "infinite_energy"; public override void SetEffectState(bool isActivate) { Game.RunConsoleCommand(Game.GetCommandWithOnOff("energyhack", isActivate)); } } public class InvertLook : BaseTimerCommand { public override string Id => "invert_look"; public override void SetEffectState(bool isActivate) { Game.RunConsoleCommand(Game.GetCommandWithOneZero("invertlook", isActivate)); } } public class MouseSensitivity : BaseTimerCommandDefaultParam { public override string Id => "mouse_sensitivity"; public MouseSensitivity() { commands = new string[2] { "mousexsensitivity", "mouseysensitivity" }; } } public class SpeedyRun : BaseTimerCommand { public override string Id => "speedy_run"; public override void SetEffectState(bool isActivate) { Game.RunConsoleCommand(Game.GetCommandWithOnOff("speedyrun", isActivate)); Game.EnableFallDamage(isActivate); } } public class StopCommandTimer : ConsoleCommand { public override string Id => "stop_command"; public override bool Execute(IEnumerable args) { foreach (string arg in args) { if (arg != null) { ScheduleFactory.Instance.RemoveTimerTask(arg); } } return true; } public override bool IsValidCommandArgs(IEnumerable args) { return true; } } public class SuperJump : BaseTimerCommand { public override string Id => "super_jump"; public override void SetEffectState(bool isActivate) { Game.RunConsoleCommand(Game.GetCommandWithOnOff("superjump", isActivate)); Game.EnableFallDamage(isActivate); } } public class TimeScale : BaseTimerCommandDefaultParam { public override string Id => "timescale"; public TimeScale() { commands = new string[1] { "timescale" }; } } }