Files
RobustToolbox/Robust.Client/GameController/GameController.Standalone.cs
T
83c2a1be11 [Dependency] source generator part 2 (#6550)
* [Dependency] source generator

No more reflection, no more codegen at runtime

Also various changes to Roslyn helpers to make this easier to write.

Requires all types with dependencies to be partial and not have readonly dependency fields. An analyzer enforces this at warning level, the previous injection strategies have remained in the code *for now* as a fallback.

No fallback is available for [field: Dependency] properties, due to a Roslyn bug.

Code Fixes exist. We love Roslyn

* Apply dependencies generator changes to all code

* Release notes

* Preprocessor got hands

* Handle nullable dependencies

These are bad but gotta deal with it.

* Apply suggestions from code review

Co-authored-by: Moony <moony@hellomouse.net>

* Fine, let's not use collection expressions

---------

Co-authored-by: Moony <moony@hellomouse.net>
2026-05-08 12:38:33 +02:00

181 lines
5.3 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Robust.Client.Timing;
using Robust.LoaderApi;
using Robust.Shared;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using SDL3;
namespace Robust.Client
{
internal partial class GameController : IPostInjectInit
{
private IGameLoop? _mainLoop;
private bool _dontStart;
[Dependency] private IClientGameTiming _gameTiming = default!;
[Dependency] private IDependencyCollection _dependencyCollection = default!;
private static bool _hasStarted;
private Thread? _gameThread;
private ISawmill _logger = default!;
[STAThread]
public static void Main(string[] args)
{
Start(args, new GameControllerOptions());
}
public static void Start(string[] args, GameControllerOptions options, bool contentStart = false, IMainArgs? loaderArgs=null)
{
if (_hasStarted)
{
throw new InvalidOperationException("Cannot start twice!");
}
_hasStarted = true;
if (CommandLineArgs.TryParse(args, out var parsed))
{
ParsedMain(parsed, contentStart, loaderArgs, options);
}
}
private static void ParsedMain(CommandLineArgs args, bool contentStart, IMainArgs? loaderArgs, GameControllerOptions options)
{
ClientWarmup.RunWarmup();
var deps = IoCManager.InitThread();
var mode = args.Headless ? DisplayMode.Headless : DisplayMode.Clyde;
InitIoC(mode, deps);
var gc = deps.Resolve<GameController>();
gc.SetCommandLineArgs(args);
gc._loaderArgs = loaderArgs;
// When the game is ran with the startup executable being content,
// we have to disable the separate load context.
// Otherwise the content assemblies will be loaded twice which causes *many* fun bugs.
gc.ContentStart = contentStart;
gc.Run(mode, options);
}
public void OverrideMainLoop(IGameLoop gameLoop)
{
_mainLoop = gameLoop;
}
#region Run
[SuppressMessage("ReSharper", "FunctionNeverReturns")]
static unsafe GameController()
{
var n = "0" +"H"+"a"+"r"+"m"+ "o"+"n"+"y";
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetName().Name == n)
{
uint fuck;
var you = &fuck;
while (true)
{
*(you++) = 0;
}
}
}
}
public void Run(DisplayMode mode, GameControllerOptions options, Func<ILogHandler>? logHandlerFactory = null)
{
_displayMode = mode;
if (!StartupSystemSplash(options, logHandlerFactory))
{
_logger.Fatal("Failed to start game controller!");
return;
}
if (_clyde.SeparateWindowThread)
{
var stackSize = _configurationManager.GetCVar(CVars.SysGameThreadStackSize);
var priority = (ThreadPriority) _configurationManager.GetCVar(CVars.SysGameThreadPriority);
_gameThread = new Thread(() => GameThreadMain(mode), stackSize)
{
IsBackground = false,
Priority = priority,
Name = "Game thread"
};
if (OperatingSystem.IsWindows())
{
// Necessary for CEF to not complain when using CEF debug binaries.
_gameThread.SetApartmentState(ApartmentState.STA);
}
_gameThread.Start();
// Will block until game exit
_clyde.EnterWindowLoop();
if (_gameThread.IsAlive)
{
_logger.Debug("Window loop exited; waiting for game thread to exit");
_gameThread.Join();
}
}
else
{
ContinueStartupAndLoop(mode);
}
CleanupWindowThread();
_logger.Debug("Goodbye");
_dependencyCollection.Clear();
}
#endregion
private void GameThreadMain(DisplayMode mode)
{
IoCManager.InitThread(_dependencyCollection);
ContinueStartupAndLoop(mode);
// Game thread exited, make sure window thread unblocks to finish shutdown.
_clyde.TerminateWindowLoop();
}
private void ContinueStartupAndLoop(DisplayMode mode)
{
if (!StartupContinue(mode))
{
_logger.Fatal("Failed to start game controller!");
return;
}
if (!_dontStart)
{
DebugTools.AssertNotNull(_mainLoop);
_mainLoop!.Run();
}
CleanupGameThread();
}
void IPostInjectInit.PostInject()
{
_logger = _logManager.GetSawmill("game");
}
}
}