您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
929 行
29 KiB
929 行
29 KiB
#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<T>() 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<string> 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<BuildInfo>();
|
|
if (buildInfo != null)
|
|
{
|
|
_buildId = buildInfo.buildId;
|
|
_buildUnityVersion = buildInfo.buildUnityVersion;
|
|
}
|
|
|
|
var commandLineArgs = new List<string>(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<ConsoleGUI>("Prefabs/ConsoleGUI"));
|
|
DontDestroyOnLoad(consoleUI);
|
|
Console.Init(buildId,buildUnityVersion,consoleUI);
|
|
|
|
m_DebugOverlay = Instantiate(Resources.Load<DebugOverlay>("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<ClientFrontend>();
|
|
}
|
|
|
|
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 <ip>: 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<Volume>();
|
|
m_ExposureVolume.priority = 100.0f;
|
|
m_ExposureVolume.isGlobal = true;
|
|
var profile = m_ExposureVolume.profile;
|
|
|
|
m_Exposure = profile.Add<Exposure>();
|
|
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<Type>();
|
|
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<AudioListener>();
|
|
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<ComponentSystemBase>());
|
|
}
|
|
}
|
|
|
|
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<ClientGameLoop>();
|
|
ThinClientGameLoop thinClientGameLoop = GetGameLoop<ThinClientGameLoop>();
|
|
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 <x,y>");
|
|
}
|
|
|
|
#endif
|
|
|
|
List<Type> m_RequestedGameLoopTypes = new List<System.Type>();
|
|
private List<string[]> m_RequestedGameLoopArguments = new List<string[]>();
|
|
|
|
List<IGameLoop> m_gameLoops = new List<IGameLoop>();
|
|
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;
|
|
|
|
}
|
|
|