#define DEBUG_LOGGING using UnityEngine; using System.Collections.Generic; using Unity.Entities; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using System; using System.Globalization; using SQP; using System.Reflection; using System.Linq; using Unity.DebugDisplay; using Unity.Sample.Core; using UnityEngine.Profiling; #if UNITY_EDITOR using UnityEditorInternal; using UnityEditor; using Unity.NetCode.Editor; #endif public class EnumeratedArrayAttribute : PropertyAttribute { public readonly string[] names; public EnumeratedArrayAttribute(Type enumtype) { names = Enum.GetNames(enumtype); } } [ExecuteAlways] [DisableAutoCreation] public class ControlledEntityCameraUpdate : ManualComponentSystemGroup { protected override void OnUpdate() { Profiler.BeginSample("ControlledEntityCameraUpdate"); base.OnUpdate(); Profiler.EndSample(); } } [DefaultExecutionOrder(-1000)] public class Game : MonoBehaviour { public delegate void UpdateDelegate(); public WeakAssetReference movableBoxPrototype; public WeakAssetReference dotsNetCodePrefabs; [AssetType(typeof(SoundDef))] public WeakAssetReference soundTrackTest; // Color scheme configurable? (cvars?) public enum GameColor { Friend, Enemy } [EnumeratedArray(typeof(GameColor))] public Color[] gameColors; public GameStatistics m_GameStatistics { get; private set; } public interface IGameLoop { bool Init(string[] args); void Shutdown(); void Update(); void FixedUpdate(); void LateUpdate(); } public static Game game; public event UpdateDelegate endUpdateEvent; // Vars owned by server and replicated to clients [ConfigVar(Name = "server.tickrate", DefaultValue = "60", Description = "Tickrate for server", Flags = ConfigVar.Flags.ServerInfo)] public static ConfigVar serverTickRate; // [ConfigVar(Name = "config.fov", DefaultValue = "60", Description = "Field of view", Flags = ConfigVar.Flags.Save)] // public static ConfigVar configFov; [ConfigVar(Name = "debug.catchloop", DefaultValue = "1", Description = "Catch exceptions in gameloop and pause game", Flags = ConfigVar.Flags.None)] public static ConfigVar debugCatchLoop; [ConfigVar(Name = "chartype", DefaultValue = "-1", Description = "Character to start with (-1 uses default character)")] public static ConfigVar characterType; [ConfigVar(Name = "allowcharchange", DefaultValue = "1", Description = "Is changing character allowed")] public static ConfigVar allowCharChange; [ConfigVar(Name = "debug.cpuprofile", DefaultValue = "0", Description = "Profile and dump cpu usage")] public static ConfigVar debugCpuProfile; [ConfigVar(Name = "net.dropevents", DefaultValue = "0", Description = "Drops a fraction of all packages containing events!!")] public static ConfigVar netDropEvents; [ConfigVar(Name = "show.entities", DefaultValue = "0", Description = "Entity stats")] public static ConfigVar showEntities; static readonly string k_UserConfigFilename = "user.cfg"; public static readonly string k_BootConfigFilename = "boot.cfg"; public UnityEngine.Audio.AudioMixer audioMixer; public Camera bootCamera; public LevelManager levelManager; public SQPClient sqpClient; public static double frameTime; public static int frameCount; public static bool IsHeadless() { return game.m_isHeadless; } public static int GameLoopCount { get { return game == null ? 0 : 1; } } public static T GetGameLoop() where T : class { if (game == null) return null; foreach (var gameLoop in game.m_gameLoops) { T result = gameLoop as T; if (result != null) return result; } return null; } public static System.Diagnostics.Stopwatch Clock { get { return game.m_Clock; } } public string buildId { get { return _buildId; } } string _buildId = "NoBuild"; public string buildUnityVersion { get { return _buildUnityVersion; } } // Start with sensible default, but we would like to have the full build version // which is only available in editor so we bake it into the build. string _buildUnityVersion = Application.unityVersion; public void RequestGameLoop(System.Type type, string[] args) { GameDebug.Assert(typeof(IGameLoop).IsAssignableFrom(type)); m_RequestedGameLoopTypes.Add(type); m_RequestedGameLoopArguments.Add(args); GameDebug.Log("Game loop " + type + " requested"); } // Pick argument for argument(!). Given list of args return null if option is // not found. Return argument following option if found or empty string if none given. // Options are expected to be prefixed with + or - public static string ArgumentForOption(List args, string option) { var idx = args.IndexOf(option); if (idx < 0) return null; if (idx < args.Count - 1) return args[idx + 1]; return ""; } /* * THIS CAUSES RANDOM CRASHES * private static void PreLoadAnimationNodes() { // In Editor, convince Burst to JIT compile all Animation UNode types immediately. #if UNITY_EDITOR var start = UnityEngine.Time.realtimeSinceStartup; var wasSyncCompile = Menu.GetChecked("Jobs/Burst/Synchronous Compilation"); Menu.SetChecked("Jobs/Burst/Synchronous Compilation", true); try { var animAssembly = typeof(Unity.Animation.AnimationGraphSystem).Assembly; var unodeNonGenericTypes = animAssembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType && typeof(Unity.DataFlowGraph.INodeFunctionality).IsAssignableFrom(t)); var unodeGenericTypesReferencedInData = animAssembly.GetTypes() .Where(t => !t.IsClass && typeof(Unity.DataFlowGraph.INodeData).IsAssignableFrom(t)) .Select(t => t .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Select(fi => fi.FieldType) .Where(ft => ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(Unity.DataFlowGraph.NodeHandle<>)) .Select(ft => ft.GetGenericArguments()[0]) .Where(ft => ft.IsGenericType)) .SelectMany(t => t) .Distinct(); var nodeSet = new Unity.DataFlowGraph.NodeSet(); MethodInfo getFunctionality = nodeSet.GetType().GetMethod("GetFunctionality", new System.Type[0]); foreach (System.Type t in unodeNonGenericTypes.Concat(unodeGenericTypesReferencedInData)) getFunctionality.MakeGenericMethod(t).Invoke(nodeSet, null); nodeSet.Dispose(); Debug.Log($"Compilation of Animation UNode types took {UnityEngine.Time.realtimeSinceStartup - start} seconds"); } finally { Menu.SetChecked("Jobs/Burst/Synchronous Compilation", wasSyncCompile); } #endif } */ public void Awake() { GameDebug.Assert(game == null); DontDestroyOnLoad(gameObject); game = this; GameApp.IsInitialized = true; //PreLoadAnimationNodes(); m_StopwatchFrequency = System.Diagnostics.Stopwatch.Frequency; m_Clock = new System.Diagnostics.Stopwatch(); m_Clock.Start(); #if UNITY_EDITOR _buildUnityVersion = InternalEditorUtility.GetFullUnityVersion(); #endif var buildInfo = FindObjectOfType(); if (buildInfo != null) { _buildId = buildInfo.buildId; _buildUnityVersion = buildInfo.buildUnityVersion; } var commandLineArgs = new List(System.Environment.GetCommandLineArgs()); // TODO we should only initialize this if we have a graphics device (i.e. non-headless) Overlay.Managed.Initialize(); #if UNITY_STANDALONE_LINUX m_isHeadless = true; #else m_isHeadless = commandLineArgs.Contains("-batchmode"); #endif var noconsole = commandLineArgs.Contains("-noconsole"); var consoleRestoreFocus = commandLineArgs.Contains("-consolerestorefocus"); if (noconsole) { UnityEngine.Debug.Log("WARNING: starting without a console"); var consoleUI = new ConsoleNullUI(); Console.Init(buildId,buildUnityVersion, consoleUI); }else if (m_isHeadless) { #if UNITY_EDITOR Debug.LogError("ERROR: Headless mode not supported in editor"); #endif #if UNITY_STANDALONE_WIN string consoleTitle; var overrideTitle = ArgumentForOption(commandLineArgs, "-title"); if (overrideTitle != null) consoleTitle = overrideTitle; else consoleTitle = Application.productName + " Console"; consoleTitle += " [" + System.Diagnostics.Process.GetCurrentProcess().Id + "]"; var consoleUI = new ConsoleTextWin(consoleTitle, consoleRestoreFocus); #elif UNITY_STANDALONE_LINUX var consoleUI = new ConsoleTextLinux(); #else UnityEngine.Debug.Log("WARNING: starting without a console"); var consoleUI = new ConsoleNullUI(); #endif Console.Init(buildId,buildUnityVersion,consoleUI); } else { var consoleUI = Instantiate(Resources.Load("Prefabs/ConsoleGUI")); DontDestroyOnLoad(consoleUI); Console.Init(buildId,buildUnityVersion,consoleUI); m_DebugOverlay = Instantiate(Resources.Load("DebugOverlay")); DontDestroyOnLoad(m_DebugOverlay); m_DebugOverlay.Init(); m_GameStatistics = new GameStatistics(); } // If -logfile was passed, we try to put our own logs next to the engine's logfile // if -logfile was set to "-" we forward our logs to Debug.Log, so that it ends up on stdout. var engineLogFileLocation = "."; var logName = m_isHeadless ? "game_" + DateTime.UtcNow.ToString("yyyyMMdd_HHmmss_fff") : "game"; var logfileArgIdx = commandLineArgs.IndexOf("-logfile"); var forceForwardToDebug = false; if (logfileArgIdx >= 0 && commandLineArgs.Count >= logfileArgIdx) { var logFile = commandLineArgs[logfileArgIdx + 1]; if (logFile == "-") forceForwardToDebug = true; else engineLogFileLocation = System.IO.Path.GetDirectoryName(logFile); } GameDebug.Init(engineLogFileLocation, logName, forceForwardToDebug); ConfigVar.Init(); // Support -port and -query_port as per Multiplay standard var serverPort = ArgumentForOption(commandLineArgs, "-port"); if (serverPort != null) Console.EnqueueCommandNoHistory("server.port " + serverPort); var sqpPort = ArgumentForOption(commandLineArgs, "-query_port"); if (sqpPort != null) Console.EnqueueCommandNoHistory("server.sqp_port " + sqpPort); Console.EnqueueCommandNoHistory("exec -s " + k_UserConfigFilename); // Default is to allow no frame cap, i.e. as fast as possible if vsync is disabled Application.targetFrameRate = -1; if (m_isHeadless) { Application.targetFrameRate = serverTickRate.IntValue; QualitySettings.vSyncCount = 0; // Needed to make targetFramerate work; even in headless mode #if !UNITY_STANDALONE_LINUX if (!commandLineArgs.Contains("-nographics")) GameDebug.Log("WARNING: running -batchmod without -nographics"); #endif } else { RenderSettings.Init(); } // Out of the box game behaviour is driven by boot.cfg unless you ask it not to if (!commandLineArgs.Contains("-noboot")) { Console.EnqueueCommandNoHistory("exec -s " + k_BootConfigFilename); } if (m_isHeadless) { SoundSystem.Initialize(new SoundSystemNull()); } else { var soundSystem = new SoundSystemBase(); soundSystem.Init(audioMixer); SoundSystem.Initialize(soundSystem); GameObject go = (GameObject)GameObject.Instantiate(Resources.Load("Prefabs/ClientFrontend", typeof(GameObject))); UnityEngine.Object.DontDestroyOnLoad(go); clientFrontend = go.GetComponentInChildren(); } sqpClient = new SQP.SQPClient(); GameDebug.Log("A2 initialized"); #if UNITY_EDITOR GameDebug.Log("Build type: editor"); #elif DEVELOPMENT_BUILD GameDebug.Log("Build type: development"); #else GameDebug.Log("Build type: release"); #endif GameDebug.Log("BuildID: " + buildId); GameDebug.Log("Unity: " + buildUnityVersion); GameDebug.Log("Cwd: " + System.IO.Directory.GetCurrentDirectory()); levelManager = new LevelManager(); levelManager.Init(); GameDebug.Log("LevelManager initialized"); GameDebug.Log("InputSystem initialized"); // Game loops Console.AddCommand("serve", CmdServe, "Start server listening"); Console.AddCommand("client", CmdClient, "client: Enter client mode."); Console.AddCommand("thinclient", CmdThinClient, "client: Enter thin client mode."); Console.AddCommand("boot", CmdBoot, "Go back to boot loop"); Console.AddCommand("connect", CmdConnect, "connect : Connect to server on ip (default: localhost)"); Console.AddCommand("menu", CmdMenu, "show the main menu"); Console.AddCommand("load", CmdLoad, "Load level"); Console.AddCommand("quit", CmdQuit, "Quits"); Console.AddCommand("screenshot", CmdScreenshot, "Capture screenshot. Optional argument is destination folder or filename."); Console.AddCommand("crashme", (string[] args) => { GameDebug.Assert(false); }, "Crashes the game next frame "); Console.AddCommand("saveconfig", CmdSaveConfig, "Save the user config variables"); Console.AddCommand("loadconfig", CmdLoadConfig, "Load the user config variables"); Console.AddCommand("profile", CmdProfile, "Run the profiling for a level"); #if UNITY_STANDALONE_WIN Console.AddCommand("windowpos", CmdWindowPosition, "Position of window. e.g. windowpos 100,100"); #endif Console.SetOpen(true); Console.ProcessCommandLineArguments(commandLineArgs.ToArray()); #if UNITY_IOS // (marton) This is a hack to work around command line arguments not working on iOS if (!Application.isEditor) Console.EnqueueCommandNoHistory("preview Level_00"); #endif GameApp.CameraStack.OnCameraEnabledChanged += OnCameraEnabledChanged; GameApp.CameraStack.PushCamera(bootCamera); } void OnDisable() { GameDebug.Shutdown(); Overlay.Managed.DoShutdown(); Console.Shutdown(); game = null; GameApp.IsInitialized = false; InputSystem.SetMousePointerLock(false); GameDebug.Log("A2 was shutdown"); } bool pipeSetup = false; public void Update() { if (!m_isHeadless) RenderSettings.Update(); // TODO (petera) remove this hack once we know exactly when renderer is available... if (!pipeSetup) { var hdpipe = RenderPipelineManager.currentPipeline as HDRenderPipeline; if (hdpipe != null) { var layer = LayerMask.NameToLayer("PostProcess Volumes"); if (layer == -1) GameDebug.LogWarning("Unable to find layer mask for camera fader"); else { var gameObject = new GameObject() { name = "Game Quick Volume", layer = layer, hideFlags = HideFlags.HideAndDontSave }; m_ExposureVolume = gameObject.AddComponent(); m_ExposureVolume.priority = 100.0f; m_ExposureVolume.isGlobal = true; var profile = m_ExposureVolume.profile; m_Exposure = profile.Add(); m_Exposure.active = false; m_Exposure.mode.Override(ExposureMode.Automatic); m_Exposure.compensation.Override(0); } pipeSetup = true; } } if (m_ExposureReleaseCount > 0) { m_ExposureReleaseCount--; if (m_ExposureReleaseCount == 0) BlackFade(false); } GameApp.CameraStack.Update(); #if UNITY_EDITOR // Ugly hack to force focus to game view when using scriptable renderloops. if (Time.frameCount < 4) { try { var gameViewType = typeof(UnityEditor.EditorWindow).Assembly.GetType("UnityEditor.GameView"); var gameView = (EditorWindow)Resources.FindObjectsOfTypeAll(gameViewType)[0]; gameView.Focus(); } catch (System.Exception) { /* too bad */ } } #endif frameTime = (double)m_Clock.ElapsedTicks / m_StopwatchFrequency; frameCount = Time.frameCount; GameDebug.SetFrameCount(frameCount); Console.SetFrameTime(frameTime);; // Switch game loop if needed if (m_RequestedGameLoopTypes.Count > 0) { // Multiple running gameloops only allowed in editor #if !UNITY_EDITOR ShutdownGameLoops(); #endif bool initSucceeded = false; for (int i = 0; i < m_RequestedGameLoopTypes.Count; i++) { try { IGameLoop gameLoop = (IGameLoop)System.Activator.CreateInstance(m_RequestedGameLoopTypes[i]); initSucceeded = gameLoop.Init(m_RequestedGameLoopArguments[i]); Debug.Log("Game initialization succeeded: " + initSucceeded); if (!initSucceeded) break; m_gameLoops.Add(gameLoop); } catch (System.Exception e) { GameDebug.LogError(string.Format("Game loop initialization threw exception : ({0})\n{1}", e.Message, e.StackTrace)); } } if (!initSucceeded) { ShutdownGameLoops(); GameDebug.LogError("Game loop initialization failed ... reverting to boot loop"); } m_RequestedGameLoopTypes.Clear(); m_RequestedGameLoopArguments.Clear(); } try { if (!m_ErrorState) { foreach (var gameLoop in m_gameLoops) { gameLoop.Update(); } levelManager.Update(); } } catch (System.Exception e) { HandleGameloopException(e); throw; } if (SoundSystem.Instance != null) SoundSystem.Instance.Update(); if (clientFrontend != null) clientFrontend.UpdateGame(); Console.ConsoleUpdate(); bool menusShowing = (clientFrontend != null && clientFrontend.menuShowing != ClientFrontend.MenuShowing.None); InputSystem.WindowFocusUpdate(menusShowing); UpdateCPUStats(); UpdateEntityStats(); sqpClient.Update(); endUpdateEvent?.Invoke(); /* HACKY WAY TO LOOK AT MONOBEHAVIOURS LEFT if (Time.frameCount % 1000 == 100) { var mbs = FindObjectsOfType(typeof(MonoBehaviour)); numMBS = mbs.Length; var h = new HashSet(); foreach (var mb in mbs) { h.Add(mb.GetType()); } foreach(var t in h) GameDebug.Log(":" + t); numUMBS = h.Count(); } Overlay.Managed.Write(2, 4, "Monobehaviours left {0} ({1})", numMBS, numUMBS); */ } //int numMBS = 0; //int numUMBS = 0; bool m_ErrorState; public void FixedUpdate() { foreach (var gameLoop in m_gameLoops) { gameLoop.FixedUpdate(); } } public void LateUpdate() { try { if (!m_ErrorState) { foreach (var gameLoop in m_gameLoops) { gameLoop.LateUpdate(); } Console.ConsoleLateUpdate(); } } catch (System.Exception e) { HandleGameloopException(e); throw; } if (m_GameStatistics != null) m_GameStatistics.TickLateUpdate(); if (m_DebugOverlay != null) m_DebugOverlay.TickLateUpdate(); Unity.DebugDisplay.Overlay.Managed.instance.TickLateUpdate(); } void OnApplicationQuit() { ShutdownGameLoops(); } void BlackFade(bool enabled) { if (m_Exposure != null) m_Exposure.active = enabled; } void OnCameraEnabledChanged(Camera camera, bool enabled) { if (enabled) RenderSettings.UpdateCameraSettings(camera); var audioListener = camera.GetComponent(); if (audioListener != null) { audioListener.enabled = enabled; if (SoundSystem.Instance != null) SoundSystem.Instance.SetCurrentListener(enabled ? audioListener : null); } if(enabled) m_ExposureReleaseCount = 10; } float m_NextCpuProfileTime = 0; double m_LastCpuUsage = 0; double m_LastCpuUsageUser = 0; void UpdateCPUStats() { if (debugCpuProfile.IntValue > 0) { if (Time.time > m_NextCpuProfileTime) { const float interval = 5.0f; m_NextCpuProfileTime = Time.time + interval; var process = System.Diagnostics.Process.GetCurrentProcess(); var user = process.UserProcessorTime.TotalMilliseconds; var total = process.TotalProcessorTime.TotalMilliseconds; float userUsagePct = (float)(user - m_LastCpuUsageUser) / 10.0f / interval; float totalUsagePct = (float)(total - m_LastCpuUsage) / 10.0f / interval; m_LastCpuUsage = total; m_LastCpuUsageUser = user; GameDebug.Log(string.Format("CPU Usage {0}% (user: {1}%)", totalUsagePct, userUsagePct)); } } } void UpdateEntityStats() { if (showEntities.IntValue <= 0) return; int y = 10; var aw = World.AllWorlds; Overlay.Managed.Write(2, y++, "Worlds: {0}", aw.Count); foreach(var w in aw) { Overlay.Managed.Write(3, y++, "{0}: {1} ents {2} sys", w.Name, w.EntityManager.UniversalQuery.CalculateEntityCountWithoutFiltering(), w.Systems.Count()); } } public void LoadLevel(string levelname) { if (!Game.game.levelManager.CanLoadLevel(levelname)) { GameDebug.Log("ERROR : Cannot load level : " + levelname); return; } Game.game.levelManager.LoadLevel(levelname); } void UnloadLevel() { // TODO } void HandleGameloopException(System.Exception e) { if (debugCatchLoop.IntValue > 0) { GameDebug.Log("EXCEPTION " + e.Message + "\n" + e.StackTrace); Console.SetOpen(true); m_ErrorState = true; } } string FindNewFilename(string pattern) { for (var i = 0; i < 10000; i++) { var f = string.Format(pattern, i); if (System.IO.File.Exists(string.Format(pattern, i))) continue; return f; } return null; } void ShutdownGameLoops() { foreach (var gameLoop in m_gameLoops) gameLoop.Shutdown(); m_gameLoops.Clear(); } void CmdServe(string[] args) { RequestGameLoop(typeof(ServerGameLoop) , args); Console.s_PendingCommandsWaitForFrames = 1; } void CmdLoad(string[] args) { LoadLevel(args[0]); Console.SetOpen(false); } void CmdProfile(string[] args) { GameDebug.Log("ProfileGameLoop not available"); return; //RequestGameLoop(typeof(ProfileGameLoop), args); //Console.s_PendingCommandsWaitForFrames = 1; } void CmdBoot(string[] args) { clientFrontend.ShowMenu(ClientFrontend.MenuShowing.None); levelManager.UnloadLevel(); ShutdownGameLoops(); Console.s_PendingCommandsWaitForFrames = 1; Console.SetOpen(true); } void CmdClient(string[] args) { RequestGameLoop(typeof(ClientGameLoop), args); Console.s_PendingCommandsWaitForFrames = 1; } void CmdConnect(string[] args) { // Special hack to allow "connect a.b.c.d" as shorthand if (m_gameLoops.Count == 0) { RequestGameLoop(typeof(ClientGameLoop), args); Console.s_PendingCommandsWaitForFrames = 1; return; } ClientGameLoop clientGameLoop = GetGameLoop(); ThinClientGameLoop thinClientGameLoop = GetGameLoop(); if (clientGameLoop != null) clientGameLoop.CmdConnect(args); else if (thinClientGameLoop != null) thinClientGameLoop.CmdConnect(args); else GameDebug.Log("Cannot connect from current gamemode"); } void CmdThinClient(string[] args) { RequestGameLoop(typeof(ThinClientGameLoop), args); Console.s_PendingCommandsWaitForFrames = 1; } void CmdQuit(string[] args) { #if UNITY_EDITOR EditorApplication.isPlaying = false; #else Application.Quit(); #endif } void CmdScreenshot(string[] arguments) { string filename = null; var root = System.IO.Path.GetFullPath("."); if (arguments.Length == 0) filename = FindNewFilename(root + "/screenshot{0}.png"); else if (arguments.Length == 1) { var a = arguments[0]; if (System.IO.Directory.Exists(a)) filename = FindNewFilename(a + "/screenshot{0}.png"); else if (!System.IO.File.Exists(a)) filename = a; else { Console.Write("File " + a + " already exists"); return; } } if (filename != null) { GameDebug.Log("Saving screenshot to " + filename); Console.SetOpen(false); ScreenCapture.CaptureScreenshot(filename); } } public ClientFrontend clientFrontend; private void CmdMenu(string[] args) { float fadeTime = 0.0f; ClientFrontend.MenuShowing show = ClientFrontend.MenuShowing.Main; if (args.Length > 0) { if (args[0] == "0") show = ClientFrontend.MenuShowing.None; else if (args[0] == "2") show = ClientFrontend.MenuShowing.Ingame; } if (args.Length > 1) { float.TryParse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out fadeTime); } clientFrontend.ShowMenu(show, fadeTime); Console.SetOpen(false); } void CmdSaveConfig(string[] arguments) { ConfigVar.Save(k_UserConfigFilename); } void CmdLoadConfig(string[] arguments) { Console.EnqueueCommandNoHistory("exec " + k_UserConfigFilename); } #if UNITY_STANDALONE_WIN void CmdWindowPosition(string[] arguments) { if (arguments.Length == 1) { string[] cords = arguments[0].Split(','); if (cords.Length == 2) { int x, y; var xParsed = int.TryParse(cords[0], out x); var yParsed = int.TryParse(cords[1], out y); if (xParsed && yParsed) { WindowsUtil.SetWindowPosition(x, y); return; } } } Console.Write("Usage: windowpos "); } #endif List m_RequestedGameLoopTypes = new List(); private List m_RequestedGameLoopArguments = new List(); List m_gameLoops = new List(); DebugOverlay m_DebugOverlay; bool m_isHeadless; long m_StopwatchFrequency; System.Diagnostics.Stopwatch m_Clock; // Global camera handling Exposure m_Exposure; Volume m_ExposureVolume; int m_ExposureReleaseCount; }