namespace Matchmaker.Server.BaseServer; internal static class ThreadManager { private static readonly List ExecuteOnMainThreadList = new(); private static readonly List ExecuteCopiedOnMainThread = new(); private static bool _actionToExecuteOnMainThread; /// /// Controls whether or not the Main Thread should run. Set to FALSE to shut down Main Thread. /// private static bool _isRunning; private static Thread? _thread; /// Sets an action to be executed on the main thread. /// The action to be executed on the main thread. public static void ExecuteOnMainThread(Action action) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (action == null) { Terminal.LogWarn("No action to execute on main thread!"); return; } lock (ExecuteOnMainThreadList) { ExecuteOnMainThreadList.Add(action); _actionToExecuteOnMainThread = true; } } /// /// Start the Main Thread so Packets can be acted upon /// public static void Start() { _isRunning = true; _thread = new Thread(MainThread); _thread.Start(); } /// /// Stop the Main Thread /// public static void Stop() { _isRunning = false; } /// /// Executes all code meant to run on the main thread. NOTE: Call this ONLY from the main thread. /// public static void UpdateMain() { if (!_actionToExecuteOnMainThread) return; ExecuteCopiedOnMainThread.Clear(); lock (ExecuteOnMainThreadList) { ExecuteCopiedOnMainThread.AddRange(ExecuteOnMainThreadList); ExecuteOnMainThreadList.Clear(); _actionToExecuteOnMainThread = false; } foreach (var t in ExecuteCopiedOnMainThread) { t(); } } private static void MainThread() { var nextLoop = DateTime.Now; while (_isRunning) { while (nextLoop < DateTime.Now) { GameLogic.Update(); nextLoop = nextLoop.AddMilliseconds(Constants.MsPerTick); if (nextLoop > DateTime.Now) { Thread.Sleep(nextLoop - DateTime.Now); } } } } }