From 75f151543f5945655a32d2e5db3112342bac8a49 Mon Sep 17 00:00:00 2001 From: Donovan Prezeau Date: Thu, 8 May 2025 15:04:01 -0600 Subject: [PATCH 01/37] initial PoC --- build/Stride.sln | 15 ++++ sources/Directory.Packages.props | 4 +- .../Engine/Builder/GameBuilder.cs | 51 +++++++++++++ .../Engine/Builder/GameBuilderExtensions.cs | 72 +++++++++++++++++++ .../Engine/Builder/IGameBuilder.cs | 16 +++++ .../Engine/Builder/MinimalGame.cs | 43 +++++++++++ .../Engine/Design/GameSettings.cs | 2 - sources/engine/Stride.Games/GameBase.cs | 4 +- .../Stride.Games/GraphicsDeviceManager.cs | 2 +- sources/engine/Stride.Games/IGame.cs | 1 - sources/engine/Stride.Hosting/BasicGame.cs | 42 +++++++++++ .../Stride.Hosting/IStrideGameBuilder.cs | 16 +++++ .../Stride.Hosting/Stride.Hosting.csproj | 28 ++++++++ .../Stride.Hosting/StrideGameBuilder.cs | 39 ++++++++++ 14 files changed, 328 insertions(+), 7 deletions(-) create mode 100644 sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs create mode 100644 sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs create mode 100644 sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs create mode 100644 sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs create mode 100644 sources/engine/Stride.Hosting/BasicGame.cs create mode 100644 sources/engine/Stride.Hosting/IStrideGameBuilder.cs create mode 100644 sources/engine/Stride.Hosting/Stride.Hosting.csproj create mode 100644 sources/engine/Stride.Hosting/StrideGameBuilder.cs diff --git a/build/Stride.sln b/build/Stride.sln index 65dbc5b24f..969e630fb4 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -338,6 +338,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.3D", "..\so EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.BepuPhysics.Tests", "..\sources\engine\Stride.BepuPhysics\Stride.BepuPhysics.Tests\Stride.BepuPhysics.Tests.csproj", "{7B70C783-4085-4702-B3C6-6570FD85CB8F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Hosting", "..\sources\engine\Stride.Hosting\Stride.Hosting.csproj", "{A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1523,6 +1525,18 @@ Global {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.ActiveCfg = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.Build.0 = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.ActiveCfg = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.Build.0 = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.Build.0 = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.ActiveCfg = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1652,6 +1666,7 @@ Global {7715D094-DF59-4D91-BC9A-9A5118039ECB} = {DE048114-9AE4-467E-A879-188DC0D88A59} {66EFFDE4-24F0-4E57-9618-0F5577E20A1E} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} {7B70C783-4085-4702-B3C6-6570FD85CB8F} = {DE048114-9AE4-467E-A879-188DC0D88A59} + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715} = {4C142567-C42B-40F5-B092-798882190209} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 28607e25a4..633d58c70b 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -11,6 +11,8 @@ + + @@ -144,4 +146,4 @@ - + \ No newline at end of file diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs new file mode 100644 index 0000000000..dd5a1b202d --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Games; + +namespace Stride.Engine.Builder; + +/// +/// Helps build the game and preps it to be able to run after built. +/// +/// +public class GameBuilder : IGameBuilder where T : IGame +{ + public IServiceRegistry Services { get; protected set; } + + public GameSystemCollection GameSystems { get; protected set; } + + public List LogListeners { get; protected set; } = []; + + public GameBase Game { get; protected set; } + + internal GameBuilder() + { + Game = new MinimalGame(); + Services = Game.Services; + GameSystems = Game.GameSystems; + } + + public static GameBuilder Create() + { + return new GameBuilder(); + } + + public virtual GameBase Build() + { + foreach (var logListener in LogListeners) + { + GlobalLogger.GlobalMessageLogged += logListener; + } + + return Game; + } +} + +/// +/// Creates a default GameBuilder for a . +/// +public class GameBuilder : GameBuilder +{ + +} diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs new file mode 100644 index 0000000000..ead167468a --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -0,0 +1,72 @@ +using System; +using Stride.Core.Diagnostics; +using Stride.Core.IO; +using Stride.Core.Storage; +using Stride.Games; +using Stride.Rendering; +using Stride.Shaders.Compiler; + +namespace Stride.Engine.Builder; +public static class GameBuilderExtensions +{ + public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gameSystem) where T : IGameSystemBase + { + gameBuilder.GameSystems.Add(gameSystem); + return gameBuilder; + } + + public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class + { + gameBuilder.Services.AddService(service); + return gameBuilder; + } + + public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogListener logListener) + { + gameBuilder.LogListeners.Add(logListener); + return gameBuilder; + } + + public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) + { + // Gets initialized by the GameBase constructor. + var dataBase = gameBuilder.Services.GetService(); + // There should probably be a change to the interface to avoid the below casting. + ((DatabaseFileProviderService)dataBase).FileProvider = provider; + return gameBuilder; + } + + public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) + { + using (Profiler.Begin(GameProfilingKeys.ObjectDatabaseInitialize)) + { + // Create and mount database file system + var objDatabase = ObjectDatabase.CreateDefaultDatabase(); + + // Only set a mount path if not mounted already + var mountPath = VirtualFileSystem.ResolveProviderUnsafe("/asset", true).Provider == null ? "/asset" : null; + var result = new DatabaseFileProvider(objDatabase, mountPath); + + gameBuilder.AddDbFileProvider(result); + } + + return gameBuilder; + } + + public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectCompiler, IVirtualFileProvider fileProvider) + { + EffectCompilerBase compiler = new EffectCompiler(fileProvider) + { + SourceDirectories = { EffectCompilerBase.DefaultSourceShaderFolder }, + }; + + if(fileProvider is DatabaseFileProvider databaseFileProvider) + { + effectCompiler.Compiler = new EffectCompilerCache(compiler, databaseFileProvider); + return effectCompiler; + } + + throw new ArgumentException("The file provider must be a DatabaseFileProvider", nameof(fileProvider)); + } + +} diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs new file mode 100644 index 0000000000..f658479a7b --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Stride.Core.Diagnostics; +using Stride.Core; +using Stride.Games; + +namespace Stride.Engine.Builder; +public interface IGameBuilder +{ + IServiceRegistry Services { get; } + + GameSystemCollection GameSystems { get; } + + List LogListeners { get; } + + GameBase Game { get; } +} diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs new file mode 100644 index 0000000000..b13acc624b --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -0,0 +1,43 @@ +using System; +using Stride.Core.Serialization; +using Stride.Core.Streaming; +using Stride.Games; +using Stride.Graphics; + +namespace Stride.Engine.Builder; +public class MinimalGame : GameBase +{ + + /// + /// Gets the graphics device manager. + /// + /// The graphics device manager. + public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } + + public MinimalGame() + { + // Creates the graphics device manager + GraphicsDeviceManager = new GraphicsDeviceManager(this); + Services.AddService(GraphicsDeviceManager); + Services.AddService(GraphicsDeviceManager); + } + + public override void ConfirmRenderingSettings(bool gameCreation) + { + var deviceManager = (GraphicsDeviceManager)graphicsDeviceManager; + + if (gameCreation) + { + //if our device width or height is actually smaller then requested we use the device one + deviceManager.PreferredBackBufferWidth = Context.RequestedWidth = Math.Min(deviceManager.PreferredBackBufferWidth, Window.ClientBounds.Width); + deviceManager.PreferredBackBufferHeight = Context.RequestedHeight = Math.Min(deviceManager.PreferredBackBufferHeight, Window.ClientBounds.Height); + } + } + + protected override void Initialize() + { + base.Initialize(); + + Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); + } +} diff --git a/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs b/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs index a012e43d9d..7bfd6fab92 100644 --- a/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs +++ b/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs @@ -1,12 +1,10 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. -using System; using Stride.Core; using Stride.Core.Mathematics; using Stride.Core.Serialization.Contents; using Stride.Data; -using Stride.Graphics; namespace Stride.Engine.Design { diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 9fd977372d..97df14822d 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -59,7 +59,7 @@ public abstract class GameBase : ComponentBase, IGame private bool isMouseVisible; - internal object TickLock = new object(); + internal object TickLock = new(); #endregion @@ -402,7 +402,7 @@ internal void InitializeBeforeRun() /// /// The window Context for this game. /// Cannot run this instance while it is already running - public void Run(GameContext gameContext = null) + public virtual void Run(GameContext gameContext = null) { if (IsRunning) { diff --git a/sources/engine/Stride.Games/GraphicsDeviceManager.cs b/sources/engine/Stride.Games/GraphicsDeviceManager.cs index 86cfab0220..90c47f779f 100644 --- a/sources/engine/Stride.Games/GraphicsDeviceManager.cs +++ b/sources/engine/Stride.Games/GraphicsDeviceManager.cs @@ -100,7 +100,7 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra /// /// The game. /// The game instance cannot be null. - internal GraphicsDeviceManager(GameBase game) + public GraphicsDeviceManager(GameBase game) { this.game = game; if (this.game == null) diff --git a/sources/engine/Stride.Games/IGame.cs b/sources/engine/Stride.Games/IGame.cs index c8da0cf304..6456ee6927 100644 --- a/sources/engine/Stride.Games/IGame.cs +++ b/sources/engine/Stride.Games/IGame.cs @@ -4,7 +4,6 @@ using Stride.Core; using Stride.Core.Serialization.Contents; -using Stride.Games.Time; using Stride.Graphics; namespace Stride.Games diff --git a/sources/engine/Stride.Hosting/BasicGame.cs b/sources/engine/Stride.Hosting/BasicGame.cs new file mode 100644 index 0000000000..1b599d454c --- /dev/null +++ b/sources/engine/Stride.Hosting/BasicGame.cs @@ -0,0 +1,42 @@ +using System; +using Stride.Core.Serialization; +using Stride.Games; +using Stride.Graphics; + +namespace Stride.Hosting; +public class BasicGame : GameBase +{ + + /// + /// Gets the graphics device manager. + /// + /// The graphics device manager. + public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } + + public BasicGame() + { + // Creates the graphics device manager + GraphicsDeviceManager = new GraphicsDeviceManager(this); + Services.AddService(GraphicsDeviceManager); + Services.AddService(GraphicsDeviceManager); + } + + public override void ConfirmRenderingSettings(bool gameCreation) + { + var deviceManager = (GraphicsDeviceManager)graphicsDeviceManager; + + if (gameCreation) + { + //if our device width or height is actually smaller then requested we use the device one + deviceManager.PreferredBackBufferWidth = Math.Min(deviceManager.PreferredBackBufferWidth, Window.ClientBounds.Width); + deviceManager.PreferredBackBufferHeight = Math.Min(deviceManager.PreferredBackBufferHeight, Window.ClientBounds.Height); + } + } + + protected override void Initialize() + { + base.Initialize(); + + Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); + } +} diff --git a/sources/engine/Stride.Hosting/IStrideGameBuilder.cs b/sources/engine/Stride.Hosting/IStrideGameBuilder.cs new file mode 100644 index 0000000000..3326ba14fb --- /dev/null +++ b/sources/engine/Stride.Hosting/IStrideGameBuilder.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Games; + +namespace Stride.Hosting; +public interface IStrideGameBuilder +{ + public IServiceRegistry Services { get; } + + GameSystemCollection GameSystems { get; } + + List LogListeners { get; } + + GameBase Game { get; } +} diff --git a/sources/engine/Stride.Hosting/Stride.Hosting.csproj b/sources/engine/Stride.Hosting/Stride.Hosting.csproj new file mode 100644 index 0000000000..dbf6858a2e --- /dev/null +++ b/sources/engine/Stride.Hosting/Stride.Hosting.csproj @@ -0,0 +1,28 @@ + + + + true + + + + true + true + * + + + + + Properties\SharedAssemblyInfo.cs + + + + + + + + + + + + + diff --git a/sources/engine/Stride.Hosting/StrideGameBuilder.cs b/sources/engine/Stride.Hosting/StrideGameBuilder.cs new file mode 100644 index 0000000000..0ba4f91eda --- /dev/null +++ b/sources/engine/Stride.Hosting/StrideGameBuilder.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Stride.Core.Diagnostics; +using Stride.Games; +using Stride.Core; + +namespace Stride.Hosting; +public class StrideGameBuilder : IStrideGameBuilder +{ + public IServiceRegistry Services { get; protected set; } + + public GameSystemCollection GameSystems { get; protected set; } + + public List LogListeners { get; protected set; } = []; + + public GameBase Game { get; protected set; } + + internal StrideGameBuilder() + { + Game = new BasicGame(); + Services = Game.Services; + GameSystems = Game.GameSystems; + } + + public static StrideGameBuilder Create() + { + return new StrideGameBuilder(); + } + + public virtual GameBase Build() + { + foreach (var logListener in LogListeners) + { + GlobalLogger.GlobalMessageLogged += logListener; + } + + return Game; + } +} From 2372862ca222c1dbab81b3b62b5b56389c49c329 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Fri, 9 May 2025 15:02:01 -0600 Subject: [PATCH 02/37] reorganizing code to work with GameStudio --- .../MicrothreadLocalDatabases.cs | 7 +- .../IO/IDatabaseFileProviderService.cs | 4 +- .../Game/EntityHierarchyEditorGame.cs | 3 +- .../EditorGame/Game/EditorServiceGame.cs | 5 + .../Stride.Editor/Engine/EmbeddedGame.cs | 3 +- .../Preview/GameStudioPreviewService.cs | 2 +- .../Stride.Editor/Preview/PreviewGame.cs | 3 +- .../Engine/Builder/GameBuilder.cs | 16 +- .../Engine/Builder/GameBuilderExtensions.cs | 36 +++++ .../Engine/Builder/MinimalGame.cs | 38 ++++- sources/engine/Stride.Engine/Engine/Game.cs | 21 ++- .../Android/GamePlatformAndroid.cs | 2 +- .../Desktop/GamePlatformDesktop.cs | 2 +- sources/engine/Stride.Games/GameBase.cs | 147 ++++++++++++------ sources/engine/Stride.Games/GameContext.cs | 10 +- sources/engine/Stride.Games/GamePlatform.cs | 38 +++-- .../Stride.Games/GraphicsDeviceManager.cs | 3 +- sources/engine/Stride.Games/IGame.cs | 4 + sources/engine/Stride.Games/IGamePlatform.cs | 3 + .../WindowsStore/GamePlatformUWP.cs | 4 +- .../Stride.Games/iOS/GamePlatformiOS.cs | 2 +- sources/engine/Stride.Input/InputManager.cs | 3 +- 22 files changed, 262 insertions(+), 94 deletions(-) diff --git a/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs b/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs index 8c892bea0e..1a99c7f97f 100644 --- a/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs +++ b/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs @@ -113,6 +113,11 @@ private static DatabaseFileProvider CreateDatabase(BuildTransaction transaction) private class MicroThreadLocalProviderService : IDatabaseFileProviderService { - public DatabaseFileProvider FileProvider => MicroThreadLocalDatabaseFileProvider.Value; + public DatabaseFileProvider FileProvider + { + get => MicroThreadLocalDatabaseFileProvider.Value; + set => MicroThreadLocalDatabaseFileProvider.Value = value; + //throw new InvalidOperationException($"Can not change the value of a {nameof(MicroThreadLocalProviderService.FileProvider)}"); + } } } diff --git a/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs b/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs index 93c36db2f8..7d251cd2c2 100644 --- a/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs +++ b/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs @@ -5,5 +5,5 @@ namespace Stride.Core.IO; public interface IDatabaseFileProviderService { - DatabaseFileProvider FileProvider { get; } -} \ No newline at end of file + DatabaseFileProvider FileProvider { get; set; } +} diff --git a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs index 589ceaeccb..b0059ad35d 100644 --- a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs +++ b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs @@ -48,7 +48,8 @@ public abstract class EntityHierarchyEditorGame : EditorServiceGame private Material fallbackColorMaterial; private Material fallbackTextureMaterial; - protected EntityHierarchyEditorGame(TaskCompletionSource gameContentLoadedTaskSource, IEffectCompiler effectCompiler, string effectLogPath) + protected EntityHierarchyEditorGame(TaskCompletionSource gameContentLoadedTaskSource, IEffectCompiler effectCompiler, string effectLogPath, GameContext context = null) + : base(context) { this.gameContentLoadedTaskSource = gameContentLoadedTaskSource; this.effectCompiler = effectCompiler; diff --git a/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs b/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs index bd04fdc2b0..25a80860c5 100644 --- a/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs +++ b/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs @@ -99,6 +99,11 @@ public bool IsEditorHidden public event EventHandler ExceptionThrown; + public EditorServiceGame(GameContext context) : base(context) + { + + } + /// /// Calculates and returns the position of the mouse in the scene. /// diff --git a/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs b/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs index 5eab6d24fc..66d262c9c3 100644 --- a/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs +++ b/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs @@ -3,6 +3,7 @@ using Stride.Core.Diagnostics; using Stride.Engine; +using Stride.Games; using Stride.Graphics; namespace Stride.Editor.Engine @@ -17,7 +18,7 @@ public class EmbeddedGame : Game /// public static bool DebugMode { get; set; } - public EmbeddedGame() + public EmbeddedGame(GameContext context) : base(context) { GraphicsDeviceManager.PreferredGraphicsProfile = new [] { GraphicsProfile.Level_11_0, GraphicsProfile.Level_10_1, GraphicsProfile.Level_10_0 }; GraphicsDeviceManager.PreferredBackBufferWidth = 64; diff --git a/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs b/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs index 522b71f911..99047e09fa 100644 --- a/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs +++ b/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs @@ -137,8 +137,8 @@ private void StrideUIThread() initializationSignal.Set(); - PreviewGame = new PreviewGame(AssetBuilderService.EffectCompiler); var context = new GameContextWinforms(gameForm) { InitializeDatabase = false }; + PreviewGame = new PreviewGame(AssetBuilderService.EffectCompiler, context); // Wait for shaders to be loaded AssetBuilderService.WaitForShaders(); diff --git a/sources/editor/Stride.Editor/Preview/PreviewGame.cs b/sources/editor/Stride.Editor/Preview/PreviewGame.cs index c88ac25032..143ab1c307 100644 --- a/sources/editor/Stride.Editor/Preview/PreviewGame.cs +++ b/sources/editor/Stride.Editor/Preview/PreviewGame.cs @@ -14,6 +14,7 @@ using Stride.Graphics; using Stride.Rendering.Compositing; using Stride.Shaders.Compiler; +using Stride.Games; namespace Stride.Editor.Preview { @@ -43,7 +44,7 @@ public class PreviewGame : EditorGame.Game.EditorServiceGame private Scene previewScene; - public PreviewGame(IEffectCompiler effectCompiler) + public PreviewGame(IEffectCompiler effectCompiler, GameContext context) : base(context) { this.effectCompiler = effectCompiler; } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index dd5a1b202d..28001c7729 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -9,7 +9,7 @@ namespace Stride.Engine.Builder; /// Helps build the game and preps it to be able to run after built. /// /// -public class GameBuilder : IGameBuilder where T : IGame +public class GameBuilder : IGameBuilder { public IServiceRegistry Services { get; protected set; } @@ -21,14 +21,14 @@ public class GameBuilder : IGameBuilder where T : IGame internal GameBuilder() { - Game = new MinimalGame(); + Game = new MinimalGame(null); Services = Game.Services; GameSystems = Game.GameSystems; } - public static GameBuilder Create() + public static GameBuilder Create() { - return new GameBuilder(); + return new GameBuilder(); } public virtual GameBase Build() @@ -41,11 +41,3 @@ public virtual GameBase Build() return Game; } } - -/// -/// Creates a default GameBuilder for a . -/// -public class GameBuilder : GameBuilder -{ - -} diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index ead167468a..ba96b7864a 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,6 +1,8 @@ using System; +using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; +using Stride.Core.Serialization.Contents; using Stride.Core.Storage; using Stride.Games; using Stride.Rendering; @@ -27,6 +29,12 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList return gameBuilder; } + /// + /// Allows the user to add a custom database file provider to the game. + /// + /// + /// + /// public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. @@ -36,6 +44,11 @@ public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, Data return gameBuilder; } + /// + /// Creates a default database to be used in the game. + /// + /// + /// public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) { using (Profiler.Begin(GameProfilingKeys.ObjectDatabaseInitialize)) @@ -53,6 +66,22 @@ public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) return gameBuilder; } + public static IGameBuilder UseDefaultContentManager(this IGameBuilder gameBuilder) + { + var services = gameBuilder.Services; + var content = new ContentManager(services); + services.AddService(content); + services.AddService(content); + return gameBuilder; + } + + /// + /// Adds a default effect compiler to the game. This is used to compile shaders and effects. + /// + /// + /// + /// + /// public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectCompiler, IVirtualFileProvider fileProvider) { EffectCompilerBase compiler = new EffectCompiler(fileProvider) @@ -69,4 +98,11 @@ public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectComp throw new ArgumentException("The file provider must be a DatabaseFileProvider", nameof(fileProvider)); } + public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameContext context) + { + gameBuilder.Game.SetGameContext(context); + + return gameBuilder; + } + } diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs index b13acc624b..8042319f43 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -1,6 +1,7 @@ using System; +using Stride.Core.Diagnostics; using Stride.Core.Serialization; -using Stride.Core.Streaming; +using Stride.Core.Serialization.Contents; using Stride.Games; using Stride.Graphics; @@ -14,10 +15,25 @@ public class MinimalGame : GameBase /// The graphics device manager. public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } - public MinimalGame() + public MinimalGame(GameContext gameContext) : base() { + Context = gameContext ?? DetectDefaultContext(); + Context.CurrentGame = this; + + // Create Platform + Context.GamePlatform = GamePlatform.Create(Context); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + // Setup registry + Services.AddService(this); + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this); + GraphicsDeviceManager = new GraphicsDeviceManager(this, gameContext); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); } @@ -40,4 +56,20 @@ protected override void Initialize() Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); } + + protected override void PrepareContext() + { + //Allow the user to add their own ContentManager + var contentManager = Services.GetService(); + + if (contentManager is null) + { + Log.Info("No ContentManager found, creating default ContentManager"); + contentManager = new ContentManager(Services); + Services.AddService(contentManager); + Services.AddService(contentManager); + } + + Content = contentManager; + } } diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index 6c4c162ed5..7e06b1ba2d 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -188,8 +188,23 @@ public LogMessageType ConsoleLogLevel /// /// Initializes a new instance of the class. /// - public Game() + public Game(GameContext context = null) : base() { + Context = context ?? DetectDefaultContext(); + Context.CurrentGame = this; + + // Create Platform + Context.GamePlatform = GamePlatform.Create(Context); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + // Setup registry + Services.AddService(this); + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + // Register the logger backend before anything else logListener = GetLogListener(); @@ -227,7 +242,7 @@ public Game() Services.AddService(VRDeviceSystem); // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this); + GraphicsDeviceManager = new GraphicsDeviceManager(this, context); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); @@ -256,7 +271,7 @@ protected override void PrepareContext() if (Context.InitializeDatabase) { databaseFileProvider = InitializeAssetDatabase(); - ((DatabaseFileProviderService)Services.GetService()).FileProvider = databaseFileProvider; + Services.GetService().FileProvider = databaseFileProvider; var renderingSettings = new RenderingSettings(); if (Content.Exists(GameSettings.AssetUrl)) diff --git a/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs b/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs index 167afa863d..5db252837c 100644 --- a/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs +++ b/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs @@ -28,7 +28,7 @@ private void PopulateFullName() FullName = $"{manufacturer} - {model}"; } - public GamePlatformAndroid(GameBase game) : base(game) + public GamePlatformAndroid(GameContext context) : base(context) { PopulateFullName(); } diff --git a/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs b/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs index 7d48fae49e..c3911ba734 100644 --- a/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs +++ b/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs @@ -29,7 +29,7 @@ namespace Stride.Games { internal class GamePlatformDesktop : GamePlatform { - public GamePlatformDesktop(GameBase game) : base(game) + public GamePlatformDesktop(GameContext context) : base(context) { IsBlockingRun = true; #if (STRIDE_UI_WINFORMS || STRIDE_UI_WPF) diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 97df14822d..05de63e24d 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -41,7 +41,7 @@ public abstract class GameBase : ComponentBase, IGame { #region Fields - private readonly GamePlatform gamePlatform; + private GamePlatform gamePlatform => Context.GamePlatform; private IGraphicsDeviceService graphicsDeviceService; protected IGraphicsDeviceManager graphicsDeviceManager; private ResumeManager resumeManager; @@ -95,21 +95,23 @@ protected GameBase() GameSystems = new GameSystemCollection(Services); Services.AddService(GameSystems); - // Create Platform - gamePlatform = GamePlatform.Create(this); - gamePlatform.Activated += GamePlatform_Activated; - gamePlatform.Deactivated += GamePlatform_Deactivated; - gamePlatform.Exiting += GamePlatform_Exiting; - gamePlatform.WindowCreated += GamePlatformOnWindowCreated; - - // Setup registry - Services.AddService(this); - Services.AddService(gamePlatform); - Services.AddService(gamePlatform); - IsActive = true; } + protected static GameContext DetectDefaultContext() + { +#if STRIDE_PLATFORM_UWP + return GameContextFactory.NewGameContextUWPXaml(); +#elif STRIDE_PLATFORM_ANDROID + return GameContextFactory.NewGameContextAndroid(); +#elif STRIDE_PLATFORM_IOS + return GameContextFactory.NewGameContextiOS(); +#else + // Here we cover all Desktop variants: OpenTK, SDL, Winforms,... + return GameContextFactory.NewGameContextDesktop(); +#endif + } + #endregion #region Public Events @@ -165,7 +167,7 @@ protected GameBase() /// /// Gets the . /// - public ContentManager Content { get; private set; } + public ContentManager Content { get; protected set; } /// /// Gets the game components registered by this game. @@ -177,15 +179,15 @@ protected GameBase() /// Gets the game context. /// /// The game context. - public GameContext Context { get; private set; } + public GameContext Context { get; protected set; } /// /// Gets the graphics device. /// /// The graphics device. - public GraphicsDevice GraphicsDevice { get; private set; } + public GraphicsDevice GraphicsDevice { get; protected set; } - public GraphicsContext GraphicsContext { get; private set; } + public GraphicsContext GraphicsContext { get; protected set; } /// /// Gets or sets the time between each when is false. @@ -197,13 +199,13 @@ protected GameBase() /// Gets a value indicating whether this instance is active. /// /// true if this instance is active; otherwise, false. - public bool IsActive { get; private set; } + public bool IsActive { get; protected set; } /// /// Gets a value indicating whether this instance is exiting. /// /// true if this instance is exiting; otherwise, false. - public bool IsExiting{ get; private set; } + public bool IsExiting{ get; protected set; } /// /// Gets or sets a value indicating whether the elapsed time between each update should be constant, @@ -295,6 +297,7 @@ public bool IsMouseVisible /// Gets the abstract window. /// /// The window. + [Obsolete("Use GameContext.GameWindow instead.")] public GameWindow Window { get @@ -416,22 +419,40 @@ public virtual void Run(GameContext gameContext = null) throw new InvalidOperationException("No GraphicsDeviceManager found"); } + if(gameContext != null) + { + gameContext.GamePlatform = Context.GamePlatform; + Context = gameContext; + Context.CurrentGame = this; + + // Overwrite Platform + if (gamePlatform != null) + { + Context.GamePlatform.Activated -= GamePlatform_Activated; + Context.GamePlatform.Deactivated -= GamePlatform_Deactivated; + Context.GamePlatform.Exiting -= GamePlatform_Exiting; + Context.GamePlatform.WindowCreated -= GamePlatformOnWindowCreated; + + Services.RemoveService(); + Services.RemoveService(); + } + + Context.GamePlatform = GamePlatform.Create(gameContext); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + } + // Gets the GameWindow Context - if (gameContext == null) + EnsureGameContextIsSet(); + if(gameContext is null) { - AppContextType c; - if (OperatingSystem.IsWindows()) - c = AppContextType.Desktop; - else if (OperatingSystem.IsAndroid()) - c = AppContextType.Android; - else if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()) - c = AppContextType.iOS; - else - c = AppContextType.DesktopSDL; - gameContext = GameContextFactory.NewGameContext(c); + throw new InvalidOperationException("No GameContext found"); } - - Context = gameContext; PrepareContext(); @@ -446,7 +467,7 @@ public virtual void Run(GameContext gameContext = null) Context.RequestedGraphicsProfile = graphicsDeviceManagerImpl.PreferredGraphicsProfile; Context.DeviceCreationFlags = graphicsDeviceManagerImpl.DeviceCreationFlags; - gamePlatform.Run(Context); + gamePlatform.Run(); if (gamePlatform.IsBlockingRun) { @@ -468,6 +489,28 @@ public virtual void Run(GameContext gameContext = null) } } + /// + /// Attempts to get GameContext based on the current platform. + /// + private void EnsureGameContextIsSet() + { + // Gets the GameWindow Context + if (Context == null) + { + AppContextType c; + if (OperatingSystem.IsWindows()) + c = AppContextType.Desktop; + else if (OperatingSystem.IsAndroid()) + c = AppContextType.Android; + else if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()) + c = AppContextType.iOS; + else + c = AppContextType.DesktopSDL; + + Context = GameContextFactory.NewGameContext(c); + } + } + /// /// Creates or updates before window and device are created. /// @@ -512,6 +555,26 @@ public void Tick() } } + public virtual void SetWindow(GameWindow window) + { + if (IsRunning) + { + throw new InvalidOperationException("Cannot set the game window while the game is running"); + } + + Context.GameWindow = window; + //Window = window; + } + + public virtual void SetGameContext(GameContext context) + { + if(IsRunning) + { + throw new InvalidOperationException("Cannot set the game context while the game is running"); + } + Context = context; + } + /// /// Calls automatically based on this game's setup, override it to implement your own system. /// @@ -586,7 +649,7 @@ protected virtual void RawTickProducer() RawTick(singleFrameElapsedTime, updateCount, drawLag / (float)TargetElapsedTime.Ticks, drawFrame); - var window = gamePlatform.MainWindow; + var window = Window; if (gamePlatform.IsBlockingRun) // throttle fps if Game.Tick() called from internal main loop { if (window.IsMinimized || window.Visible == false || (window.Focused == false && TreatNotFocusedLikeMinimized)) @@ -717,17 +780,13 @@ protected override void Destroy() for (int i = 0; i < array.Length; i++) { var disposable = array[i] as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } + disposable?.Dispose(); } // Reset graphics context GraphicsContext = null; - var disposableGraphicsManager = graphicsDeviceManager as IDisposable; - if (disposableGraphicsManager != null) + if (graphicsDeviceManager is IDisposable disposableGraphicsManager) { disposableGraphicsManager.Dispose(); } @@ -888,7 +947,7 @@ protected virtual void OnWindowCreated() WindowCreated?.Invoke(this, EventArgs.Empty); } - private void GamePlatformOnWindowCreated(object sender, EventArgs eventArgs) + protected void GamePlatformOnWindowCreated(object sender, EventArgs eventArgs) { Window.IsMouseVisible = isMouseVisible; OnWindowCreated(); @@ -912,7 +971,7 @@ protected virtual void UnloadContent() GameSystems.UnloadContent(); } - private void GamePlatform_Activated(object sender, EventArgs e) + protected void GamePlatform_Activated(object sender, EventArgs e) { if (!IsActive) { @@ -921,7 +980,7 @@ private void GamePlatform_Activated(object sender, EventArgs e) } } - private void GamePlatform_Deactivated(object sender, EventArgs e) + protected void GamePlatform_Deactivated(object sender, EventArgs e) { if (IsActive) { @@ -930,7 +989,7 @@ private void GamePlatform_Deactivated(object sender, EventArgs e) } } - private void GamePlatform_Exiting(object sender, EventArgs e) + protected void GamePlatform_Exiting(object sender, EventArgs e) { OnExiting(this, EventArgs.Empty); } diff --git a/sources/engine/Stride.Games/GameContext.cs b/sources/engine/Stride.Games/GameContext.cs index 8e2338269e..4fd1a2bc8a 100644 --- a/sources/engine/Stride.Games/GameContext.cs +++ b/sources/engine/Stride.Games/GameContext.cs @@ -30,7 +30,7 @@ namespace Stride.Games { /// - /// Contains context used to render the game (Control for WinForm, a DrawingSurface for WP8...etc.). + /// Contains context for the game and its core modules. /// public abstract class GameContext { @@ -56,6 +56,14 @@ public abstract class GameContext /// The run loop. public Action ExitCallback { get; internal set; } + public GameBase CurrentGame { get; set; } + + public GamePlatform GamePlatform { get; set; } + + public GameWindow GameWindow { get; set; } + + public IServiceRegistry Services { get; set; } + // TODO: remove these requested values. /// diff --git a/sources/engine/Stride.Games/GamePlatform.cs b/sources/engine/Stride.Games/GamePlatform.cs index 36848ae436..c5eab1e0e9 100644 --- a/sources/engine/Stride.Games/GamePlatform.cs +++ b/sources/engine/Stride.Games/GamePlatform.cs @@ -30,35 +30,38 @@ namespace Stride.Games { - internal abstract class GamePlatform : ReferenceBase, IGraphicsDeviceFactory, IGamePlatform + public abstract class GamePlatform : ReferenceBase, IGraphicsDeviceFactory, IGamePlatform { private bool hasExitRan = false; - protected readonly GameBase game; - protected readonly IServiceRegistry Services; - protected GameWindow gameWindow; + protected GameBase game => context.CurrentGame; + + protected GameWindow gameWindow => context.GameWindow; + + protected GameContext context; public string FullName { get; protected set; } = string.Empty; - protected GamePlatform(GameBase game) + protected GamePlatform(GameContext context) { - this.game = game; - Services = game.Services; + Services = context.Services; + this.context = context; + this.context.GamePlatform = this; } - public static GamePlatform Create(GameBase game) + public static GamePlatform Create(GameContext context) { #if STRIDE_PLATFORM_UWP - return new GamePlatformUWP(game); + return new GamePlatformUWP(context); #elif STRIDE_PLATFORM_ANDROID - return new GamePlatformAndroid(game); + return new GamePlatformAndroid(context); #elif STRIDE_PLATFORM_IOS - return new GamePlatformiOS(game); + return new GamePlatformiOS(context); #else // Here we cover all Desktop variants: OpenTK, SDL, Winforms,... - return new GamePlatformDesktop(game); + return new GamePlatformDesktop(context); #endif } @@ -103,6 +106,7 @@ public virtual GameWindow CreateWindow(GameContext gameContext) window.PreferredFullscreenSize = requestedSize; window.Initialize(gameContext); + context.GameWindow = window; return window; } @@ -115,11 +119,11 @@ public virtual GameWindow CreateWindow(GameContext gameContext) /// public bool IsBlockingRun { get; protected set; } - public void Run(GameContext gameContext) + public void Run() { - IsBlockingRun = !gameContext.IsUserManagingRun; + IsBlockingRun = !context.IsUserManagingRun; - gameWindow = CreateWindow(gameContext); + context.GameWindow = CreateWindow(context); // Register on Activated gameWindow.Activated += OnActivated; @@ -293,7 +297,7 @@ public virtual List FindBestDevices(GameGraphicsParam IsFullScreen = preferredParameters.IsFullScreen, PreferredFullScreenOutputIndex = preferredParameters.PreferredFullScreenOutputIndex, PresentationInterval = preferredParameters.SynchronizeWithVerticalRetrace ? PresentInterval.One : PresentInterval.Immediate, - DeviceWindowHandle = MainWindow.NativeWindow, + DeviceWindowHandle = context.GameWindow.NativeWindow, ColorSpace = preferredParameters.ColorSpace, }, }; @@ -380,7 +384,7 @@ protected override void Destroy() if (gameWindow != null) { gameWindow.Dispose(); - gameWindow = null; + context.GameWindow = null; } Activated = null; diff --git a/sources/engine/Stride.Games/GraphicsDeviceManager.cs b/sources/engine/Stride.Games/GraphicsDeviceManager.cs index 90c47f779f..00be755193 100644 --- a/sources/engine/Stride.Games/GraphicsDeviceManager.cs +++ b/sources/engine/Stride.Games/GraphicsDeviceManager.cs @@ -100,7 +100,7 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra /// /// The game. /// The game instance cannot be null. - public GraphicsDeviceManager(GameBase game) + public GraphicsDeviceManager(GameBase game, GameContext context = null) { this.game = game; if (this.game == null) @@ -661,6 +661,7 @@ protected virtual GraphicsDeviceInformation FindBestDevice(bool anySuitableDevic preferredParameters.PreferredBackBufferHeight = resizedBackBufferHeight; } + graphicsDeviceFactory = game.Services.GetService(); var devices = graphicsDeviceFactory.FindBestDevices(preferredParameters); if (devices.Count == 0) { diff --git a/sources/engine/Stride.Games/IGame.cs b/sources/engine/Stride.Games/IGame.cs index 6456ee6927..8e55414ba2 100644 --- a/sources/engine/Stride.Games/IGame.cs +++ b/sources/engine/Stride.Games/IGame.cs @@ -139,5 +139,9 @@ public interface IGame /// /// The window. GameWindow Window { get; } + + public void SetWindow(GameWindow window); + + public void SetGameContext(GameContext context); } } diff --git a/sources/engine/Stride.Games/IGamePlatform.cs b/sources/engine/Stride.Games/IGamePlatform.cs index 9db9b9618f..ea4671c2dc 100644 --- a/sources/engine/Stride.Games/IGamePlatform.cs +++ b/sources/engine/Stride.Games/IGamePlatform.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; + namespace Stride.Games { /// @@ -17,6 +19,7 @@ public interface IGamePlatform /// Gets the main window. /// /// The main window. + [Obsolete("Use GameContext.MainWindow instead. This property will be removed in a future version.")] GameWindow MainWindow { get; } /// diff --git a/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs b/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs index 466ac7fc17..87cfa1bbc7 100644 --- a/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs +++ b/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. // // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel @@ -32,7 +32,7 @@ namespace Stride.Games { internal class GamePlatformUWP : GamePlatform { - public GamePlatformUWP(GameBase game) : base(game) + public GamePlatformUWP(GameContext context) : base(context) { // Application lifecycle reference: // https://docs.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle diff --git a/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs b/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs index 70f2c9ddbd..5ca80248c4 100644 --- a/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs +++ b/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs @@ -27,7 +27,7 @@ private unsafe void PopulateFullName() Marshal.FreeHGlobal(output); } - public GamePlatformiOS(GameBase game) : base(game) + public GamePlatformiOS(GameContext context) : base(context) { PopulateFullName(); } diff --git a/sources/engine/Stride.Input/InputManager.cs b/sources/engine/Stride.Input/InputManager.cs index 5b16714c14..7c91017ac3 100644 --- a/sources/engine/Stride.Input/InputManager.cs +++ b/sources/engine/Stride.Input/InputManager.cs @@ -704,7 +704,8 @@ private void AddSources() #endif break; default: - throw new InvalidOperationException("GameContext type is not supported by the InputManager"); + Logger.Warning("GameContext type is not supported by the InputManager. Register your own for input to be handled properly."); + break; } } From 2f7f21327132b73040b4aca938d642f32c27277d Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Fri, 9 May 2025 15:17:34 -0600 Subject: [PATCH 03/37] clean up --- build/Stride.sln | 15 ------- sources/engine/Stride.Games/GameBase.cs | 4 +- sources/engine/Stride.Hosting/BasicGame.cs | 42 ------------------- .../Stride.Hosting/IStrideGameBuilder.cs | 16 ------- .../Stride.Hosting/Stride.Hosting.csproj | 28 ------------- .../Stride.Hosting/StrideGameBuilder.cs | 39 ----------------- 6 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 sources/engine/Stride.Hosting/BasicGame.cs delete mode 100644 sources/engine/Stride.Hosting/IStrideGameBuilder.cs delete mode 100644 sources/engine/Stride.Hosting/Stride.Hosting.csproj delete mode 100644 sources/engine/Stride.Hosting/StrideGameBuilder.cs diff --git a/build/Stride.sln b/build/Stride.sln index 969e630fb4..65dbc5b24f 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -338,8 +338,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.3D", "..\so EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.BepuPhysics.Tests", "..\sources\engine\Stride.BepuPhysics\Stride.BepuPhysics.Tests\Stride.BepuPhysics.Tests.csproj", "{7B70C783-4085-4702-B3C6-6570FD85CB8F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Hosting", "..\sources\engine\Stride.Hosting\Stride.Hosting.csproj", "{A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1525,18 +1523,6 @@ Global {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.ActiveCfg = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.Build.0 = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.ActiveCfg = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.Build.0 = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.Build.0 = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.ActiveCfg = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1666,7 +1652,6 @@ Global {7715D094-DF59-4D91-BC9A-9A5118039ECB} = {DE048114-9AE4-467E-A879-188DC0D88A59} {66EFFDE4-24F0-4E57-9618-0F5577E20A1E} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} {7B70C783-4085-4702-B3C6-6570FD85CB8F} = {DE048114-9AE4-467E-A879-188DC0D88A59} - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715} = {4C142567-C42B-40F5-B092-798882190209} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 05de63e24d..9531bf2332 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -419,7 +419,7 @@ public virtual void Run(GameContext gameContext = null) throw new InvalidOperationException("No GraphicsDeviceManager found"); } - if(gameContext != null) + if(Context is not null && gameContext is not null) { gameContext.GamePlatform = Context.GamePlatform; Context = gameContext; @@ -449,7 +449,7 @@ public virtual void Run(GameContext gameContext = null) // Gets the GameWindow Context EnsureGameContextIsSet(); - if(gameContext is null) + if(Context is null) { throw new InvalidOperationException("No GameContext found"); } diff --git a/sources/engine/Stride.Hosting/BasicGame.cs b/sources/engine/Stride.Hosting/BasicGame.cs deleted file mode 100644 index 1b599d454c..0000000000 --- a/sources/engine/Stride.Hosting/BasicGame.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using Stride.Core.Serialization; -using Stride.Games; -using Stride.Graphics; - -namespace Stride.Hosting; -public class BasicGame : GameBase -{ - - /// - /// Gets the graphics device manager. - /// - /// The graphics device manager. - public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } - - public BasicGame() - { - // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this); - Services.AddService(GraphicsDeviceManager); - Services.AddService(GraphicsDeviceManager); - } - - public override void ConfirmRenderingSettings(bool gameCreation) - { - var deviceManager = (GraphicsDeviceManager)graphicsDeviceManager; - - if (gameCreation) - { - //if our device width or height is actually smaller then requested we use the device one - deviceManager.PreferredBackBufferWidth = Math.Min(deviceManager.PreferredBackBufferWidth, Window.ClientBounds.Width); - deviceManager.PreferredBackBufferHeight = Math.Min(deviceManager.PreferredBackBufferHeight, Window.ClientBounds.Height); - } - } - - protected override void Initialize() - { - base.Initialize(); - - Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); - } -} diff --git a/sources/engine/Stride.Hosting/IStrideGameBuilder.cs b/sources/engine/Stride.Hosting/IStrideGameBuilder.cs deleted file mode 100644 index 3326ba14fb..0000000000 --- a/sources/engine/Stride.Hosting/IStrideGameBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using Stride.Core; -using Stride.Core.Diagnostics; -using Stride.Games; - -namespace Stride.Hosting; -public interface IStrideGameBuilder -{ - public IServiceRegistry Services { get; } - - GameSystemCollection GameSystems { get; } - - List LogListeners { get; } - - GameBase Game { get; } -} diff --git a/sources/engine/Stride.Hosting/Stride.Hosting.csproj b/sources/engine/Stride.Hosting/Stride.Hosting.csproj deleted file mode 100644 index dbf6858a2e..0000000000 --- a/sources/engine/Stride.Hosting/Stride.Hosting.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - true - - - - true - true - * - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - diff --git a/sources/engine/Stride.Hosting/StrideGameBuilder.cs b/sources/engine/Stride.Hosting/StrideGameBuilder.cs deleted file mode 100644 index 0ba4f91eda..0000000000 --- a/sources/engine/Stride.Hosting/StrideGameBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using Stride.Core.Diagnostics; -using Stride.Games; -using Stride.Core; - -namespace Stride.Hosting; -public class StrideGameBuilder : IStrideGameBuilder -{ - public IServiceRegistry Services { get; protected set; } - - public GameSystemCollection GameSystems { get; protected set; } - - public List LogListeners { get; protected set; } = []; - - public GameBase Game { get; protected set; } - - internal StrideGameBuilder() - { - Game = new BasicGame(); - Services = Game.Services; - GameSystems = Game.GameSystems; - } - - public static StrideGameBuilder Create() - { - return new StrideGameBuilder(); - } - - public virtual GameBase Build() - { - foreach (var logListener in LogListeners) - { - GlobalLogger.GlobalMessageLogged += logListener; - } - - return Game; - } -} From 5e82aa1415c843bd6f07e1d26d171391de5f0915 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 10 May 2025 14:06:08 -0600 Subject: [PATCH 04/37] fixing input issues --- .../Engine/Builder/GameBuilderExtensions.cs | 25 +++++- .../Engine/Builder/MinimalGame.cs | 17 +++- sources/engine/Stride.Engine/Engine/Game.cs | 48 ++++++++++- sources/engine/Stride.Games/GameBase.cs | 80 +++++++++---------- sources/engine/Stride.Games/GamePlatform.cs | 4 +- .../Stride.Games/GraphicsDeviceManager.cs | 12 +-- sources/engine/Stride.Games/IGame.cs | 6 +- sources/engine/Stride.Input/InputManager.cs | 7 +- .../engine/Stride.Input/SDL/InputSourceSDL.cs | 2 +- .../Windows/InputSourceWinforms.cs | 2 +- 10 files changed, 137 insertions(+), 66 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index ba96b7864a..0b45d86da8 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,10 +1,10 @@ using System; -using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; using Stride.Core.Serialization.Contents; using Stride.Core.Storage; using Stride.Games; +using Stride.Input; using Stride.Rendering; using Stride.Shaders.Compiler; @@ -29,6 +29,12 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList return gameBuilder; } + public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameContext context) + { + gameBuilder.Game.SetGameContext(context); + return gameBuilder; + } + /// /// Allows the user to add a custom database file provider to the game. /// @@ -39,8 +45,7 @@ public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, Data { // Gets initialized by the GameBase constructor. var dataBase = gameBuilder.Services.GetService(); - // There should probably be a change to the interface to avoid the below casting. - ((DatabaseFileProviderService)dataBase).FileProvider = provider; + dataBase.FileProvider = provider; return gameBuilder; } @@ -105,4 +110,18 @@ public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameCon return gameBuilder; } + public static IGameBuilder AddInput(this IGameBuilder gameBuilder, IInputSource inputSource) + { + var inputManager = gameBuilder.Services.GetService(); + + if (inputManager == null) + { + throw new InvalidOperationException("InputManager is not registered in the service registry."); + } + + inputManager.Sources.Add(inputSource); + + return gameBuilder; + } + } diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs index 8042319f43..62e77076ea 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -4,8 +4,13 @@ using Stride.Core.Serialization.Contents; using Stride.Games; using Stride.Graphics; +using Stride.Input; namespace Stride.Engine.Builder; + +/// +/// A game class with no registered systems by default. +/// public class MinimalGame : GameBase { @@ -17,7 +22,7 @@ public class MinimalGame : GameBase public MinimalGame(GameContext gameContext) : base() { - Context = gameContext ?? DetectDefaultContext(); + Context = gameContext ?? GetDefaultContext(); Context.CurrentGame = this; // Create Platform @@ -33,7 +38,7 @@ public MinimalGame(GameContext gameContext) : base() Services.AddService(Context.GamePlatform); // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this, gameContext); + GraphicsDeviceManager = new GraphicsDeviceManager(this); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); } @@ -55,6 +60,14 @@ protected override void Initialize() base.Initialize(); Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); + + // Add window specific input source + var inputManager = Services.GetService(); + if (inputManager is not null) + { + var windowInputSource = InputSourceFactory.NewWindowInputSource(Context); + inputManager.Sources.Add(windowInputSource); + } } protected override void PrepareContext() diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index 7e06b1ba2d..0094175372 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -190,7 +190,7 @@ public LogMessageType ConsoleLogLevel /// public Game(GameContext context = null) : base() { - Context = context ?? DetectDefaultContext(); + Context = context ?? GetDefaultContext(); Context.CurrentGame = this; // Create Platform @@ -242,7 +242,7 @@ public Game(GameContext context = null) : base() Services.AddService(VRDeviceSystem); // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this, context); + GraphicsDeviceManager = new GraphicsDeviceManager(this); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); @@ -357,6 +357,7 @@ protected override void Initialize() Input = inputSystem.Manager; Services.AddService(Input); GameSystems.Add(inputSystem); + SetInitialInputSources(Input); // Initialize the systems base.Initialize(); @@ -408,6 +409,49 @@ protected override void Initialize() OnGameStarted(this); } + private void SetInitialInputSources(InputManager inputManager) + { + // Add window specific input source + var windowInputSource = InputSourceFactory.NewWindowInputSource(Context); + inputManager.Sources.Add(windowInputSource); + + // Add platform specific input sources + switch (Context.ContextType) + { +#if STRIDE_UI_SDL + case AppContextType.DesktopSDL: + break; +#endif +#if STRIDE_PLATFORM_ANDROID + case AppContextType.Android: + break; +#endif +#if STRIDE_PLATFORM_IOS + case AppContextType.iOS: + break; +#endif +#if STRIDE_PLATFORM_UWP + case AppContextType.UWPXaml: + case AppContextType.UWPCoreWindow: + break; +#endif + case AppContextType.Desktop: +#if (STRIDE_UI_WINFORMS || STRIDE_UI_WPF) + inputManager.Sources.Add(new InputSourceWindowsDirectInput()); + if (InputSourceWindowsXInput.IsSupported()) + inputManager.Sources.Add(new InputSourceWindowsXInput()); +#if STRIDE_INPUT_RAWINPUT + if (rawInputEnabled && context is GameContextWinforms gameContextWinforms) + inputManager.Sources.Add(new InputSourceWindowsRawInput(gameContextWinforms.Control)); +#endif +#endif + break; + default: + Log.Warning("GameContext type is not supported by the InputManager. Register your own for input to be handled properly."); + break; + } + } + internal static DatabaseFileProvider InitializeAssetDatabase() { using (Profiler.Begin(GameProfilingKeys.ObjectDatabaseInitialize)) diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 9531bf2332..9cb2262684 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -98,7 +98,7 @@ protected GameBase() IsActive = true; } - protected static GameContext DetectDefaultContext() + protected static GameContext GetDefaultContext() { #if STRIDE_PLATFORM_UWP return GameContextFactory.NewGameContextUWPXaml(); @@ -302,9 +302,9 @@ public GameWindow Window { get { - if (gamePlatform != null) + if (Context != null) { - return gamePlatform.MainWindow; + return Context.GameWindow; } return null; } @@ -419,35 +419,7 @@ public virtual void Run(GameContext gameContext = null) throw new InvalidOperationException("No GraphicsDeviceManager found"); } - if(Context is not null && gameContext is not null) - { - gameContext.GamePlatform = Context.GamePlatform; - Context = gameContext; - Context.CurrentGame = this; - - // Overwrite Platform - if (gamePlatform != null) - { - Context.GamePlatform.Activated -= GamePlatform_Activated; - Context.GamePlatform.Deactivated -= GamePlatform_Deactivated; - Context.GamePlatform.Exiting -= GamePlatform_Exiting; - Context.GamePlatform.WindowCreated -= GamePlatformOnWindowCreated; - - Services.RemoveService(); - Services.RemoveService(); - } - - Context.GamePlatform = GamePlatform.Create(gameContext); - Context.GamePlatform.Activated += GamePlatform_Activated; - Context.GamePlatform.Deactivated += GamePlatform_Deactivated; - Context.GamePlatform.Exiting += GamePlatform_Exiting; - Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; - - Services.AddService(Context.GamePlatform); - Services.AddService(Context.GamePlatform); - } - - // Gets the GameWindow Context + SetGameContext(gameContext); EnsureGameContextIsSet(); if(Context is null) { @@ -494,7 +466,7 @@ public virtual void Run(GameContext gameContext = null) /// private void EnsureGameContextIsSet() { - // Gets the GameWindow Context + // Gets the Game Context if (Context == null) { AppContextType c; @@ -555,24 +527,44 @@ public void Tick() } } - public virtual void SetWindow(GameWindow window) + public virtual void SetGameContext(GameContext context) { - if (IsRunning) + if(IsRunning) { - throw new InvalidOperationException("Cannot set the game window while the game is running"); + throw new InvalidOperationException("Cannot set the game context while the game is running"); } - Context.GameWindow = window; - //Window = window; - } + if (Context is not null && context is not null) + { + context.GamePlatform = Context.GamePlatform; + Context = context; + Context.CurrentGame = this; - public virtual void SetGameContext(GameContext context) - { - if(IsRunning) + // Overwrite Platform + if (gamePlatform != null) + { + Context.GamePlatform.Activated -= GamePlatform_Activated; + Context.GamePlatform.Deactivated -= GamePlatform_Deactivated; + Context.GamePlatform.Exiting -= GamePlatform_Exiting; + Context.GamePlatform.WindowCreated -= GamePlatformOnWindowCreated; + + Services.RemoveService(); + Services.RemoveService(); + } + + Context.GamePlatform = GamePlatform.Create(context); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + } + else if (context is not null) { - throw new InvalidOperationException("Cannot set the game context while the game is running"); + Context = context; } - Context = context; } /// diff --git a/sources/engine/Stride.Games/GamePlatform.cs b/sources/engine/Stride.Games/GamePlatform.cs index c5eab1e0e9..d6fa19874d 100644 --- a/sources/engine/Stride.Games/GamePlatform.cs +++ b/sources/engine/Stride.Games/GamePlatform.cs @@ -106,7 +106,6 @@ public virtual GameWindow CreateWindow(GameContext gameContext) window.PreferredFullscreenSize = requestedSize; window.Initialize(gameContext); - context.GameWindow = window; return window; } @@ -123,7 +122,8 @@ public void Run() { IsBlockingRun = !context.IsUserManagingRun; - context.GameWindow = CreateWindow(context); + // Create the game window if not already created manually + context.GameWindow ??= CreateWindow(context); // Register on Activated gameWindow.Activated += OnActivated; diff --git a/sources/engine/Stride.Games/GraphicsDeviceManager.cs b/sources/engine/Stride.Games/GraphicsDeviceManager.cs index 00be755193..08cd2404b4 100644 --- a/sources/engine/Stride.Games/GraphicsDeviceManager.cs +++ b/sources/engine/Stride.Games/GraphicsDeviceManager.cs @@ -49,7 +49,7 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra private readonly object lockDeviceCreation; - private GameBase game; + private readonly GameBase game; private bool deviceSettingsChanged; @@ -100,12 +100,12 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra /// /// The game. /// The game instance cannot be null. - public GraphicsDeviceManager(GameBase game, GameContext context = null) + public GraphicsDeviceManager(GameBase game) { this.game = game; if (this.game == null) { - throw new ArgumentNullException("game"); + throw new ArgumentNullException(nameof(game)); } lockDeviceCreation = new object(); @@ -119,8 +119,8 @@ public GraphicsDeviceManager(GameBase game, GameContext context = null) preferredBackBufferHeight = DefaultBackBufferHeight; preferredRefreshRate = new Rational(60, 1); PreferredMultisampleCount = MultisampleCount.None; - PreferredGraphicsProfile = new[] - { + PreferredGraphicsProfile = + [ GraphicsProfile.Level_11_1, GraphicsProfile.Level_11_0, GraphicsProfile.Level_10_1, @@ -128,7 +128,7 @@ public GraphicsDeviceManager(GameBase game, GameContext context = null) GraphicsProfile.Level_9_3, GraphicsProfile.Level_9_2, GraphicsProfile.Level_9_1, - }; + ]; graphicsDeviceFactory = game.Services.GetService(); if (graphicsDeviceFactory == null) diff --git a/sources/engine/Stride.Games/IGame.cs b/sources/engine/Stride.Games/IGame.cs index 8e55414ba2..5788899fcd 100644 --- a/sources/engine/Stride.Games/IGame.cs +++ b/sources/engine/Stride.Games/IGame.cs @@ -140,8 +140,10 @@ public interface IGame /// The window. GameWindow Window { get; } - public void SetWindow(GameWindow window); - + /// + /// Sets the game context. + /// + /// public void SetGameContext(GameContext context); } } diff --git a/sources/engine/Stride.Input/InputManager.cs b/sources/engine/Stride.Input/InputManager.cs index 7c91017ac3..7793f87679 100644 --- a/sources/engine/Stride.Input/InputManager.cs +++ b/sources/engine/Stride.Input/InputManager.cs @@ -318,7 +318,7 @@ public void Initialize(GameContext gameContext) { this.gameContext = gameContext ?? throw new ArgumentNullException(nameof(gameContext)); - AddSources(); + //AddSources(); // After adding initial devices, reassign gamepad id's // this creates a beter index assignment in the case where you have both an xbox controller and another controller at startup @@ -626,14 +626,15 @@ public void PoolInputEvent(InputEvent inputEvent) { eventRouters[inputEvent.GetType()].PoolEvent(inputEvent); } - + /// /// Resets the collection back to it's default values /// + [Obsolete] public void ResetSources() { Sources.Clear(); - AddSources(); + //AddSources(); } /// diff --git a/sources/engine/Stride.Input/SDL/InputSourceSDL.cs b/sources/engine/Stride.Input/SDL/InputSourceSDL.cs index 7b33c0afb0..328cb58cad 100644 --- a/sources/engine/Stride.Input/SDL/InputSourceSDL.cs +++ b/sources/engine/Stride.Input/SDL/InputSourceSDL.cs @@ -14,7 +14,7 @@ namespace Stride.Input /// /// Provides support for mouse/touch/keyboard/gamepads using SDL /// - internal unsafe class InputSourceSDL : InputSourceBase + public unsafe class InputSourceSDL : InputSourceBase { private static Sdl SDL = Window.SDL; diff --git a/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs b/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs index b46764d109..cbfca61bfa 100644 --- a/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs +++ b/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs @@ -18,7 +18,7 @@ namespace Stride.Input /// /// Provides support for mouse and keyboard input on windows forms /// - internal class InputSourceWinforms : InputSourceBase + public class InputSourceWinforms : InputSourceBase { private readonly HashSet heldKeys = new HashSet(); private readonly List keysToRelease = new List(); From 7136214315a59f3e151abd8e49b87582f2606565 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sun, 11 May 2025 20:17:18 -0600 Subject: [PATCH 05/37] Separated input --- sources/Directory.Packages.props | 4 +- sources/core/Stride.Core/IServiceRegistry.cs | 9 +++ sources/core/Stride.Core/ServiceRegistry.cs | 16 ++++ .../Engine/Builder/GameBuilder.cs | 76 +++++++++++++++++-- .../Engine/Builder/GameBuilderExtensions.cs | 49 ++++++++---- .../Engine/Builder/IGameBuilder.cs | 15 +++- .../Engine/Builder/MinimalGame.cs | 18 ++++- .../Engine/Hosting/IStrideBuilder.cs | 16 ++++ .../Engine/Hosting/StrideBuilder.cs | 52 +++++++++++++ .../engine/Stride.Engine/Stride.Engine.csproj | 2 + 10 files changed, 233 insertions(+), 24 deletions(-) create mode 100644 sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs create mode 100644 sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 633d58c70b..12076223f2 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -12,6 +12,7 @@ + @@ -45,6 +46,7 @@ + @@ -146,4 +148,4 @@ - \ No newline at end of file + diff --git a/sources/core/Stride.Core/IServiceRegistry.cs b/sources/core/Stride.Core/IServiceRegistry.cs index c2b564561a..9f25c94df1 100644 --- a/sources/core/Stride.Core/IServiceRegistry.cs +++ b/sources/core/Stride.Core/IServiceRegistry.cs @@ -47,6 +47,15 @@ public interface IServiceRegistry /// Thrown when a service of the same type is already registered. void AddService(T service) where T : class; + /// + /// Adds a service to this . + /// + /// The service to add. + /// The type to register as. + /// Thrown when the provided service is null. + /// Thrown when a service of the same type is already registered. + void AddService(object service, Type type); + /// /// Gets the service object of the specified type. /// diff --git a/sources/core/Stride.Core/ServiceRegistry.cs b/sources/core/Stride.Core/ServiceRegistry.cs index 09b7543516..ca5dcede37 100644 --- a/sources/core/Stride.Core/ServiceRegistry.cs +++ b/sources/core/Stride.Core/ServiceRegistry.cs @@ -76,6 +76,22 @@ public void AddService(T service) OnServiceAdded(new ServiceEventArgs(type, service)); } + /// + /// + /// This implementation triggers the event after a service is successfully added. + /// + public void AddService(object service, Type type) + { + ArgumentNullException.ThrowIfNull(service); + + lock (registeredService) + { + if (!registeredService.TryAdd(type, service)) + throw new ArgumentException("Service is already registered with this type", nameof(type)); + } + OnServiceAdded(new ServiceEventArgs(type, service)); + } + /// /// /// This implementation triggers the event after a service is successfully removed. diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 28001c7729..82affae29c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -1,7 +1,12 @@ +using System; using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Stride.Core; using Stride.Core.Diagnostics; +using Stride.Core.IO; using Stride.Games; +using Stride.Input; namespace Stride.Engine.Builder; @@ -11,19 +16,28 @@ namespace Stride.Engine.Builder; /// public class GameBuilder : IGameBuilder { - public IServiceRegistry Services { get; protected set; } + public Dictionary Services { get; internal set; } = []; - public GameSystemCollection GameSystems { get; protected set; } + public IServiceCollection DiServices { get; internal set; } = new ServiceCollection(); - public List LogListeners { get; protected set; } = []; + public GameSystemCollection GameSystems { get; internal set; } - public GameBase Game { get; protected set; } + public List LogListeners { get; internal set; } = []; + + public List InputSources { get; internal set; } = []; + + public DatabaseFileProvider DatabaseFileProvider { get; set; } + + public GameBase Game { get; set; } + + public GameContext Context { get; set; } internal GameBuilder() { Game = new MinimalGame(null); - Services = Game.Services; GameSystems = Game.GameSystems; + DiServices.AddSingleton(Game.Services); + Services.Add(typeof(IServiceRegistry), Game.Services); } public static GameBuilder Create() @@ -33,11 +47,63 @@ public static GameBuilder Create() public virtual GameBase Build() { + var provider = DiServices.BuildServiceProvider(); + foreach (var service in Services) + { + if (service.Key == typeof(IServiceRegistry) || service.Key == typeof(IServiceProvider)) + continue; + + try + { + if (service.Value == null) + { + var instance = provider.GetService(service.Key); + Game.Services.AddService(instance, service.Key); + Services[service.Key] = instance; + } + else + { + Game.Services.AddService(service.Value, service.Key); + } + } + catch (Exception ex) + { + // TODO: check if service is already registered first. + } + } + + // Add all game systems to the game. + foreach (var service in Services) + { + var system = provider.GetService(service.Key); + if (system is IGameSystemBase gameSystem && !Game.GameSystems.Contains(gameSystem)) + { + Game.GameSystems.Add(gameSystem); + } + } + foreach (var logListener in LogListeners) { GlobalLogger.GlobalMessageLogged += logListener; } + if (Context != null) + { + Game.SetGameContext(Context); + } + + if(InputSources.Count > 0) + { + var inputManager = Game.Services.GetService() ?? throw new InvalidOperationException("InputManager is not registered in the service registry."); + foreach (var inputSource in InputSources) + { + inputManager.Sources.Add(inputSource); + } + } + + var dataBase = Game.Services.GetService(); + dataBase.FileProvider = DatabaseFileProvider; + return Game; } } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 0b45d86da8..83b0d1af70 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,12 +1,20 @@ using System; +using Microsoft.Extensions.DependencyInjection; +using Stride.Audio; +using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; using Stride.Core.Serialization.Contents; using Stride.Core.Storage; +using Stride.Engine.Processors; using Stride.Games; using Stride.Input; +using Stride.Profiling; using Stride.Rendering; +using Stride.Rendering.Fonts; +using Stride.Rendering.Sprites; using Stride.Shaders.Compiler; +using Stride.Streaming; namespace Stride.Engine.Builder; public static class GameBuilderExtensions @@ -19,7 +27,15 @@ public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gam public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class { - gameBuilder.Services.AddService(service); + gameBuilder.Services.Add(typeof(T), service); + gameBuilder.DiServices.AddSingleton(service); + return gameBuilder; + } + + public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T : class + { + gameBuilder.Services.Add(typeof(T), null); + gameBuilder.DiServices.AddSingleton(); return gameBuilder; } @@ -29,6 +45,20 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList return gameBuilder; } + public static IGameBuilder AddStrideInput(this IGameBuilder gameBuilder) + { + var services = gameBuilder.Services[typeof(IServiceRegistry)] as IServiceRegistry; + + var inputSystem = new InputSystem(services); + + gameBuilder + .AddGameSystem(inputSystem) + .AddService(inputSystem) + .AddService(inputSystem.Manager); + + return gameBuilder; + } + public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameContext context) { gameBuilder.Game.SetGameContext(context); @@ -44,8 +74,7 @@ public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameCon public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. - var dataBase = gameBuilder.Services.GetService(); - dataBase.FileProvider = provider; + gameBuilder.DatabaseFileProvider = provider; return gameBuilder; } @@ -73,7 +102,7 @@ public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) public static IGameBuilder UseDefaultContentManager(this IGameBuilder gameBuilder) { - var services = gameBuilder.Services; + var services = gameBuilder.Game.Services; var content = new ContentManager(services); services.AddService(content); services.AddService(content); @@ -106,22 +135,12 @@ public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectComp public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameContext context) { gameBuilder.Game.SetGameContext(context); - return gameBuilder; } public static IGameBuilder AddInput(this IGameBuilder gameBuilder, IInputSource inputSource) { - var inputManager = gameBuilder.Services.GetService(); - - if (inputManager == null) - { - throw new InvalidOperationException("InputManager is not registered in the service registry."); - } - - inputManager.Sources.Add(inputSource); - + gameBuilder.InputSources.Add(inputSource); return gameBuilder; } - } diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index f658479a7b..9bb6a0060a 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -1,16 +1,27 @@ using System.Collections.Generic; using Stride.Core.Diagnostics; -using Stride.Core; using Stride.Games; +using System; +using Microsoft.Extensions.DependencyInjection; +using Stride.Input; +using Stride.Core.IO; namespace Stride.Engine.Builder; public interface IGameBuilder { - IServiceRegistry Services { get; } + Dictionary Services { get; } + + IServiceCollection DiServices { get; } GameSystemCollection GameSystems { get; } List LogListeners { get; } + List InputSources { get; } + + DatabaseFileProvider DatabaseFileProvider { get; set; } + GameBase Game { get; } + + GameContext Context { get; } } diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs index 62e77076ea..ec51270191 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -1,4 +1,7 @@ using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; using Stride.Core.Diagnostics; using Stride.Core.Serialization; using Stride.Core.Serialization.Contents; @@ -11,7 +14,7 @@ namespace Stride.Engine.Builder; /// /// A game class with no registered systems by default. /// -public class MinimalGame : GameBase +public class MinimalGame : GameBase, IHostedService { /// @@ -85,4 +88,17 @@ protected override void PrepareContext() Content = contentManager; } + + public Task StartAsync(CancellationToken cancellationToken = default) + { + Run(); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken = default) + { + Exit(); + return Task.CompletedTask; + } + } diff --git a/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs new file mode 100644 index 0000000000..9de0f50191 --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Hosting; +using Stride.Games; + +namespace Stride.Engine.Hosting; + +public interface IStrideBuilder : IHostBuilder +{ + /// + /// Gets the game context. + /// + GameContext Context { get; } + /// + /// Gets the game. + /// + GameBase Game { get; } +} diff --git a/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs new file mode 100644 index 0000000000..f6bffe3367 --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Stride.Games; + +namespace Stride.Engine.Hosting; + +public class StrideBuilder : IStrideBuilder +{ + public GameContext Context { get; set; } + + public GameBase Game { get; set; } + + public IDictionary Properties { get; set; } + + public IHost Build() + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureContainer(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureServices(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull + { + throw new NotImplementedException(); + } + + public IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull + { + throw new NotImplementedException(); + } +} diff --git a/sources/engine/Stride.Engine/Stride.Engine.csproj b/sources/engine/Stride.Engine/Stride.Engine.csproj index 7bded5b689..234f39796f 100644 --- a/sources/engine/Stride.Engine/Stride.Engine.csproj +++ b/sources/engine/Stride.Engine/Stride.Engine.csproj @@ -26,8 +26,10 @@ + + From aa3c9fe1dac7f023215a4262d475b6c1fb7627fa Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Mon, 12 May 2025 15:47:29 -0600 Subject: [PATCH 06/37] clean up and docs --- .../Engine/Builder/GameBuilder.cs | 16 +++- .../Engine/Builder/GameBuilderExtensions.cs | 87 ++++++++++++++----- .../Engine/Builder/IGameBuilder.cs | 4 +- .../Engine/Hosting/IStrideBuilder.cs | 16 ---- .../Engine/Hosting/StrideBuilder.cs | 52 ----------- 5 files changed, 82 insertions(+), 93 deletions(-) delete mode 100644 sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs delete mode 100644 sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 82affae29c..af4d652806 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.Extensions.DependencyInjection; using Stride.Core; using Stride.Core.Diagnostics; @@ -58,6 +57,21 @@ public virtual GameBase Build() if (service.Value == null) { var instance = provider.GetService(service.Key); + + if(instance == null) + { + //check if the type is inherited from another instance in the services. + foreach (var kvp in Services) + { + if (kvp.Key.IsAssignableFrom(service.Key) && kvp.Value != null) + { + instance = provider.GetService(kvp.Key); + if(instance is not null) + break; + } + } + } + Game.Services.AddService(instance, service.Key); Services[service.Key] = instance; } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 83b0d1af70..2a1fe0d064 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,30 +1,39 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Stride.Audio; using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; using Stride.Core.Serialization.Contents; using Stride.Core.Storage; -using Stride.Engine.Processors; using Stride.Games; using Stride.Input; -using Stride.Profiling; using Stride.Rendering; -using Stride.Rendering.Fonts; -using Stride.Rendering.Sprites; using Stride.Shaders.Compiler; -using Stride.Streaming; namespace Stride.Engine.Builder; public static class GameBuilderExtensions { + + /// + /// Adds cire systems to the game. Does not register the systems into the + /// + /// + /// + /// + /// public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gameSystem) where T : IGameSystemBase { gameBuilder.GameSystems.Add(gameSystem); return gameBuilder; } + /// + /// Registers a service into the . + /// + /// + /// + /// + /// public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class { gameBuilder.Services.Add(typeof(T), service); @@ -32,6 +41,12 @@ public static IGameBuilder AddService(this IGameBuilder gameBuilder, T servic return gameBuilder; } + /// + /// Registers a service into the . + /// + /// + /// + /// public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T : class { gameBuilder.Services.Add(typeof(T), null); @@ -39,13 +54,41 @@ public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T return gameBuilder; } + /// + /// Registers a service and its interface into the . + /// + /// + /// + /// + /// + public static IGameBuilder AddService(this IGameBuilder gameBuilder) where TClass : class, TInterface where TInterface : class + { + // This is a work around to allow DI to work the same way as the ServiceRegistry expects. + // Without registering both the interface and the class, the DI will not be able to resolve the interface on build. + gameBuilder.Services.Add(typeof(TInterface), null); + gameBuilder.Services.Add(typeof(TClass), null); + gameBuilder.DiServices.AddSingleton(); + return gameBuilder; + } + + /// + /// Adds a log listener to the game. This is used thoughout Stride systems for logging events. + /// + /// + /// + /// public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogListener logListener) { gameBuilder.LogListeners.Add(logListener); return gameBuilder; } - public static IGameBuilder AddStrideInput(this IGameBuilder gameBuilder) + /// + /// Adds the Stride input system to the game with no sources. + /// + /// + /// + public static IGameBuilder UseStrideInput(this IGameBuilder gameBuilder) { var services = gameBuilder.Services[typeof(IServiceRegistry)] as IServiceRegistry; @@ -61,17 +104,17 @@ public static IGameBuilder AddStrideInput(this IGameBuilder gameBuilder) public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameContext context) { - gameBuilder.Game.SetGameContext(context); + gameBuilder.Context = context; return gameBuilder; } /// - /// Allows the user to add a custom database file provider to the game. + /// Add a custom database file provider to the game. /// /// /// /// - public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) + public static IGameBuilder SetDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. gameBuilder.DatabaseFileProvider = provider; @@ -94,7 +137,7 @@ public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) var mountPath = VirtualFileSystem.ResolveProviderUnsafe("/asset", true).Provider == null ? "/asset" : null; var result = new DatabaseFileProvider(objDatabase, mountPath); - gameBuilder.AddDbFileProvider(result); + gameBuilder.SetDbFileProvider(result); } return gameBuilder; @@ -112,11 +155,11 @@ public static IGameBuilder UseDefaultContentManager(this IGameBuilder gameBuilde /// /// Adds a default effect compiler to the game. This is used to compile shaders and effects. /// - /// + /// /// /// /// - public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectCompiler, IVirtualFileProvider fileProvider) + public static EffectSystem CreateDefaultEffectCompiler(this EffectSystem effectSystem, IVirtualFileProvider fileProvider) { EffectCompilerBase compiler = new EffectCompiler(fileProvider) { @@ -125,20 +168,20 @@ public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectComp if(fileProvider is DatabaseFileProvider databaseFileProvider) { - effectCompiler.Compiler = new EffectCompilerCache(compiler, databaseFileProvider); - return effectCompiler; + effectSystem.Compiler = new EffectCompilerCache(compiler, databaseFileProvider); + return effectSystem; } throw new ArgumentException("The file provider must be a DatabaseFileProvider", nameof(fileProvider)); } - public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameContext context) - { - gameBuilder.Game.SetGameContext(context); - return gameBuilder; - } - - public static IGameBuilder AddInput(this IGameBuilder gameBuilder, IInputSource inputSource) + /// + /// Adds an input source to the game. This requires the Stride input system to be used. + /// + /// + /// + /// + public static IGameBuilder AddStrideInputSource(this IGameBuilder gameBuilder, IInputSource inputSource) { gameBuilder.InputSources.Add(inputSource); return gameBuilder; diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index 9bb6a0060a..7b1d916455 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -21,7 +21,7 @@ public interface IGameBuilder DatabaseFileProvider DatabaseFileProvider { get; set; } - GameBase Game { get; } + GameBase Game { get; set; } - GameContext Context { get; } + GameContext Context { get; set; } } diff --git a/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs deleted file mode 100644 index 9de0f50191..0000000000 --- a/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.Extensions.Hosting; -using Stride.Games; - -namespace Stride.Engine.Hosting; - -public interface IStrideBuilder : IHostBuilder -{ - /// - /// Gets the game context. - /// - GameContext Context { get; } - /// - /// Gets the game. - /// - GameBase Game { get; } -} diff --git a/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs deleted file mode 100644 index f6bffe3367..0000000000 --- a/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Stride.Games; - -namespace Stride.Engine.Hosting; - -public class StrideBuilder : IStrideBuilder -{ - public GameContext Context { get; set; } - - public GameBase Game { get; set; } - - public IDictionary Properties { get; set; } - - public IHost Build() - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureContainer(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureServices(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull - { - throw new NotImplementedException(); - } - - public IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull - { - throw new NotImplementedException(); - } -} From 257c4589ede6ce30b94355f4f7213e7cd812d511 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 14:53:08 -0600 Subject: [PATCH 07/37] Kryptos feedback --- sources/Directory.Packages.props | 4 +--- sources/editor/Stride.Editor/Preview/PreviewGame.cs | 10 +++++----- sources/engine/Stride.Input/InputManager.cs | 6 ++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 12076223f2..f289648c35 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -11,9 +11,7 @@ - - - + diff --git a/sources/editor/Stride.Editor/Preview/PreviewGame.cs b/sources/editor/Stride.Editor/Preview/PreviewGame.cs index 143ab1c307..b7a5a8c9f1 100644 --- a/sources/editor/Stride.Editor/Preview/PreviewGame.cs +++ b/sources/editor/Stride.Editor/Preview/PreviewGame.cs @@ -2,19 +2,19 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using System.Threading.Tasks; -using Stride.Core.BuildEngine; -using Stride.Core; -using Stride.Core.Diagnostics; -using Stride.Core.Mathematics; using Stride.Assets; using Stride.Assets.SpriteFont; using Stride.Assets.SpriteFont.Compiler; +using Stride.Core; +using Stride.Core.BuildEngine; +using Stride.Core.Diagnostics; +using Stride.Core.Mathematics; using Stride.Engine; using Stride.Engine.Design; +using Stride.Games; using Stride.Graphics; using Stride.Rendering.Compositing; using Stride.Shaders.Compiler; -using Stride.Games; namespace Stride.Editor.Preview { diff --git a/sources/engine/Stride.Input/InputManager.cs b/sources/engine/Stride.Input/InputManager.cs index 7793f87679..eb9e1d788c 100644 --- a/sources/engine/Stride.Input/InputManager.cs +++ b/sources/engine/Stride.Input/InputManager.cs @@ -318,8 +318,6 @@ public void Initialize(GameContext gameContext) { this.gameContext = gameContext ?? throw new ArgumentNullException(nameof(gameContext)); - //AddSources(); - // After adding initial devices, reassign gamepad id's // this creates a beter index assignment in the case where you have both an xbox controller and another controller at startup var sortedGamePads = GamePads.OrderBy(x => x.CanChangeIndex); @@ -630,11 +628,11 @@ public void PoolInputEvent(InputEvent inputEvent) /// /// Resets the collection back to it's default values /// - [Obsolete] + [Obsolete("This should be managed manually instead by using the Sources collection")] public void ResetSources() { Sources.Clear(); - //AddSources(); + AddSources(); } /// From e83810a6d1876da56cfe4318e77b01315705b1c6 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 15:10:04 -0600 Subject: [PATCH 08/37] missed some duplicate packages --- sources/Directory.Packages.props | 3 +-- sources/engine/Stride.Engine/Stride.Engine.csproj | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index f289648c35..6368b59dc8 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -11,7 +11,7 @@ - + @@ -44,7 +44,6 @@ - diff --git a/sources/engine/Stride.Engine/Stride.Engine.csproj b/sources/engine/Stride.Engine/Stride.Engine.csproj index 234f39796f..7836b61cf4 100644 --- a/sources/engine/Stride.Engine/Stride.Engine.csproj +++ b/sources/engine/Stride.Engine/Stride.Engine.csproj @@ -29,7 +29,6 @@ - From 18397c542e215dbf2c59ae383b0df5272cf33818 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 18:16:47 -0600 Subject: [PATCH 09/37] add option for DI in GameFontSystem --- .../engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs b/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs index 0db5f3866f..570f6895b3 100644 --- a/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs @@ -15,11 +15,11 @@ public class GameFontSystem : GameSystemBase { public FontSystem FontSystem { get; private set; } - public GameFontSystem(IServiceRegistry registry) + public GameFontSystem(IServiceRegistry registry, FontSystem fontSystem = null) : base(registry) { Visible = true; - FontSystem = new FontSystem(); + FontSystem = fontSystem ?? new FontSystem(); } public override void Draw(GameTime gameTime) From 2588b5fd969c7a265cd873d48ada25edd9903d92 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 19:37:36 -0600 Subject: [PATCH 10/37] Fix error caused by StreamingManager DI. --- .../Stride.Engine/Engine/Builder/GameBuilder.cs | 14 ++++++++------ sources/engine/Stride.Engine/Engine/Game.cs | 3 +++ .../Stride.Rendering/Streaming/StreamingManager.cs | 5 ++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index af4d652806..c3b6cbc3ec 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -46,6 +46,12 @@ public static GameBuilder Create() public virtual GameBase Build() { + + foreach (var logListener in LogListeners) + { + GlobalLogger.GlobalMessageLogged += logListener; + } + var provider = DiServices.BuildServiceProvider(); foreach (var service in Services) { @@ -82,7 +88,8 @@ public virtual GameBase Build() } catch (Exception ex) { - // TODO: check if service is already registered first. + // TODO: check if service is already registered first.' + GlobalLogger.GetLogger("GameBuilder").Error($"Failed to register service {service.Key.Name}.\n\n", ex); } } @@ -96,11 +103,6 @@ public virtual GameBase Build() } } - foreach (var logListener in LogListeners) - { - GlobalLogger.GlobalMessageLogged += logListener; - } - if (Context != null) { Game.SetGameContext(Context); diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index 0094175372..dbaaab9489 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -11,10 +11,12 @@ using Stride.Core.IO; using Stride.Core.Mathematics; using Stride.Core.Storage; +using Stride.Core.Streaming; using Stride.Engine.Design; using Stride.Engine.Processors; using Stride.Games; using Stride.Graphics; +using Stride.Graphics.Data; using Stride.Graphics.Font; using Stride.Input; using Stride.Profiling; @@ -220,6 +222,7 @@ public Game(GameContext context = null) : base() Services.AddService(SceneSystem); Streaming = new StreamingManager(Services); + Services.AddService(Streaming); Audio = new AudioSystem(Services); Services.AddService(Audio); diff --git a/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs b/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs index 394c791ec6..21ca774303 100644 --- a/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs +++ b/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs @@ -92,9 +92,8 @@ public class StreamingManager : GameSystemBase, IStreamingManager, ITexturesStre public StreamingManager([NotNull] IServiceRegistry services) : base(services) { - services.AddService(this); - services.AddService(this); - services.AddService(this); + Services.AddService(this); + Services.AddService(this); ContentStreaming = new ContentStreamingService(); From f1f7b49b16185d3ad20319286861cb03838d6ce7 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:01:50 -0600 Subject: [PATCH 11/37] renaming to favour ServiceCollection --- .../Engine/Builder/GameBuilder.cs | 19 +++++++++---------- .../Engine/Builder/GameBuilderExtensions.cs | 16 ++++++++-------- .../Engine/Builder/IGameBuilder.cs | 4 ++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index c3b6cbc3ec..092440c1bc 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -15,9 +15,9 @@ namespace Stride.Engine.Builder; /// public class GameBuilder : IGameBuilder { - public Dictionary Services { get; internal set; } = []; + public Dictionary InternalServices { get; internal set; } = []; - public IServiceCollection DiServices { get; internal set; } = new ServiceCollection(); + public IServiceCollection Services { get; internal set; } = new ServiceCollection(); public GameSystemCollection GameSystems { get; internal set; } @@ -35,8 +35,8 @@ internal GameBuilder() { Game = new MinimalGame(null); GameSystems = Game.GameSystems; - DiServices.AddSingleton(Game.Services); - Services.Add(typeof(IServiceRegistry), Game.Services); + Services.AddSingleton(Game.Services); + InternalServices.Add(typeof(IServiceRegistry), Game.Services); } public static GameBuilder Create() @@ -46,14 +46,13 @@ public static GameBuilder Create() public virtual GameBase Build() { - foreach (var logListener in LogListeners) { GlobalLogger.GlobalMessageLogged += logListener; } - var provider = DiServices.BuildServiceProvider(); - foreach (var service in Services) + var provider = Services.BuildServiceProvider(); + foreach (var service in InternalServices) { if (service.Key == typeof(IServiceRegistry) || service.Key == typeof(IServiceProvider)) continue; @@ -67,7 +66,7 @@ public virtual GameBase Build() if(instance == null) { //check if the type is inherited from another instance in the services. - foreach (var kvp in Services) + foreach (var kvp in InternalServices) { if (kvp.Key.IsAssignableFrom(service.Key) && kvp.Value != null) { @@ -79,7 +78,7 @@ public virtual GameBase Build() } Game.Services.AddService(instance, service.Key); - Services[service.Key] = instance; + InternalServices[service.Key] = instance; } else { @@ -94,7 +93,7 @@ public virtual GameBase Build() } // Add all game systems to the game. - foreach (var service in Services) + foreach (var service in InternalServices) { var system = provider.GetService(service.Key); if (system is IGameSystemBase gameSystem && !Game.GameSystems.Contains(gameSystem)) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 2a1fe0d064..702d8f1912 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -36,8 +36,8 @@ public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gam /// public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class { - gameBuilder.Services.Add(typeof(T), service); - gameBuilder.DiServices.AddSingleton(service); + gameBuilder.InternalServices.Add(typeof(T), service); + gameBuilder.Services.AddSingleton(service); return gameBuilder; } @@ -49,8 +49,8 @@ public static IGameBuilder AddService(this IGameBuilder gameBuilder, T servic /// public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T : class { - gameBuilder.Services.Add(typeof(T), null); - gameBuilder.DiServices.AddSingleton(); + gameBuilder.InternalServices.Add(typeof(T), null); + gameBuilder.Services.AddSingleton(); return gameBuilder; } @@ -65,9 +65,9 @@ public static IGameBuilder AddService(this IGameBuilder game { // This is a work around to allow DI to work the same way as the ServiceRegistry expects. // Without registering both the interface and the class, the DI will not be able to resolve the interface on build. - gameBuilder.Services.Add(typeof(TInterface), null); - gameBuilder.Services.Add(typeof(TClass), null); - gameBuilder.DiServices.AddSingleton(); + gameBuilder.InternalServices.Add(typeof(TInterface), null); + gameBuilder.InternalServices.Add(typeof(TClass), null); + gameBuilder.Services.AddSingleton(); return gameBuilder; } @@ -90,7 +90,7 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList /// public static IGameBuilder UseStrideInput(this IGameBuilder gameBuilder) { - var services = gameBuilder.Services[typeof(IServiceRegistry)] as IServiceRegistry; + var services = gameBuilder.InternalServices[typeof(IServiceRegistry)] as IServiceRegistry; var inputSystem = new InputSystem(services); diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index 7b1d916455..a56c041060 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -9,9 +9,9 @@ namespace Stride.Engine.Builder; public interface IGameBuilder { - Dictionary Services { get; } + Dictionary InternalServices { get; } - IServiceCollection DiServices { get; } + IServiceCollection Services { get; } GameSystemCollection GameSystems { get; } From 938a50cb2e63b39058032e235d891390ca2338fb Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:06:39 -0600 Subject: [PATCH 12/37] typo --- .../Stride.Engine/Engine/Builder/GameBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 702d8f1912..5e09032a5a 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -15,7 +15,7 @@ public static class GameBuilderExtensions { /// - /// Adds cire systems to the game. Does not register the systems into the + /// Adds core systems to the game. Does not register the systems into the /// /// /// From c2de7f489b4fb0963abfa9754520890898533b26 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:43:14 -0600 Subject: [PATCH 13/37] docs --- sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 092440c1bc..fee0db5890 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -15,8 +15,14 @@ namespace Stride.Engine.Builder; /// public class GameBuilder : IGameBuilder { + /// + /// This is used to allow the same instance to be registered multiple times as differenet interfaces or types. This was done due to how works."/> + /// public Dictionary InternalServices { get; internal set; } = []; + /// + /// This allows for Service to be registered through DI. + /// public IServiceCollection Services { get; internal set; } = new ServiceCollection(); public GameSystemCollection GameSystems { get; internal set; } From 3b26a456073a61252efd331daaf12150839a3a6a Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:53:31 -0600 Subject: [PATCH 14/37] docs --- .../Engine/Builder/GameBuilder.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index fee0db5890..ec4a2dd27e 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -10,7 +10,7 @@ namespace Stride.Engine.Builder; /// -/// Helps build the game and preps it to be able to run after built. +/// Helps build the game and preps it to be able to run after . /// /// public class GameBuilder : IGameBuilder @@ -25,10 +25,19 @@ public class GameBuilder : IGameBuilder /// public IServiceCollection Services { get; internal set; } = new ServiceCollection(); + /// + /// This is a direct reference to the game systems collection of the game. + /// public GameSystemCollection GameSystems { get; internal set; } + /// + /// Adds log listeners to the game on . This is registered first so it will log build errors if they occur."/> + /// public List LogListeners { get; internal set; } = []; + /// + /// Adds input sources to the game on . + /// public List InputSources { get; internal set; } = []; public DatabaseFileProvider DatabaseFileProvider { get; set; } @@ -37,17 +46,21 @@ public class GameBuilder : IGameBuilder public GameContext Context { get; set; } - internal GameBuilder() + internal GameBuilder(GameBase game) { - Game = new MinimalGame(null); + Game = game ?? new MinimalGame(null); GameSystems = Game.GameSystems; Services.AddSingleton(Game.Services); InternalServices.Add(typeof(IServiceRegistry), Game.Services); } - public static GameBuilder Create() + /// + /// Creates a new instance of the class. + /// + /// + public static GameBuilder Create(GameBase game = null) { - return new GameBuilder(); + return new GameBuilder(game); } public virtual GameBase Build() From 384dd48fae6c4e07394661db662b40f4165dccfb Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 12:42:05 -0600 Subject: [PATCH 15/37] clean up and build logging --- .../Engine/Builder/GameBuilder.cs | 27 ++++++++++++------- .../Engine/Builder/GameBuilderExtensions.cs | 4 ++- .../Engine/Builder/IGameBuilder.cs | 2 -- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index ec4a2dd27e..0e0fd4fa3c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection; using Stride.Core; using Stride.Core.Diagnostics; -using Stride.Core.IO; using Stride.Games; using Stride.Input; @@ -26,7 +25,7 @@ public class GameBuilder : IGameBuilder public IServiceCollection Services { get; internal set; } = new ServiceCollection(); /// - /// This is a direct reference to the game systems collection of the game. + /// This is a direct reference to the game systems collection of the . /// public GameSystemCollection GameSystems { get; internal set; } @@ -40,12 +39,12 @@ public class GameBuilder : IGameBuilder /// public List InputSources { get; internal set; } = []; - public DatabaseFileProvider DatabaseFileProvider { get; set; } - public GameBase Game { get; set; } public GameContext Context { get; set; } + private static Logger _log => GlobalLogger.GetLogger("GameBuilder"); + internal GameBuilder(GameBase game) { Game = game ?? new MinimalGame(null); @@ -96,18 +95,20 @@ public virtual GameBase Build() } } + _log.Info($"Registering service {service.Key.Name}."); Game.Services.AddService(instance, service.Key); InternalServices[service.Key] = instance; } else { + _log.Info($"Registering service {service.Key.Name}."); Game.Services.AddService(service.Value, service.Key); } } catch (Exception ex) { // TODO: check if service is already registered first.' - GlobalLogger.GetLogger("GameBuilder").Error($"Failed to register service {service.Key.Name}.\n\n", ex); + _log.Error($"Failed to register service {service.Key.Name}.\n\n", ex); } } @@ -117,27 +118,35 @@ public virtual GameBase Build() var system = provider.GetService(service.Key); if (system is IGameSystemBase gameSystem && !Game.GameSystems.Contains(gameSystem)) { + _log.Info($"Adding game system {gameSystem.GetType().Name} to the game systems collection."); Game.GameSystems.Add(gameSystem); } } if (Context != null) { + _log.Info($"Setting game context."); Game.SetGameContext(Context); } if(InputSources.Count > 0) { - var inputManager = Game.Services.GetService() ?? throw new InvalidOperationException("InputManager is not registered in the service registry."); + var inputManager = Game.Services.GetService(); + + if (inputManager is null) + { + _log.Info("No InputManager found in the game services, creating default."); + inputManager = new InputManager(); + Game.Services.AddService(inputManager); + } + foreach (var inputSource in InputSources) { + _log.Info($"Adding input source {inputSource.GetType().Name} to the input manager."); inputManager.Sources.Add(inputSource); } } - var dataBase = Game.Services.GetService(); - dataBase.FileProvider = DatabaseFileProvider; - return Game; } } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 5e09032a5a..200289b29c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -117,7 +117,9 @@ public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameCon public static IGameBuilder SetDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. - gameBuilder.DatabaseFileProvider = provider; + var fileProviderService = gameBuilder.Game.Services.GetService(); + + fileProviderService.FileProvider = provider; return gameBuilder; } diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index a56c041060..eba6f13b5b 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -19,8 +19,6 @@ public interface IGameBuilder List InputSources { get; } - DatabaseFileProvider DatabaseFileProvider { get; set; } - GameBase Game { get; set; } GameContext Context { get; set; } From ceaec0485b18b36b84767ebd28b6d7b345164f21 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 18:49:07 -0600 Subject: [PATCH 16/37] remove some hardcoded Game references --- .../Stride.Debugger/Debugger/LiveAssemblyReloader.cs | 11 ++++++----- .../Stride.Engine/Engine/Builder/GameBuilder.cs | 4 ++-- sources/engine/Stride.Engine/Engine/Game.cs | 2 -- sources/engine/Stride.Engine/Engine/GameSystem.cs | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs b/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs index 67c314e85f..bb4d910626 100644 --- a/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs +++ b/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs @@ -1,29 +1,30 @@ -// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Stride.Core; using Stride.Core.Reflection; -using Stride.Core.Serialization; using Stride.Core.Yaml; using Stride.Core.Yaml.Events; using Stride.Core.Yaml.Serialization; using Stride.Debugger.Target; using Stride.Engine; +using Stride.Games; namespace Stride.Debugger { public static class LiveAssemblyReloader { - public static void Reload(Game game, AssemblyContainer assemblyContainer, List assembliesToUnregister, List assembliesToRegister) + public static void Reload(GameBase game, AssemblyContainer assemblyContainer, List assembliesToUnregister, List assembliesToRegister) { List entities = new List(); + var sceneSystem = game.Services.GetSafeServiceAs(); + if (game != null) - entities.AddRange(game.SceneSystem.SceneInstance); + entities.AddRange(sceneSystem.SceneInstance); CloneReferenceSerializer.References = new List(); diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 0e0fd4fa3c..0469f99e15 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -107,7 +107,7 @@ public virtual GameBase Build() } catch (Exception ex) { - // TODO: check if service is already registered first.' + // TODO: check if service is already registered first. _log.Error($"Failed to register service {service.Key.Name}.\n\n", ex); } } @@ -147,6 +147,6 @@ public virtual GameBase Build() } } - return Game; + return Game; } } diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index dbaaab9489..7064d566e6 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -11,12 +11,10 @@ using Stride.Core.IO; using Stride.Core.Mathematics; using Stride.Core.Storage; -using Stride.Core.Streaming; using Stride.Engine.Design; using Stride.Engine.Processors; using Stride.Games; using Stride.Graphics; -using Stride.Graphics.Data; using Stride.Graphics.Font; using Stride.Input; using Stride.Profiling; diff --git a/sources/engine/Stride.Engine/Engine/GameSystem.cs b/sources/engine/Stride.Engine/Engine/GameSystem.cs index 3ea3a8f9ec..545609b8c9 100644 --- a/sources/engine/Stride.Engine/Engine/GameSystem.cs +++ b/sources/engine/Stride.Engine/Engine/GameSystem.cs @@ -19,6 +19,6 @@ protected GameSystem(IServiceRegistry registry) : base(registry) /// /// The game. /// This value can be null - public new Game Game => (Game)base.Game; + public new GameBase Game => base.Game; } } From 20033644648dbf7ca4e9096a5c58143ca0863264 Mon Sep 17 00:00:00 2001 From: Nicolas Musset Date: Mon, 2 Jun 2025 13:05:31 +0200 Subject: [PATCH 17/37] feat: Setup concurrency for CI workflows (#2807) --- .github/workflows/build-android.yml | 4 ++++ .github/workflows/build-assembly-processor.yml | 4 ++++ .github/workflows/build-ios.yml | 4 ++++ .github/workflows/build-launcher.yml | 4 ++++ .github/workflows/build-linux-runtime.yml | 4 ++++ .github/workflows/build-vs-package.yml | 4 ++++ .github/workflows/build-windows-full.yml | 4 ++++ .github/workflows/build-windows-runtime.yml | 4 ++++ .github/workflows/main.yml | 4 ++++ .github/workflows/test-linux.yml | 10 +++++++--- .github/workflows/test-windows.yml | 4 ++++ 11 files changed, 47 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index 45242d98e5..239f2c7c73 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -33,6 +33,10 @@ on: default: Debug type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + cancel-in-progress: true + jobs: # # Build Stride Runtime for Android diff --git a/.github/workflows/build-assembly-processor.yml b/.github/workflows/build-assembly-processor.yml index 9a00e4194e..4b208ea5a2 100644 --- a/.github/workflows/build-assembly-processor.yml +++ b/.github/workflows/build-assembly-processor.yml @@ -29,6 +29,10 @@ on: default: Debug type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + cancel-in-progress: true + jobs: # # Build Assembly Processor diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 9ada33e0a1..2cad4b67b4 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -33,6 +33,10 @@ on: default: Debug type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + cancel-in-progress: true + jobs: # # Build Stride Runtime for iOS diff --git a/.github/workflows/build-launcher.yml b/.github/workflows/build-launcher.yml index bcced9d613..c2faab22ff 100644 --- a/.github/workflows/build-launcher.yml +++ b/.github/workflows/build-launcher.yml @@ -31,6 +31,10 @@ on: default: Debug type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + cancel-in-progress: true + jobs: # # Build Stride Runtime for Windows diff --git a/.github/workflows/build-linux-runtime.yml b/.github/workflows/build-linux-runtime.yml index 531768b08f..4b9c52432c 100644 --- a/.github/workflows/build-linux-runtime.yml +++ b/.github/workflows/build-linux-runtime.yml @@ -43,6 +43,10 @@ on: default: OpenGL type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.graphics-api || inputs.graphics-api || 'OpenGL' }} + cancel-in-progress: true + jobs: # # Build Stride Runtime for Linux diff --git a/.github/workflows/build-vs-package.yml b/.github/workflows/build-vs-package.yml index f89b051ad0..a80b9413bb 100644 --- a/.github/workflows/build-vs-package.yml +++ b/.github/workflows/build-vs-package.yml @@ -28,6 +28,10 @@ on: default: Debug type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + cancel-in-progress: true + jobs: # # Build Visual Studio Package diff --git a/.github/workflows/build-windows-full.yml b/.github/workflows/build-windows-full.yml index c1f6ce681e..99b30f3ab5 100644 --- a/.github/workflows/build-windows-full.yml +++ b/.github/workflows/build-windows-full.yml @@ -28,6 +28,10 @@ on: default: Debug type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + cancel-in-progress: true + jobs: # # Build Stride for Windows diff --git a/.github/workflows/build-windows-runtime.yml b/.github/workflows/build-windows-runtime.yml index 6d0ded05a7..10b9ff4055 100644 --- a/.github/workflows/build-windows-runtime.yml +++ b/.github/workflows/build-windows-runtime.yml @@ -46,6 +46,10 @@ on: default: Direct3D11 type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.graphics-api || inputs.graphics-api || 'Direct3D11' }} + cancel-in-progress: true + jobs: # # Build Stride Runtime for Windows diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 35813c2aba..f940f82411 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,10 @@ on: - '!crowdin.yml' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: ### Misc. ### Assembly-Processor: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 551819bcce..6356087acc 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -26,13 +26,17 @@ on: default: Simple type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }} + cancel-in-progress: true + jobs: # # Test Stride on Linux # Linux-Tests: if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || github.event.pull_request.draft == false }} - name: Test (${{ github.event.inputs.build-type || inputs.build-type }}, ${{ github.event.inputs.test-category || inputs.test-category }}) + name: Test (${{ github.event.inputs.build-type || inputs.build-type }}, ${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }}) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -43,7 +47,7 @@ jobs: dotnet-version: '8.0.x' - name: Build run: | - dotnet build build\Stride.Tests.${{ github.event.inputs.test-category || inputs.test-category }}.slnf ` + dotnet build build\Stride.Tests.${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }}.slnf ` -restore -m:1 -nr:false ` -v:m -p:WarningLevel=0 ` -p:Configuration=${{ github.event.inputs.build-type || inputs.build-type }} ` @@ -51,6 +55,6 @@ jobs: -p:StrideGraphicsApis=OpenGL - name: Test run: | - dotnet test build\Stride.Tests.${{ github.event.inputs.test-category || inputs.test-category }}.slnf ` + dotnet test build\Stride.Tests.${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }}.slnf ` --no-build ` -p:Configuration=${{ github.event.inputs.build-type || inputs.build-type }} diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 449a83eb6e..cc81400a2d 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -39,6 +39,10 @@ on: default: Simple type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }} + cancel-in-progress: true + jobs: # # Test Stride on Windows From d64fedd7d5157527246c19d29885887529f2a654 Mon Sep 17 00:00:00 2001 From: Nicolas Musset Date: Mon, 2 Jun 2025 13:27:32 +0200 Subject: [PATCH 18/37] fix: concurrency group names can collide when running from the CI (main) workflow (#2809) --- .github/workflows/build-android.yml | 2 +- .github/workflows/build-assembly-processor.yml | 2 +- .github/workflows/build-ios.yml | 2 +- .github/workflows/build-launcher.yml | 2 +- .github/workflows/build-linux-runtime.yml | 2 +- .github/workflows/build-vs-package.yml | 2 +- .github/workflows/build-windows-full.yml | 2 +- .github/workflows/build-windows-runtime.yml | 2 +- .github/workflows/test-linux.yml | 2 +- .github/workflows/test-windows.yml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index 239f2c7c73..27782be9e4 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -34,7 +34,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + group: build-android-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-assembly-processor.yml b/.github/workflows/build-assembly-processor.yml index 4b208ea5a2..6eda02accf 100644 --- a/.github/workflows/build-assembly-processor.yml +++ b/.github/workflows/build-assembly-processor.yml @@ -30,7 +30,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + group: build-assembly-processor-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 2cad4b67b4..7a713efef6 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -34,7 +34,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + group: build-ios-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-launcher.yml b/.github/workflows/build-launcher.yml index c2faab22ff..11258e4aa2 100644 --- a/.github/workflows/build-launcher.yml +++ b/.github/workflows/build-launcher.yml @@ -32,7 +32,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + group: build-launcher-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-linux-runtime.yml b/.github/workflows/build-linux-runtime.yml index 4b9c52432c..baea1644d8 100644 --- a/.github/workflows/build-linux-runtime.yml +++ b/.github/workflows/build-linux-runtime.yml @@ -44,7 +44,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.graphics-api || inputs.graphics-api || 'OpenGL' }} + group: build-linux-runtime-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.graphics-api || inputs.graphics-api || 'OpenGL' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-vs-package.yml b/.github/workflows/build-vs-package.yml index a80b9413bb..03427c189f 100644 --- a/.github/workflows/build-vs-package.yml +++ b/.github/workflows/build-vs-package.yml @@ -29,7 +29,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + group: build-vs-package-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-windows-full.yml b/.github/workflows/build-windows-full.yml index 99b30f3ab5..d18fc15037 100644 --- a/.github/workflows/build-windows-full.yml +++ b/.github/workflows/build-windows-full.yml @@ -29,7 +29,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} + group: build-windows-full-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/build-windows-runtime.yml b/.github/workflows/build-windows-runtime.yml index 10b9ff4055..60008aef7f 100644 --- a/.github/workflows/build-windows-runtime.yml +++ b/.github/workflows/build-windows-runtime.yml @@ -47,7 +47,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.graphics-api || inputs.graphics-api || 'Direct3D11' }} + group: build-windows-runtime-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.graphics-api || inputs.graphics-api || 'Direct3D11' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 6356087acc..2124c3af7a 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -27,7 +27,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }} + group: test-linux-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }} cancel-in-progress: true jobs: diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index cc81400a2d..8d6136c078 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -40,7 +40,7 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }} + group: test-windows-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}-${{ github.event.inputs.test-category || inputs.test-category || 'Simple' }} cancel-in-progress: true jobs: From 27f87bf78e20cad0c5671ed782e0b46b6fef35e8 Mon Sep 17 00:00:00 2001 From: Eideren Date: Sat, 21 Jun 2025 13:41:07 +0200 Subject: [PATCH 19/37] chore: Increment PublicVersion Build number following the breaking change introduced in #2714 (#2822) --- sources/shared/SharedAssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/shared/SharedAssemblyInfo.cs b/sources/shared/SharedAssemblyInfo.cs index 949fe0282e..f1a976f73c 100644 --- a/sources/shared/SharedAssemblyInfo.cs +++ b/sources/shared/SharedAssemblyInfo.cs @@ -25,7 +25,7 @@ internal class StrideVersion /// /// The version used by editor for display purpose. The 4th digit will automatically be replaced by the git height when building packages with Stride.Build. /// - public const string PublicVersion = "4.2.0.1"; + public const string PublicVersion = "4.2.1.1"; /// /// The current assembly version as text, currently same as . @@ -71,4 +71,4 @@ internal partial class PublicKeys #else public const string Default = ""; #endif -} \ No newline at end of file +} From 8723abe05256e87e32bed9950ed3cc9ca7c95332 Mon Sep 17 00:00:00 2001 From: Donovan Prezeau Date: Thu, 8 May 2025 15:04:01 -0600 Subject: [PATCH 20/37] initial PoC --- build/Stride.sln | 15 ++++ sources/Directory.Packages.props | 4 +- .../Engine/Builder/GameBuilder.cs | 51 +++++++++++++ .../Engine/Builder/GameBuilderExtensions.cs | 72 +++++++++++++++++++ .../Engine/Builder/IGameBuilder.cs | 16 +++++ .../Engine/Builder/MinimalGame.cs | 43 +++++++++++ .../Engine/Design/GameSettings.cs | 2 - sources/engine/Stride.Games/GameBase.cs | 4 +- .../Stride.Games/GraphicsDeviceManager.cs | 2 +- sources/engine/Stride.Games/IGame.cs | 1 - sources/engine/Stride.Hosting/BasicGame.cs | 42 +++++++++++ .../Stride.Hosting/IStrideGameBuilder.cs | 16 +++++ .../Stride.Hosting/Stride.Hosting.csproj | 28 ++++++++ .../Stride.Hosting/StrideGameBuilder.cs | 39 ++++++++++ 14 files changed, 328 insertions(+), 7 deletions(-) create mode 100644 sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs create mode 100644 sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs create mode 100644 sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs create mode 100644 sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs create mode 100644 sources/engine/Stride.Hosting/BasicGame.cs create mode 100644 sources/engine/Stride.Hosting/IStrideGameBuilder.cs create mode 100644 sources/engine/Stride.Hosting/Stride.Hosting.csproj create mode 100644 sources/engine/Stride.Hosting/StrideGameBuilder.cs diff --git a/build/Stride.sln b/build/Stride.sln index c7060e2b2c..6283779106 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -338,6 +338,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.3D", "..\so EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.BepuPhysics.Tests", "..\sources\engine\Stride.BepuPhysics\Stride.BepuPhysics.Tests\Stride.BepuPhysics.Tests.csproj", "{7B70C783-4085-4702-B3C6-6570FD85CB8F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Hosting", "..\sources\engine\Stride.Hosting\Stride.Hosting.csproj", "{A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1523,6 +1525,18 @@ Global {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.ActiveCfg = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.Build.0 = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.ActiveCfg = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.Build.0 = Debug|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.Build.0 = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.ActiveCfg = Release|Any CPU + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1651,6 +1665,7 @@ Global {7715D094-DF59-4D91-BC9A-9A5118039ECB} = {DE048114-9AE4-467E-A879-188DC0D88A59} {66EFFDE4-24F0-4E57-9618-0F5577E20A1E} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} {7B70C783-4085-4702-B3C6-6570FD85CB8F} = {DE048114-9AE4-467E-A879-188DC0D88A59} + {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715} = {4C142567-C42B-40F5-B092-798882190209} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 28607e25a4..633d58c70b 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -11,6 +11,8 @@ + + @@ -144,4 +146,4 @@ - + \ No newline at end of file diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs new file mode 100644 index 0000000000..dd5a1b202d --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Games; + +namespace Stride.Engine.Builder; + +/// +/// Helps build the game and preps it to be able to run after built. +/// +/// +public class GameBuilder : IGameBuilder where T : IGame +{ + public IServiceRegistry Services { get; protected set; } + + public GameSystemCollection GameSystems { get; protected set; } + + public List LogListeners { get; protected set; } = []; + + public GameBase Game { get; protected set; } + + internal GameBuilder() + { + Game = new MinimalGame(); + Services = Game.Services; + GameSystems = Game.GameSystems; + } + + public static GameBuilder Create() + { + return new GameBuilder(); + } + + public virtual GameBase Build() + { + foreach (var logListener in LogListeners) + { + GlobalLogger.GlobalMessageLogged += logListener; + } + + return Game; + } +} + +/// +/// Creates a default GameBuilder for a . +/// +public class GameBuilder : GameBuilder +{ + +} diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs new file mode 100644 index 0000000000..ead167468a --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -0,0 +1,72 @@ +using System; +using Stride.Core.Diagnostics; +using Stride.Core.IO; +using Stride.Core.Storage; +using Stride.Games; +using Stride.Rendering; +using Stride.Shaders.Compiler; + +namespace Stride.Engine.Builder; +public static class GameBuilderExtensions +{ + public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gameSystem) where T : IGameSystemBase + { + gameBuilder.GameSystems.Add(gameSystem); + return gameBuilder; + } + + public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class + { + gameBuilder.Services.AddService(service); + return gameBuilder; + } + + public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogListener logListener) + { + gameBuilder.LogListeners.Add(logListener); + return gameBuilder; + } + + public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) + { + // Gets initialized by the GameBase constructor. + var dataBase = gameBuilder.Services.GetService(); + // There should probably be a change to the interface to avoid the below casting. + ((DatabaseFileProviderService)dataBase).FileProvider = provider; + return gameBuilder; + } + + public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) + { + using (Profiler.Begin(GameProfilingKeys.ObjectDatabaseInitialize)) + { + // Create and mount database file system + var objDatabase = ObjectDatabase.CreateDefaultDatabase(); + + // Only set a mount path if not mounted already + var mountPath = VirtualFileSystem.ResolveProviderUnsafe("/asset", true).Provider == null ? "/asset" : null; + var result = new DatabaseFileProvider(objDatabase, mountPath); + + gameBuilder.AddDbFileProvider(result); + } + + return gameBuilder; + } + + public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectCompiler, IVirtualFileProvider fileProvider) + { + EffectCompilerBase compiler = new EffectCompiler(fileProvider) + { + SourceDirectories = { EffectCompilerBase.DefaultSourceShaderFolder }, + }; + + if(fileProvider is DatabaseFileProvider databaseFileProvider) + { + effectCompiler.Compiler = new EffectCompilerCache(compiler, databaseFileProvider); + return effectCompiler; + } + + throw new ArgumentException("The file provider must be a DatabaseFileProvider", nameof(fileProvider)); + } + +} diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs new file mode 100644 index 0000000000..f658479a7b --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Stride.Core.Diagnostics; +using Stride.Core; +using Stride.Games; + +namespace Stride.Engine.Builder; +public interface IGameBuilder +{ + IServiceRegistry Services { get; } + + GameSystemCollection GameSystems { get; } + + List LogListeners { get; } + + GameBase Game { get; } +} diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs new file mode 100644 index 0000000000..b13acc624b --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -0,0 +1,43 @@ +using System; +using Stride.Core.Serialization; +using Stride.Core.Streaming; +using Stride.Games; +using Stride.Graphics; + +namespace Stride.Engine.Builder; +public class MinimalGame : GameBase +{ + + /// + /// Gets the graphics device manager. + /// + /// The graphics device manager. + public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } + + public MinimalGame() + { + // Creates the graphics device manager + GraphicsDeviceManager = new GraphicsDeviceManager(this); + Services.AddService(GraphicsDeviceManager); + Services.AddService(GraphicsDeviceManager); + } + + public override void ConfirmRenderingSettings(bool gameCreation) + { + var deviceManager = (GraphicsDeviceManager)graphicsDeviceManager; + + if (gameCreation) + { + //if our device width or height is actually smaller then requested we use the device one + deviceManager.PreferredBackBufferWidth = Context.RequestedWidth = Math.Min(deviceManager.PreferredBackBufferWidth, Window.ClientBounds.Width); + deviceManager.PreferredBackBufferHeight = Context.RequestedHeight = Math.Min(deviceManager.PreferredBackBufferHeight, Window.ClientBounds.Height); + } + } + + protected override void Initialize() + { + base.Initialize(); + + Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); + } +} diff --git a/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs b/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs index a012e43d9d..7bfd6fab92 100644 --- a/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs +++ b/sources/engine/Stride.Engine/Engine/Design/GameSettings.cs @@ -1,12 +1,10 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. -using System; using Stride.Core; using Stride.Core.Mathematics; using Stride.Core.Serialization.Contents; using Stride.Data; -using Stride.Graphics; namespace Stride.Engine.Design { diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 9fd977372d..97df14822d 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -59,7 +59,7 @@ public abstract class GameBase : ComponentBase, IGame private bool isMouseVisible; - internal object TickLock = new object(); + internal object TickLock = new(); #endregion @@ -402,7 +402,7 @@ internal void InitializeBeforeRun() /// /// The window Context for this game. /// Cannot run this instance while it is already running - public void Run(GameContext gameContext = null) + public virtual void Run(GameContext gameContext = null) { if (IsRunning) { diff --git a/sources/engine/Stride.Games/GraphicsDeviceManager.cs b/sources/engine/Stride.Games/GraphicsDeviceManager.cs index 86cfab0220..90c47f779f 100644 --- a/sources/engine/Stride.Games/GraphicsDeviceManager.cs +++ b/sources/engine/Stride.Games/GraphicsDeviceManager.cs @@ -100,7 +100,7 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra /// /// The game. /// The game instance cannot be null. - internal GraphicsDeviceManager(GameBase game) + public GraphicsDeviceManager(GameBase game) { this.game = game; if (this.game == null) diff --git a/sources/engine/Stride.Games/IGame.cs b/sources/engine/Stride.Games/IGame.cs index c8da0cf304..6456ee6927 100644 --- a/sources/engine/Stride.Games/IGame.cs +++ b/sources/engine/Stride.Games/IGame.cs @@ -4,7 +4,6 @@ using Stride.Core; using Stride.Core.Serialization.Contents; -using Stride.Games.Time; using Stride.Graphics; namespace Stride.Games diff --git a/sources/engine/Stride.Hosting/BasicGame.cs b/sources/engine/Stride.Hosting/BasicGame.cs new file mode 100644 index 0000000000..1b599d454c --- /dev/null +++ b/sources/engine/Stride.Hosting/BasicGame.cs @@ -0,0 +1,42 @@ +using System; +using Stride.Core.Serialization; +using Stride.Games; +using Stride.Graphics; + +namespace Stride.Hosting; +public class BasicGame : GameBase +{ + + /// + /// Gets the graphics device manager. + /// + /// The graphics device manager. + public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } + + public BasicGame() + { + // Creates the graphics device manager + GraphicsDeviceManager = new GraphicsDeviceManager(this); + Services.AddService(GraphicsDeviceManager); + Services.AddService(GraphicsDeviceManager); + } + + public override void ConfirmRenderingSettings(bool gameCreation) + { + var deviceManager = (GraphicsDeviceManager)graphicsDeviceManager; + + if (gameCreation) + { + //if our device width or height is actually smaller then requested we use the device one + deviceManager.PreferredBackBufferWidth = Math.Min(deviceManager.PreferredBackBufferWidth, Window.ClientBounds.Width); + deviceManager.PreferredBackBufferHeight = Math.Min(deviceManager.PreferredBackBufferHeight, Window.ClientBounds.Height); + } + } + + protected override void Initialize() + { + base.Initialize(); + + Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); + } +} diff --git a/sources/engine/Stride.Hosting/IStrideGameBuilder.cs b/sources/engine/Stride.Hosting/IStrideGameBuilder.cs new file mode 100644 index 0000000000..3326ba14fb --- /dev/null +++ b/sources/engine/Stride.Hosting/IStrideGameBuilder.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Games; + +namespace Stride.Hosting; +public interface IStrideGameBuilder +{ + public IServiceRegistry Services { get; } + + GameSystemCollection GameSystems { get; } + + List LogListeners { get; } + + GameBase Game { get; } +} diff --git a/sources/engine/Stride.Hosting/Stride.Hosting.csproj b/sources/engine/Stride.Hosting/Stride.Hosting.csproj new file mode 100644 index 0000000000..dbf6858a2e --- /dev/null +++ b/sources/engine/Stride.Hosting/Stride.Hosting.csproj @@ -0,0 +1,28 @@ + + + + true + + + + true + true + * + + + + + Properties\SharedAssemblyInfo.cs + + + + + + + + + + + + + diff --git a/sources/engine/Stride.Hosting/StrideGameBuilder.cs b/sources/engine/Stride.Hosting/StrideGameBuilder.cs new file mode 100644 index 0000000000..0ba4f91eda --- /dev/null +++ b/sources/engine/Stride.Hosting/StrideGameBuilder.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Stride.Core.Diagnostics; +using Stride.Games; +using Stride.Core; + +namespace Stride.Hosting; +public class StrideGameBuilder : IStrideGameBuilder +{ + public IServiceRegistry Services { get; protected set; } + + public GameSystemCollection GameSystems { get; protected set; } + + public List LogListeners { get; protected set; } = []; + + public GameBase Game { get; protected set; } + + internal StrideGameBuilder() + { + Game = new BasicGame(); + Services = Game.Services; + GameSystems = Game.GameSystems; + } + + public static StrideGameBuilder Create() + { + return new StrideGameBuilder(); + } + + public virtual GameBase Build() + { + foreach (var logListener in LogListeners) + { + GlobalLogger.GlobalMessageLogged += logListener; + } + + return Game; + } +} From 7590a43a41f344aceb54016f60b46139724b73aa Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Fri, 9 May 2025 15:02:01 -0600 Subject: [PATCH 21/37] reorganizing code to work with GameStudio --- .../MicrothreadLocalDatabases.cs | 7 +- .../IO/IDatabaseFileProviderService.cs | 4 +- .../Game/EntityHierarchyEditorGame.cs | 3 +- .../EditorGame/Game/EditorServiceGame.cs | 5 + .../Stride.Editor/Engine/EmbeddedGame.cs | 3 +- .../Preview/GameStudioPreviewService.cs | 2 +- .../Stride.Editor/Preview/PreviewGame.cs | 3 +- .../Engine/Builder/GameBuilder.cs | 16 +- .../Engine/Builder/GameBuilderExtensions.cs | 36 +++++ .../Engine/Builder/MinimalGame.cs | 38 ++++- sources/engine/Stride.Engine/Engine/Game.cs | 21 ++- .../Android/GamePlatformAndroid.cs | 2 +- .../Desktop/GamePlatformDesktop.cs | 2 +- sources/engine/Stride.Games/GameBase.cs | 147 ++++++++++++------ sources/engine/Stride.Games/GameContext.cs | 10 +- sources/engine/Stride.Games/GamePlatform.cs | 38 +++-- .../Stride.Games/GraphicsDeviceManager.cs | 3 +- sources/engine/Stride.Games/IGame.cs | 4 + sources/engine/Stride.Games/IGamePlatform.cs | 3 + .../WindowsStore/GamePlatformUWP.cs | 4 +- .../Stride.Games/iOS/GamePlatformiOS.cs | 2 +- sources/engine/Stride.Input/InputManager.cs | 3 +- 22 files changed, 262 insertions(+), 94 deletions(-) diff --git a/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs b/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs index 8c892bea0e..1a99c7f97f 100644 --- a/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs +++ b/sources/buildengine/Stride.Core.BuildEngine.Common/MicrothreadLocalDatabases.cs @@ -113,6 +113,11 @@ private static DatabaseFileProvider CreateDatabase(BuildTransaction transaction) private class MicroThreadLocalProviderService : IDatabaseFileProviderService { - public DatabaseFileProvider FileProvider => MicroThreadLocalDatabaseFileProvider.Value; + public DatabaseFileProvider FileProvider + { + get => MicroThreadLocalDatabaseFileProvider.Value; + set => MicroThreadLocalDatabaseFileProvider.Value = value; + //throw new InvalidOperationException($"Can not change the value of a {nameof(MicroThreadLocalProviderService.FileProvider)}"); + } } } diff --git a/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs b/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs index 93c36db2f8..7d251cd2c2 100644 --- a/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs +++ b/sources/core/Stride.Core.Serialization/IO/IDatabaseFileProviderService.cs @@ -5,5 +5,5 @@ namespace Stride.Core.IO; public interface IDatabaseFileProviderService { - DatabaseFileProvider FileProvider { get; } -} \ No newline at end of file + DatabaseFileProvider FileProvider { get; set; } +} diff --git a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs index 589ceaeccb..b0059ad35d 100644 --- a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs +++ b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EntityHierarchyEditorGame.cs @@ -48,7 +48,8 @@ public abstract class EntityHierarchyEditorGame : EditorServiceGame private Material fallbackColorMaterial; private Material fallbackTextureMaterial; - protected EntityHierarchyEditorGame(TaskCompletionSource gameContentLoadedTaskSource, IEffectCompiler effectCompiler, string effectLogPath) + protected EntityHierarchyEditorGame(TaskCompletionSource gameContentLoadedTaskSource, IEffectCompiler effectCompiler, string effectLogPath, GameContext context = null) + : base(context) { this.gameContentLoadedTaskSource = gameContentLoadedTaskSource; this.effectCompiler = effectCompiler; diff --git a/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs b/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs index bd04fdc2b0..25a80860c5 100644 --- a/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs +++ b/sources/editor/Stride.Editor/EditorGame/Game/EditorServiceGame.cs @@ -99,6 +99,11 @@ public bool IsEditorHidden public event EventHandler ExceptionThrown; + public EditorServiceGame(GameContext context) : base(context) + { + + } + /// /// Calculates and returns the position of the mouse in the scene. /// diff --git a/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs b/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs index 5eab6d24fc..66d262c9c3 100644 --- a/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs +++ b/sources/editor/Stride.Editor/Engine/EmbeddedGame.cs @@ -3,6 +3,7 @@ using Stride.Core.Diagnostics; using Stride.Engine; +using Stride.Games; using Stride.Graphics; namespace Stride.Editor.Engine @@ -17,7 +18,7 @@ public class EmbeddedGame : Game /// public static bool DebugMode { get; set; } - public EmbeddedGame() + public EmbeddedGame(GameContext context) : base(context) { GraphicsDeviceManager.PreferredGraphicsProfile = new [] { GraphicsProfile.Level_11_0, GraphicsProfile.Level_10_1, GraphicsProfile.Level_10_0 }; GraphicsDeviceManager.PreferredBackBufferWidth = 64; diff --git a/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs b/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs index 522b71f911..99047e09fa 100644 --- a/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs +++ b/sources/editor/Stride.Editor/Preview/GameStudioPreviewService.cs @@ -137,8 +137,8 @@ private void StrideUIThread() initializationSignal.Set(); - PreviewGame = new PreviewGame(AssetBuilderService.EffectCompiler); var context = new GameContextWinforms(gameForm) { InitializeDatabase = false }; + PreviewGame = new PreviewGame(AssetBuilderService.EffectCompiler, context); // Wait for shaders to be loaded AssetBuilderService.WaitForShaders(); diff --git a/sources/editor/Stride.Editor/Preview/PreviewGame.cs b/sources/editor/Stride.Editor/Preview/PreviewGame.cs index c88ac25032..143ab1c307 100644 --- a/sources/editor/Stride.Editor/Preview/PreviewGame.cs +++ b/sources/editor/Stride.Editor/Preview/PreviewGame.cs @@ -14,6 +14,7 @@ using Stride.Graphics; using Stride.Rendering.Compositing; using Stride.Shaders.Compiler; +using Stride.Games; namespace Stride.Editor.Preview { @@ -43,7 +44,7 @@ public class PreviewGame : EditorGame.Game.EditorServiceGame private Scene previewScene; - public PreviewGame(IEffectCompiler effectCompiler) + public PreviewGame(IEffectCompiler effectCompiler, GameContext context) : base(context) { this.effectCompiler = effectCompiler; } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index dd5a1b202d..28001c7729 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -9,7 +9,7 @@ namespace Stride.Engine.Builder; /// Helps build the game and preps it to be able to run after built. /// /// -public class GameBuilder : IGameBuilder where T : IGame +public class GameBuilder : IGameBuilder { public IServiceRegistry Services { get; protected set; } @@ -21,14 +21,14 @@ public class GameBuilder : IGameBuilder where T : IGame internal GameBuilder() { - Game = new MinimalGame(); + Game = new MinimalGame(null); Services = Game.Services; GameSystems = Game.GameSystems; } - public static GameBuilder Create() + public static GameBuilder Create() { - return new GameBuilder(); + return new GameBuilder(); } public virtual GameBase Build() @@ -41,11 +41,3 @@ public virtual GameBase Build() return Game; } } - -/// -/// Creates a default GameBuilder for a . -/// -public class GameBuilder : GameBuilder -{ - -} diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index ead167468a..ba96b7864a 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,6 +1,8 @@ using System; +using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; +using Stride.Core.Serialization.Contents; using Stride.Core.Storage; using Stride.Games; using Stride.Rendering; @@ -27,6 +29,12 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList return gameBuilder; } + /// + /// Allows the user to add a custom database file provider to the game. + /// + /// + /// + /// public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. @@ -36,6 +44,11 @@ public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, Data return gameBuilder; } + /// + /// Creates a default database to be used in the game. + /// + /// + /// public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) { using (Profiler.Begin(GameProfilingKeys.ObjectDatabaseInitialize)) @@ -53,6 +66,22 @@ public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) return gameBuilder; } + public static IGameBuilder UseDefaultContentManager(this IGameBuilder gameBuilder) + { + var services = gameBuilder.Services; + var content = new ContentManager(services); + services.AddService(content); + services.AddService(content); + return gameBuilder; + } + + /// + /// Adds a default effect compiler to the game. This is used to compile shaders and effects. + /// + /// + /// + /// + /// public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectCompiler, IVirtualFileProvider fileProvider) { EffectCompilerBase compiler = new EffectCompiler(fileProvider) @@ -69,4 +98,11 @@ public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectComp throw new ArgumentException("The file provider must be a DatabaseFileProvider", nameof(fileProvider)); } + public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameContext context) + { + gameBuilder.Game.SetGameContext(context); + + return gameBuilder; + } + } diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs index b13acc624b..8042319f43 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -1,6 +1,7 @@ using System; +using Stride.Core.Diagnostics; using Stride.Core.Serialization; -using Stride.Core.Streaming; +using Stride.Core.Serialization.Contents; using Stride.Games; using Stride.Graphics; @@ -14,10 +15,25 @@ public class MinimalGame : GameBase /// The graphics device manager. public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } - public MinimalGame() + public MinimalGame(GameContext gameContext) : base() { + Context = gameContext ?? DetectDefaultContext(); + Context.CurrentGame = this; + + // Create Platform + Context.GamePlatform = GamePlatform.Create(Context); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + // Setup registry + Services.AddService(this); + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this); + GraphicsDeviceManager = new GraphicsDeviceManager(this, gameContext); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); } @@ -40,4 +56,20 @@ protected override void Initialize() Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); } + + protected override void PrepareContext() + { + //Allow the user to add their own ContentManager + var contentManager = Services.GetService(); + + if (contentManager is null) + { + Log.Info("No ContentManager found, creating default ContentManager"); + contentManager = new ContentManager(Services); + Services.AddService(contentManager); + Services.AddService(contentManager); + } + + Content = contentManager; + } } diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index 6c4c162ed5..7e06b1ba2d 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -188,8 +188,23 @@ public LogMessageType ConsoleLogLevel /// /// Initializes a new instance of the class. /// - public Game() + public Game(GameContext context = null) : base() { + Context = context ?? DetectDefaultContext(); + Context.CurrentGame = this; + + // Create Platform + Context.GamePlatform = GamePlatform.Create(Context); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + // Setup registry + Services.AddService(this); + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + // Register the logger backend before anything else logListener = GetLogListener(); @@ -227,7 +242,7 @@ public Game() Services.AddService(VRDeviceSystem); // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this); + GraphicsDeviceManager = new GraphicsDeviceManager(this, context); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); @@ -256,7 +271,7 @@ protected override void PrepareContext() if (Context.InitializeDatabase) { databaseFileProvider = InitializeAssetDatabase(); - ((DatabaseFileProviderService)Services.GetService()).FileProvider = databaseFileProvider; + Services.GetService().FileProvider = databaseFileProvider; var renderingSettings = new RenderingSettings(); if (Content.Exists(GameSettings.AssetUrl)) diff --git a/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs b/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs index 167afa863d..5db252837c 100644 --- a/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs +++ b/sources/engine/Stride.Games/Android/GamePlatformAndroid.cs @@ -28,7 +28,7 @@ private void PopulateFullName() FullName = $"{manufacturer} - {model}"; } - public GamePlatformAndroid(GameBase game) : base(game) + public GamePlatformAndroid(GameContext context) : base(context) { PopulateFullName(); } diff --git a/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs b/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs index 7d48fae49e..c3911ba734 100644 --- a/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs +++ b/sources/engine/Stride.Games/Desktop/GamePlatformDesktop.cs @@ -29,7 +29,7 @@ namespace Stride.Games { internal class GamePlatformDesktop : GamePlatform { - public GamePlatformDesktop(GameBase game) : base(game) + public GamePlatformDesktop(GameContext context) : base(context) { IsBlockingRun = true; #if (STRIDE_UI_WINFORMS || STRIDE_UI_WPF) diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 97df14822d..05de63e24d 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -41,7 +41,7 @@ public abstract class GameBase : ComponentBase, IGame { #region Fields - private readonly GamePlatform gamePlatform; + private GamePlatform gamePlatform => Context.GamePlatform; private IGraphicsDeviceService graphicsDeviceService; protected IGraphicsDeviceManager graphicsDeviceManager; private ResumeManager resumeManager; @@ -95,21 +95,23 @@ protected GameBase() GameSystems = new GameSystemCollection(Services); Services.AddService(GameSystems); - // Create Platform - gamePlatform = GamePlatform.Create(this); - gamePlatform.Activated += GamePlatform_Activated; - gamePlatform.Deactivated += GamePlatform_Deactivated; - gamePlatform.Exiting += GamePlatform_Exiting; - gamePlatform.WindowCreated += GamePlatformOnWindowCreated; - - // Setup registry - Services.AddService(this); - Services.AddService(gamePlatform); - Services.AddService(gamePlatform); - IsActive = true; } + protected static GameContext DetectDefaultContext() + { +#if STRIDE_PLATFORM_UWP + return GameContextFactory.NewGameContextUWPXaml(); +#elif STRIDE_PLATFORM_ANDROID + return GameContextFactory.NewGameContextAndroid(); +#elif STRIDE_PLATFORM_IOS + return GameContextFactory.NewGameContextiOS(); +#else + // Here we cover all Desktop variants: OpenTK, SDL, Winforms,... + return GameContextFactory.NewGameContextDesktop(); +#endif + } + #endregion #region Public Events @@ -165,7 +167,7 @@ protected GameBase() /// /// Gets the . /// - public ContentManager Content { get; private set; } + public ContentManager Content { get; protected set; } /// /// Gets the game components registered by this game. @@ -177,15 +179,15 @@ protected GameBase() /// Gets the game context. /// /// The game context. - public GameContext Context { get; private set; } + public GameContext Context { get; protected set; } /// /// Gets the graphics device. /// /// The graphics device. - public GraphicsDevice GraphicsDevice { get; private set; } + public GraphicsDevice GraphicsDevice { get; protected set; } - public GraphicsContext GraphicsContext { get; private set; } + public GraphicsContext GraphicsContext { get; protected set; } /// /// Gets or sets the time between each when is false. @@ -197,13 +199,13 @@ protected GameBase() /// Gets a value indicating whether this instance is active. /// /// true if this instance is active; otherwise, false. - public bool IsActive { get; private set; } + public bool IsActive { get; protected set; } /// /// Gets a value indicating whether this instance is exiting. /// /// true if this instance is exiting; otherwise, false. - public bool IsExiting{ get; private set; } + public bool IsExiting{ get; protected set; } /// /// Gets or sets a value indicating whether the elapsed time between each update should be constant, @@ -295,6 +297,7 @@ public bool IsMouseVisible /// Gets the abstract window. /// /// The window. + [Obsolete("Use GameContext.GameWindow instead.")] public GameWindow Window { get @@ -416,22 +419,40 @@ public virtual void Run(GameContext gameContext = null) throw new InvalidOperationException("No GraphicsDeviceManager found"); } + if(gameContext != null) + { + gameContext.GamePlatform = Context.GamePlatform; + Context = gameContext; + Context.CurrentGame = this; + + // Overwrite Platform + if (gamePlatform != null) + { + Context.GamePlatform.Activated -= GamePlatform_Activated; + Context.GamePlatform.Deactivated -= GamePlatform_Deactivated; + Context.GamePlatform.Exiting -= GamePlatform_Exiting; + Context.GamePlatform.WindowCreated -= GamePlatformOnWindowCreated; + + Services.RemoveService(); + Services.RemoveService(); + } + + Context.GamePlatform = GamePlatform.Create(gameContext); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + } + // Gets the GameWindow Context - if (gameContext == null) + EnsureGameContextIsSet(); + if(gameContext is null) { - AppContextType c; - if (OperatingSystem.IsWindows()) - c = AppContextType.Desktop; - else if (OperatingSystem.IsAndroid()) - c = AppContextType.Android; - else if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()) - c = AppContextType.iOS; - else - c = AppContextType.DesktopSDL; - gameContext = GameContextFactory.NewGameContext(c); + throw new InvalidOperationException("No GameContext found"); } - - Context = gameContext; PrepareContext(); @@ -446,7 +467,7 @@ public virtual void Run(GameContext gameContext = null) Context.RequestedGraphicsProfile = graphicsDeviceManagerImpl.PreferredGraphicsProfile; Context.DeviceCreationFlags = graphicsDeviceManagerImpl.DeviceCreationFlags; - gamePlatform.Run(Context); + gamePlatform.Run(); if (gamePlatform.IsBlockingRun) { @@ -468,6 +489,28 @@ public virtual void Run(GameContext gameContext = null) } } + /// + /// Attempts to get GameContext based on the current platform. + /// + private void EnsureGameContextIsSet() + { + // Gets the GameWindow Context + if (Context == null) + { + AppContextType c; + if (OperatingSystem.IsWindows()) + c = AppContextType.Desktop; + else if (OperatingSystem.IsAndroid()) + c = AppContextType.Android; + else if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS()) + c = AppContextType.iOS; + else + c = AppContextType.DesktopSDL; + + Context = GameContextFactory.NewGameContext(c); + } + } + /// /// Creates or updates before window and device are created. /// @@ -512,6 +555,26 @@ public void Tick() } } + public virtual void SetWindow(GameWindow window) + { + if (IsRunning) + { + throw new InvalidOperationException("Cannot set the game window while the game is running"); + } + + Context.GameWindow = window; + //Window = window; + } + + public virtual void SetGameContext(GameContext context) + { + if(IsRunning) + { + throw new InvalidOperationException("Cannot set the game context while the game is running"); + } + Context = context; + } + /// /// Calls automatically based on this game's setup, override it to implement your own system. /// @@ -586,7 +649,7 @@ protected virtual void RawTickProducer() RawTick(singleFrameElapsedTime, updateCount, drawLag / (float)TargetElapsedTime.Ticks, drawFrame); - var window = gamePlatform.MainWindow; + var window = Window; if (gamePlatform.IsBlockingRun) // throttle fps if Game.Tick() called from internal main loop { if (window.IsMinimized || window.Visible == false || (window.Focused == false && TreatNotFocusedLikeMinimized)) @@ -717,17 +780,13 @@ protected override void Destroy() for (int i = 0; i < array.Length; i++) { var disposable = array[i] as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } + disposable?.Dispose(); } // Reset graphics context GraphicsContext = null; - var disposableGraphicsManager = graphicsDeviceManager as IDisposable; - if (disposableGraphicsManager != null) + if (graphicsDeviceManager is IDisposable disposableGraphicsManager) { disposableGraphicsManager.Dispose(); } @@ -888,7 +947,7 @@ protected virtual void OnWindowCreated() WindowCreated?.Invoke(this, EventArgs.Empty); } - private void GamePlatformOnWindowCreated(object sender, EventArgs eventArgs) + protected void GamePlatformOnWindowCreated(object sender, EventArgs eventArgs) { Window.IsMouseVisible = isMouseVisible; OnWindowCreated(); @@ -912,7 +971,7 @@ protected virtual void UnloadContent() GameSystems.UnloadContent(); } - private void GamePlatform_Activated(object sender, EventArgs e) + protected void GamePlatform_Activated(object sender, EventArgs e) { if (!IsActive) { @@ -921,7 +980,7 @@ private void GamePlatform_Activated(object sender, EventArgs e) } } - private void GamePlatform_Deactivated(object sender, EventArgs e) + protected void GamePlatform_Deactivated(object sender, EventArgs e) { if (IsActive) { @@ -930,7 +989,7 @@ private void GamePlatform_Deactivated(object sender, EventArgs e) } } - private void GamePlatform_Exiting(object sender, EventArgs e) + protected void GamePlatform_Exiting(object sender, EventArgs e) { OnExiting(this, EventArgs.Empty); } diff --git a/sources/engine/Stride.Games/GameContext.cs b/sources/engine/Stride.Games/GameContext.cs index 8e2338269e..4fd1a2bc8a 100644 --- a/sources/engine/Stride.Games/GameContext.cs +++ b/sources/engine/Stride.Games/GameContext.cs @@ -30,7 +30,7 @@ namespace Stride.Games { /// - /// Contains context used to render the game (Control for WinForm, a DrawingSurface for WP8...etc.). + /// Contains context for the game and its core modules. /// public abstract class GameContext { @@ -56,6 +56,14 @@ public abstract class GameContext /// The run loop. public Action ExitCallback { get; internal set; } + public GameBase CurrentGame { get; set; } + + public GamePlatform GamePlatform { get; set; } + + public GameWindow GameWindow { get; set; } + + public IServiceRegistry Services { get; set; } + // TODO: remove these requested values. /// diff --git a/sources/engine/Stride.Games/GamePlatform.cs b/sources/engine/Stride.Games/GamePlatform.cs index 36848ae436..c5eab1e0e9 100644 --- a/sources/engine/Stride.Games/GamePlatform.cs +++ b/sources/engine/Stride.Games/GamePlatform.cs @@ -30,35 +30,38 @@ namespace Stride.Games { - internal abstract class GamePlatform : ReferenceBase, IGraphicsDeviceFactory, IGamePlatform + public abstract class GamePlatform : ReferenceBase, IGraphicsDeviceFactory, IGamePlatform { private bool hasExitRan = false; - protected readonly GameBase game; - protected readonly IServiceRegistry Services; - protected GameWindow gameWindow; + protected GameBase game => context.CurrentGame; + + protected GameWindow gameWindow => context.GameWindow; + + protected GameContext context; public string FullName { get; protected set; } = string.Empty; - protected GamePlatform(GameBase game) + protected GamePlatform(GameContext context) { - this.game = game; - Services = game.Services; + Services = context.Services; + this.context = context; + this.context.GamePlatform = this; } - public static GamePlatform Create(GameBase game) + public static GamePlatform Create(GameContext context) { #if STRIDE_PLATFORM_UWP - return new GamePlatformUWP(game); + return new GamePlatformUWP(context); #elif STRIDE_PLATFORM_ANDROID - return new GamePlatformAndroid(game); + return new GamePlatformAndroid(context); #elif STRIDE_PLATFORM_IOS - return new GamePlatformiOS(game); + return new GamePlatformiOS(context); #else // Here we cover all Desktop variants: OpenTK, SDL, Winforms,... - return new GamePlatformDesktop(game); + return new GamePlatformDesktop(context); #endif } @@ -103,6 +106,7 @@ public virtual GameWindow CreateWindow(GameContext gameContext) window.PreferredFullscreenSize = requestedSize; window.Initialize(gameContext); + context.GameWindow = window; return window; } @@ -115,11 +119,11 @@ public virtual GameWindow CreateWindow(GameContext gameContext) /// public bool IsBlockingRun { get; protected set; } - public void Run(GameContext gameContext) + public void Run() { - IsBlockingRun = !gameContext.IsUserManagingRun; + IsBlockingRun = !context.IsUserManagingRun; - gameWindow = CreateWindow(gameContext); + context.GameWindow = CreateWindow(context); // Register on Activated gameWindow.Activated += OnActivated; @@ -293,7 +297,7 @@ public virtual List FindBestDevices(GameGraphicsParam IsFullScreen = preferredParameters.IsFullScreen, PreferredFullScreenOutputIndex = preferredParameters.PreferredFullScreenOutputIndex, PresentationInterval = preferredParameters.SynchronizeWithVerticalRetrace ? PresentInterval.One : PresentInterval.Immediate, - DeviceWindowHandle = MainWindow.NativeWindow, + DeviceWindowHandle = context.GameWindow.NativeWindow, ColorSpace = preferredParameters.ColorSpace, }, }; @@ -380,7 +384,7 @@ protected override void Destroy() if (gameWindow != null) { gameWindow.Dispose(); - gameWindow = null; + context.GameWindow = null; } Activated = null; diff --git a/sources/engine/Stride.Games/GraphicsDeviceManager.cs b/sources/engine/Stride.Games/GraphicsDeviceManager.cs index 90c47f779f..00be755193 100644 --- a/sources/engine/Stride.Games/GraphicsDeviceManager.cs +++ b/sources/engine/Stride.Games/GraphicsDeviceManager.cs @@ -100,7 +100,7 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra /// /// The game. /// The game instance cannot be null. - public GraphicsDeviceManager(GameBase game) + public GraphicsDeviceManager(GameBase game, GameContext context = null) { this.game = game; if (this.game == null) @@ -661,6 +661,7 @@ protected virtual GraphicsDeviceInformation FindBestDevice(bool anySuitableDevic preferredParameters.PreferredBackBufferHeight = resizedBackBufferHeight; } + graphicsDeviceFactory = game.Services.GetService(); var devices = graphicsDeviceFactory.FindBestDevices(preferredParameters); if (devices.Count == 0) { diff --git a/sources/engine/Stride.Games/IGame.cs b/sources/engine/Stride.Games/IGame.cs index 6456ee6927..8e55414ba2 100644 --- a/sources/engine/Stride.Games/IGame.cs +++ b/sources/engine/Stride.Games/IGame.cs @@ -139,5 +139,9 @@ public interface IGame /// /// The window. GameWindow Window { get; } + + public void SetWindow(GameWindow window); + + public void SetGameContext(GameContext context); } } diff --git a/sources/engine/Stride.Games/IGamePlatform.cs b/sources/engine/Stride.Games/IGamePlatform.cs index 9db9b9618f..ea4671c2dc 100644 --- a/sources/engine/Stride.Games/IGamePlatform.cs +++ b/sources/engine/Stride.Games/IGamePlatform.cs @@ -1,5 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; + namespace Stride.Games { /// @@ -17,6 +19,7 @@ public interface IGamePlatform /// Gets the main window. /// /// The main window. + [Obsolete("Use GameContext.MainWindow instead. This property will be removed in a future version.")] GameWindow MainWindow { get; } /// diff --git a/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs b/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs index 466ac7fc17..87cfa1bbc7 100644 --- a/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs +++ b/sources/engine/Stride.Games/WindowsStore/GamePlatformUWP.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. // // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel @@ -32,7 +32,7 @@ namespace Stride.Games { internal class GamePlatformUWP : GamePlatform { - public GamePlatformUWP(GameBase game) : base(game) + public GamePlatformUWP(GameContext context) : base(context) { // Application lifecycle reference: // https://docs.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle diff --git a/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs b/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs index 70f2c9ddbd..5ca80248c4 100644 --- a/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs +++ b/sources/engine/Stride.Games/iOS/GamePlatformiOS.cs @@ -27,7 +27,7 @@ private unsafe void PopulateFullName() Marshal.FreeHGlobal(output); } - public GamePlatformiOS(GameBase game) : base(game) + public GamePlatformiOS(GameContext context) : base(context) { PopulateFullName(); } diff --git a/sources/engine/Stride.Input/InputManager.cs b/sources/engine/Stride.Input/InputManager.cs index 5b16714c14..7c91017ac3 100644 --- a/sources/engine/Stride.Input/InputManager.cs +++ b/sources/engine/Stride.Input/InputManager.cs @@ -704,7 +704,8 @@ private void AddSources() #endif break; default: - throw new InvalidOperationException("GameContext type is not supported by the InputManager"); + Logger.Warning("GameContext type is not supported by the InputManager. Register your own for input to be handled properly."); + break; } } From 6f02a8beefa88c149d471bfd15c25fe6dc920743 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Fri, 9 May 2025 15:17:34 -0600 Subject: [PATCH 22/37] clean up --- build/Stride.sln | 15 ------- sources/engine/Stride.Games/GameBase.cs | 4 +- sources/engine/Stride.Hosting/BasicGame.cs | 42 ------------------- .../Stride.Hosting/IStrideGameBuilder.cs | 16 ------- .../Stride.Hosting/Stride.Hosting.csproj | 28 ------------- .../Stride.Hosting/StrideGameBuilder.cs | 39 ----------------- 6 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 sources/engine/Stride.Hosting/BasicGame.cs delete mode 100644 sources/engine/Stride.Hosting/IStrideGameBuilder.cs delete mode 100644 sources/engine/Stride.Hosting/Stride.Hosting.csproj delete mode 100644 sources/engine/Stride.Hosting/StrideGameBuilder.cs diff --git a/build/Stride.sln b/build/Stride.sln index 6283779106..c7060e2b2c 100644 --- a/build/Stride.sln +++ b/build/Stride.sln @@ -338,8 +338,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Importer.3D", "..\so EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.BepuPhysics.Tests", "..\sources\engine\Stride.BepuPhysics\Stride.BepuPhysics.Tests\Stride.BepuPhysics.Tests.csproj", "{7B70C783-4085-4702-B3C6-6570FD85CB8F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Hosting", "..\sources\engine\Stride.Hosting\Stride.Hosting.csproj", "{A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1525,18 +1523,6 @@ Global {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.ActiveCfg = Release|Any CPU {7B70C783-4085-4702-B3C6-6570FD85CB8F}.Release|Win32.Build.0 = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.ActiveCfg = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Debug|Win32.Build.0 = Debug|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Any CPU.Build.0 = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.ActiveCfg = Release|Any CPU - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715}.Release|Win32.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1665,7 +1651,6 @@ Global {7715D094-DF59-4D91-BC9A-9A5118039ECB} = {DE048114-9AE4-467E-A879-188DC0D88A59} {66EFFDE4-24F0-4E57-9618-0F5577E20A1E} = {6F473FA6-4F8B-4FBA-AE33-EE5AF997D50C} {7B70C783-4085-4702-B3C6-6570FD85CB8F} = {DE048114-9AE4-467E-A879-188DC0D88A59} - {A8C9E40D-B141-4D2E-A3BD-24D5C88DA715} = {4C142567-C42B-40F5-B092-798882190209} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF877973-604D-4EA7-B5F5-A129961F9EF2} diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 05de63e24d..9531bf2332 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -419,7 +419,7 @@ public virtual void Run(GameContext gameContext = null) throw new InvalidOperationException("No GraphicsDeviceManager found"); } - if(gameContext != null) + if(Context is not null && gameContext is not null) { gameContext.GamePlatform = Context.GamePlatform; Context = gameContext; @@ -449,7 +449,7 @@ public virtual void Run(GameContext gameContext = null) // Gets the GameWindow Context EnsureGameContextIsSet(); - if(gameContext is null) + if(Context is null) { throw new InvalidOperationException("No GameContext found"); } diff --git a/sources/engine/Stride.Hosting/BasicGame.cs b/sources/engine/Stride.Hosting/BasicGame.cs deleted file mode 100644 index 1b599d454c..0000000000 --- a/sources/engine/Stride.Hosting/BasicGame.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using Stride.Core.Serialization; -using Stride.Games; -using Stride.Graphics; - -namespace Stride.Hosting; -public class BasicGame : GameBase -{ - - /// - /// Gets the graphics device manager. - /// - /// The graphics device manager. - public GraphicsDeviceManager GraphicsDeviceManager { get; internal set; } - - public BasicGame() - { - // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this); - Services.AddService(GraphicsDeviceManager); - Services.AddService(GraphicsDeviceManager); - } - - public override void ConfirmRenderingSettings(bool gameCreation) - { - var deviceManager = (GraphicsDeviceManager)graphicsDeviceManager; - - if (gameCreation) - { - //if our device width or height is actually smaller then requested we use the device one - deviceManager.PreferredBackBufferWidth = Math.Min(deviceManager.PreferredBackBufferWidth, Window.ClientBounds.Width); - deviceManager.PreferredBackBufferHeight = Math.Min(deviceManager.PreferredBackBufferHeight, Window.ClientBounds.Height); - } - } - - protected override void Initialize() - { - base.Initialize(); - - Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); - } -} diff --git a/sources/engine/Stride.Hosting/IStrideGameBuilder.cs b/sources/engine/Stride.Hosting/IStrideGameBuilder.cs deleted file mode 100644 index 3326ba14fb..0000000000 --- a/sources/engine/Stride.Hosting/IStrideGameBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using Stride.Core; -using Stride.Core.Diagnostics; -using Stride.Games; - -namespace Stride.Hosting; -public interface IStrideGameBuilder -{ - public IServiceRegistry Services { get; } - - GameSystemCollection GameSystems { get; } - - List LogListeners { get; } - - GameBase Game { get; } -} diff --git a/sources/engine/Stride.Hosting/Stride.Hosting.csproj b/sources/engine/Stride.Hosting/Stride.Hosting.csproj deleted file mode 100644 index dbf6858a2e..0000000000 --- a/sources/engine/Stride.Hosting/Stride.Hosting.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - true - - - - true - true - * - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - diff --git a/sources/engine/Stride.Hosting/StrideGameBuilder.cs b/sources/engine/Stride.Hosting/StrideGameBuilder.cs deleted file mode 100644 index 0ba4f91eda..0000000000 --- a/sources/engine/Stride.Hosting/StrideGameBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using Stride.Core.Diagnostics; -using Stride.Games; -using Stride.Core; - -namespace Stride.Hosting; -public class StrideGameBuilder : IStrideGameBuilder -{ - public IServiceRegistry Services { get; protected set; } - - public GameSystemCollection GameSystems { get; protected set; } - - public List LogListeners { get; protected set; } = []; - - public GameBase Game { get; protected set; } - - internal StrideGameBuilder() - { - Game = new BasicGame(); - Services = Game.Services; - GameSystems = Game.GameSystems; - } - - public static StrideGameBuilder Create() - { - return new StrideGameBuilder(); - } - - public virtual GameBase Build() - { - foreach (var logListener in LogListeners) - { - GlobalLogger.GlobalMessageLogged += logListener; - } - - return Game; - } -} From 9f7bd5259e364cf0ee615d6985994843e2a7f69d Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 10 May 2025 14:06:08 -0600 Subject: [PATCH 23/37] fixing input issues --- .../Engine/Builder/GameBuilderExtensions.cs | 25 +++++- .../Engine/Builder/MinimalGame.cs | 17 +++- sources/engine/Stride.Engine/Engine/Game.cs | 48 ++++++++++- sources/engine/Stride.Games/GameBase.cs | 80 +++++++++---------- sources/engine/Stride.Games/GamePlatform.cs | 4 +- .../Stride.Games/GraphicsDeviceManager.cs | 12 +-- sources/engine/Stride.Games/IGame.cs | 6 +- sources/engine/Stride.Input/InputManager.cs | 7 +- .../engine/Stride.Input/SDL/InputSourceSDL.cs | 2 +- .../Windows/InputSourceWinforms.cs | 2 +- 10 files changed, 137 insertions(+), 66 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index ba96b7864a..0b45d86da8 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,10 +1,10 @@ using System; -using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; using Stride.Core.Serialization.Contents; using Stride.Core.Storage; using Stride.Games; +using Stride.Input; using Stride.Rendering; using Stride.Shaders.Compiler; @@ -29,6 +29,12 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList return gameBuilder; } + public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameContext context) + { + gameBuilder.Game.SetGameContext(context); + return gameBuilder; + } + /// /// Allows the user to add a custom database file provider to the game. /// @@ -39,8 +45,7 @@ public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, Data { // Gets initialized by the GameBase constructor. var dataBase = gameBuilder.Services.GetService(); - // There should probably be a change to the interface to avoid the below casting. - ((DatabaseFileProviderService)dataBase).FileProvider = provider; + dataBase.FileProvider = provider; return gameBuilder; } @@ -105,4 +110,18 @@ public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameCon return gameBuilder; } + public static IGameBuilder AddInput(this IGameBuilder gameBuilder, IInputSource inputSource) + { + var inputManager = gameBuilder.Services.GetService(); + + if (inputManager == null) + { + throw new InvalidOperationException("InputManager is not registered in the service registry."); + } + + inputManager.Sources.Add(inputSource); + + return gameBuilder; + } + } diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs index 8042319f43..62e77076ea 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -4,8 +4,13 @@ using Stride.Core.Serialization.Contents; using Stride.Games; using Stride.Graphics; +using Stride.Input; namespace Stride.Engine.Builder; + +/// +/// A game class with no registered systems by default. +/// public class MinimalGame : GameBase { @@ -17,7 +22,7 @@ public class MinimalGame : GameBase public MinimalGame(GameContext gameContext) : base() { - Context = gameContext ?? DetectDefaultContext(); + Context = gameContext ?? GetDefaultContext(); Context.CurrentGame = this; // Create Platform @@ -33,7 +38,7 @@ public MinimalGame(GameContext gameContext) : base() Services.AddService(Context.GamePlatform); // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this, gameContext); + GraphicsDeviceManager = new GraphicsDeviceManager(this); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); } @@ -55,6 +60,14 @@ protected override void Initialize() base.Initialize(); Content.Serializer.LowLevelSerializerSelector = new SerializerSelector("Default", "Content"); + + // Add window specific input source + var inputManager = Services.GetService(); + if (inputManager is not null) + { + var windowInputSource = InputSourceFactory.NewWindowInputSource(Context); + inputManager.Sources.Add(windowInputSource); + } } protected override void PrepareContext() diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index 7e06b1ba2d..0094175372 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -190,7 +190,7 @@ public LogMessageType ConsoleLogLevel /// public Game(GameContext context = null) : base() { - Context = context ?? DetectDefaultContext(); + Context = context ?? GetDefaultContext(); Context.CurrentGame = this; // Create Platform @@ -242,7 +242,7 @@ public Game(GameContext context = null) : base() Services.AddService(VRDeviceSystem); // Creates the graphics device manager - GraphicsDeviceManager = new GraphicsDeviceManager(this, context); + GraphicsDeviceManager = new GraphicsDeviceManager(this); Services.AddService(GraphicsDeviceManager); Services.AddService(GraphicsDeviceManager); @@ -357,6 +357,7 @@ protected override void Initialize() Input = inputSystem.Manager; Services.AddService(Input); GameSystems.Add(inputSystem); + SetInitialInputSources(Input); // Initialize the systems base.Initialize(); @@ -408,6 +409,49 @@ protected override void Initialize() OnGameStarted(this); } + private void SetInitialInputSources(InputManager inputManager) + { + // Add window specific input source + var windowInputSource = InputSourceFactory.NewWindowInputSource(Context); + inputManager.Sources.Add(windowInputSource); + + // Add platform specific input sources + switch (Context.ContextType) + { +#if STRIDE_UI_SDL + case AppContextType.DesktopSDL: + break; +#endif +#if STRIDE_PLATFORM_ANDROID + case AppContextType.Android: + break; +#endif +#if STRIDE_PLATFORM_IOS + case AppContextType.iOS: + break; +#endif +#if STRIDE_PLATFORM_UWP + case AppContextType.UWPXaml: + case AppContextType.UWPCoreWindow: + break; +#endif + case AppContextType.Desktop: +#if (STRIDE_UI_WINFORMS || STRIDE_UI_WPF) + inputManager.Sources.Add(new InputSourceWindowsDirectInput()); + if (InputSourceWindowsXInput.IsSupported()) + inputManager.Sources.Add(new InputSourceWindowsXInput()); +#if STRIDE_INPUT_RAWINPUT + if (rawInputEnabled && context is GameContextWinforms gameContextWinforms) + inputManager.Sources.Add(new InputSourceWindowsRawInput(gameContextWinforms.Control)); +#endif +#endif + break; + default: + Log.Warning("GameContext type is not supported by the InputManager. Register your own for input to be handled properly."); + break; + } + } + internal static DatabaseFileProvider InitializeAssetDatabase() { using (Profiler.Begin(GameProfilingKeys.ObjectDatabaseInitialize)) diff --git a/sources/engine/Stride.Games/GameBase.cs b/sources/engine/Stride.Games/GameBase.cs index 9531bf2332..9cb2262684 100644 --- a/sources/engine/Stride.Games/GameBase.cs +++ b/sources/engine/Stride.Games/GameBase.cs @@ -98,7 +98,7 @@ protected GameBase() IsActive = true; } - protected static GameContext DetectDefaultContext() + protected static GameContext GetDefaultContext() { #if STRIDE_PLATFORM_UWP return GameContextFactory.NewGameContextUWPXaml(); @@ -302,9 +302,9 @@ public GameWindow Window { get { - if (gamePlatform != null) + if (Context != null) { - return gamePlatform.MainWindow; + return Context.GameWindow; } return null; } @@ -419,35 +419,7 @@ public virtual void Run(GameContext gameContext = null) throw new InvalidOperationException("No GraphicsDeviceManager found"); } - if(Context is not null && gameContext is not null) - { - gameContext.GamePlatform = Context.GamePlatform; - Context = gameContext; - Context.CurrentGame = this; - - // Overwrite Platform - if (gamePlatform != null) - { - Context.GamePlatform.Activated -= GamePlatform_Activated; - Context.GamePlatform.Deactivated -= GamePlatform_Deactivated; - Context.GamePlatform.Exiting -= GamePlatform_Exiting; - Context.GamePlatform.WindowCreated -= GamePlatformOnWindowCreated; - - Services.RemoveService(); - Services.RemoveService(); - } - - Context.GamePlatform = GamePlatform.Create(gameContext); - Context.GamePlatform.Activated += GamePlatform_Activated; - Context.GamePlatform.Deactivated += GamePlatform_Deactivated; - Context.GamePlatform.Exiting += GamePlatform_Exiting; - Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; - - Services.AddService(Context.GamePlatform); - Services.AddService(Context.GamePlatform); - } - - // Gets the GameWindow Context + SetGameContext(gameContext); EnsureGameContextIsSet(); if(Context is null) { @@ -494,7 +466,7 @@ public virtual void Run(GameContext gameContext = null) /// private void EnsureGameContextIsSet() { - // Gets the GameWindow Context + // Gets the Game Context if (Context == null) { AppContextType c; @@ -555,24 +527,44 @@ public void Tick() } } - public virtual void SetWindow(GameWindow window) + public virtual void SetGameContext(GameContext context) { - if (IsRunning) + if(IsRunning) { - throw new InvalidOperationException("Cannot set the game window while the game is running"); + throw new InvalidOperationException("Cannot set the game context while the game is running"); } - Context.GameWindow = window; - //Window = window; - } + if (Context is not null && context is not null) + { + context.GamePlatform = Context.GamePlatform; + Context = context; + Context.CurrentGame = this; - public virtual void SetGameContext(GameContext context) - { - if(IsRunning) + // Overwrite Platform + if (gamePlatform != null) + { + Context.GamePlatform.Activated -= GamePlatform_Activated; + Context.GamePlatform.Deactivated -= GamePlatform_Deactivated; + Context.GamePlatform.Exiting -= GamePlatform_Exiting; + Context.GamePlatform.WindowCreated -= GamePlatformOnWindowCreated; + + Services.RemoveService(); + Services.RemoveService(); + } + + Context.GamePlatform = GamePlatform.Create(context); + Context.GamePlatform.Activated += GamePlatform_Activated; + Context.GamePlatform.Deactivated += GamePlatform_Deactivated; + Context.GamePlatform.Exiting += GamePlatform_Exiting; + Context.GamePlatform.WindowCreated += GamePlatformOnWindowCreated; + + Services.AddService(Context.GamePlatform); + Services.AddService(Context.GamePlatform); + } + else if (context is not null) { - throw new InvalidOperationException("Cannot set the game context while the game is running"); + Context = context; } - Context = context; } /// diff --git a/sources/engine/Stride.Games/GamePlatform.cs b/sources/engine/Stride.Games/GamePlatform.cs index c5eab1e0e9..d6fa19874d 100644 --- a/sources/engine/Stride.Games/GamePlatform.cs +++ b/sources/engine/Stride.Games/GamePlatform.cs @@ -106,7 +106,6 @@ public virtual GameWindow CreateWindow(GameContext gameContext) window.PreferredFullscreenSize = requestedSize; window.Initialize(gameContext); - context.GameWindow = window; return window; } @@ -123,7 +122,8 @@ public void Run() { IsBlockingRun = !context.IsUserManagingRun; - context.GameWindow = CreateWindow(context); + // Create the game window if not already created manually + context.GameWindow ??= CreateWindow(context); // Register on Activated gameWindow.Activated += OnActivated; diff --git a/sources/engine/Stride.Games/GraphicsDeviceManager.cs b/sources/engine/Stride.Games/GraphicsDeviceManager.cs index 00be755193..08cd2404b4 100644 --- a/sources/engine/Stride.Games/GraphicsDeviceManager.cs +++ b/sources/engine/Stride.Games/GraphicsDeviceManager.cs @@ -49,7 +49,7 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra private readonly object lockDeviceCreation; - private GameBase game; + private readonly GameBase game; private bool deviceSettingsChanged; @@ -100,12 +100,12 @@ public class GraphicsDeviceManager : ComponentBase, IGraphicsDeviceManager, IGra /// /// The game. /// The game instance cannot be null. - public GraphicsDeviceManager(GameBase game, GameContext context = null) + public GraphicsDeviceManager(GameBase game) { this.game = game; if (this.game == null) { - throw new ArgumentNullException("game"); + throw new ArgumentNullException(nameof(game)); } lockDeviceCreation = new object(); @@ -119,8 +119,8 @@ public GraphicsDeviceManager(GameBase game, GameContext context = null) preferredBackBufferHeight = DefaultBackBufferHeight; preferredRefreshRate = new Rational(60, 1); PreferredMultisampleCount = MultisampleCount.None; - PreferredGraphicsProfile = new[] - { + PreferredGraphicsProfile = + [ GraphicsProfile.Level_11_1, GraphicsProfile.Level_11_0, GraphicsProfile.Level_10_1, @@ -128,7 +128,7 @@ public GraphicsDeviceManager(GameBase game, GameContext context = null) GraphicsProfile.Level_9_3, GraphicsProfile.Level_9_2, GraphicsProfile.Level_9_1, - }; + ]; graphicsDeviceFactory = game.Services.GetService(); if (graphicsDeviceFactory == null) diff --git a/sources/engine/Stride.Games/IGame.cs b/sources/engine/Stride.Games/IGame.cs index 8e55414ba2..5788899fcd 100644 --- a/sources/engine/Stride.Games/IGame.cs +++ b/sources/engine/Stride.Games/IGame.cs @@ -140,8 +140,10 @@ public interface IGame /// The window. GameWindow Window { get; } - public void SetWindow(GameWindow window); - + /// + /// Sets the game context. + /// + /// public void SetGameContext(GameContext context); } } diff --git a/sources/engine/Stride.Input/InputManager.cs b/sources/engine/Stride.Input/InputManager.cs index 7c91017ac3..7793f87679 100644 --- a/sources/engine/Stride.Input/InputManager.cs +++ b/sources/engine/Stride.Input/InputManager.cs @@ -318,7 +318,7 @@ public void Initialize(GameContext gameContext) { this.gameContext = gameContext ?? throw new ArgumentNullException(nameof(gameContext)); - AddSources(); + //AddSources(); // After adding initial devices, reassign gamepad id's // this creates a beter index assignment in the case where you have both an xbox controller and another controller at startup @@ -626,14 +626,15 @@ public void PoolInputEvent(InputEvent inputEvent) { eventRouters[inputEvent.GetType()].PoolEvent(inputEvent); } - + /// /// Resets the collection back to it's default values /// + [Obsolete] public void ResetSources() { Sources.Clear(); - AddSources(); + //AddSources(); } /// diff --git a/sources/engine/Stride.Input/SDL/InputSourceSDL.cs b/sources/engine/Stride.Input/SDL/InputSourceSDL.cs index 7b33c0afb0..328cb58cad 100644 --- a/sources/engine/Stride.Input/SDL/InputSourceSDL.cs +++ b/sources/engine/Stride.Input/SDL/InputSourceSDL.cs @@ -14,7 +14,7 @@ namespace Stride.Input /// /// Provides support for mouse/touch/keyboard/gamepads using SDL /// - internal unsafe class InputSourceSDL : InputSourceBase + public unsafe class InputSourceSDL : InputSourceBase { private static Sdl SDL = Window.SDL; diff --git a/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs b/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs index b46764d109..cbfca61bfa 100644 --- a/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs +++ b/sources/engine/Stride.Input/Windows/InputSourceWinforms.cs @@ -18,7 +18,7 @@ namespace Stride.Input /// /// Provides support for mouse and keyboard input on windows forms /// - internal class InputSourceWinforms : InputSourceBase + public class InputSourceWinforms : InputSourceBase { private readonly HashSet heldKeys = new HashSet(); private readonly List keysToRelease = new List(); From 848bd105a58e694c464f74f5cd7b9ff21037675f Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sun, 11 May 2025 20:17:18 -0600 Subject: [PATCH 24/37] Separated input --- sources/Directory.Packages.props | 4 +- sources/core/Stride.Core/IServiceRegistry.cs | 9 +++ sources/core/Stride.Core/ServiceRegistry.cs | 16 ++++ .../Engine/Builder/GameBuilder.cs | 76 +++++++++++++++++-- .../Engine/Builder/GameBuilderExtensions.cs | 49 ++++++++---- .../Engine/Builder/IGameBuilder.cs | 15 +++- .../Engine/Builder/MinimalGame.cs | 18 ++++- .../Engine/Hosting/IStrideBuilder.cs | 16 ++++ .../Engine/Hosting/StrideBuilder.cs | 52 +++++++++++++ .../engine/Stride.Engine/Stride.Engine.csproj | 2 + 10 files changed, 233 insertions(+), 24 deletions(-) create mode 100644 sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs create mode 100644 sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 633d58c70b..12076223f2 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -12,6 +12,7 @@ + @@ -45,6 +46,7 @@ + @@ -146,4 +148,4 @@ - \ No newline at end of file + diff --git a/sources/core/Stride.Core/IServiceRegistry.cs b/sources/core/Stride.Core/IServiceRegistry.cs index c2b564561a..9f25c94df1 100644 --- a/sources/core/Stride.Core/IServiceRegistry.cs +++ b/sources/core/Stride.Core/IServiceRegistry.cs @@ -47,6 +47,15 @@ public interface IServiceRegistry /// Thrown when a service of the same type is already registered. void AddService(T service) where T : class; + /// + /// Adds a service to this . + /// + /// The service to add. + /// The type to register as. + /// Thrown when the provided service is null. + /// Thrown when a service of the same type is already registered. + void AddService(object service, Type type); + /// /// Gets the service object of the specified type. /// diff --git a/sources/core/Stride.Core/ServiceRegistry.cs b/sources/core/Stride.Core/ServiceRegistry.cs index 09b7543516..ca5dcede37 100644 --- a/sources/core/Stride.Core/ServiceRegistry.cs +++ b/sources/core/Stride.Core/ServiceRegistry.cs @@ -76,6 +76,22 @@ public void AddService(T service) OnServiceAdded(new ServiceEventArgs(type, service)); } + /// + /// + /// This implementation triggers the event after a service is successfully added. + /// + public void AddService(object service, Type type) + { + ArgumentNullException.ThrowIfNull(service); + + lock (registeredService) + { + if (!registeredService.TryAdd(type, service)) + throw new ArgumentException("Service is already registered with this type", nameof(type)); + } + OnServiceAdded(new ServiceEventArgs(type, service)); + } + /// /// /// This implementation triggers the event after a service is successfully removed. diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 28001c7729..82affae29c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -1,7 +1,12 @@ +using System; using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Stride.Core; using Stride.Core.Diagnostics; +using Stride.Core.IO; using Stride.Games; +using Stride.Input; namespace Stride.Engine.Builder; @@ -11,19 +16,28 @@ namespace Stride.Engine.Builder; /// public class GameBuilder : IGameBuilder { - public IServiceRegistry Services { get; protected set; } + public Dictionary Services { get; internal set; } = []; - public GameSystemCollection GameSystems { get; protected set; } + public IServiceCollection DiServices { get; internal set; } = new ServiceCollection(); - public List LogListeners { get; protected set; } = []; + public GameSystemCollection GameSystems { get; internal set; } - public GameBase Game { get; protected set; } + public List LogListeners { get; internal set; } = []; + + public List InputSources { get; internal set; } = []; + + public DatabaseFileProvider DatabaseFileProvider { get; set; } + + public GameBase Game { get; set; } + + public GameContext Context { get; set; } internal GameBuilder() { Game = new MinimalGame(null); - Services = Game.Services; GameSystems = Game.GameSystems; + DiServices.AddSingleton(Game.Services); + Services.Add(typeof(IServiceRegistry), Game.Services); } public static GameBuilder Create() @@ -33,11 +47,63 @@ public static GameBuilder Create() public virtual GameBase Build() { + var provider = DiServices.BuildServiceProvider(); + foreach (var service in Services) + { + if (service.Key == typeof(IServiceRegistry) || service.Key == typeof(IServiceProvider)) + continue; + + try + { + if (service.Value == null) + { + var instance = provider.GetService(service.Key); + Game.Services.AddService(instance, service.Key); + Services[service.Key] = instance; + } + else + { + Game.Services.AddService(service.Value, service.Key); + } + } + catch (Exception ex) + { + // TODO: check if service is already registered first. + } + } + + // Add all game systems to the game. + foreach (var service in Services) + { + var system = provider.GetService(service.Key); + if (system is IGameSystemBase gameSystem && !Game.GameSystems.Contains(gameSystem)) + { + Game.GameSystems.Add(gameSystem); + } + } + foreach (var logListener in LogListeners) { GlobalLogger.GlobalMessageLogged += logListener; } + if (Context != null) + { + Game.SetGameContext(Context); + } + + if(InputSources.Count > 0) + { + var inputManager = Game.Services.GetService() ?? throw new InvalidOperationException("InputManager is not registered in the service registry."); + foreach (var inputSource in InputSources) + { + inputManager.Sources.Add(inputSource); + } + } + + var dataBase = Game.Services.GetService(); + dataBase.FileProvider = DatabaseFileProvider; + return Game; } } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 0b45d86da8..83b0d1af70 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,12 +1,20 @@ using System; +using Microsoft.Extensions.DependencyInjection; +using Stride.Audio; +using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; using Stride.Core.Serialization.Contents; using Stride.Core.Storage; +using Stride.Engine.Processors; using Stride.Games; using Stride.Input; +using Stride.Profiling; using Stride.Rendering; +using Stride.Rendering.Fonts; +using Stride.Rendering.Sprites; using Stride.Shaders.Compiler; +using Stride.Streaming; namespace Stride.Engine.Builder; public static class GameBuilderExtensions @@ -19,7 +27,15 @@ public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gam public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class { - gameBuilder.Services.AddService(service); + gameBuilder.Services.Add(typeof(T), service); + gameBuilder.DiServices.AddSingleton(service); + return gameBuilder; + } + + public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T : class + { + gameBuilder.Services.Add(typeof(T), null); + gameBuilder.DiServices.AddSingleton(); return gameBuilder; } @@ -29,6 +45,20 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList return gameBuilder; } + public static IGameBuilder AddStrideInput(this IGameBuilder gameBuilder) + { + var services = gameBuilder.Services[typeof(IServiceRegistry)] as IServiceRegistry; + + var inputSystem = new InputSystem(services); + + gameBuilder + .AddGameSystem(inputSystem) + .AddService(inputSystem) + .AddService(inputSystem.Manager); + + return gameBuilder; + } + public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameContext context) { gameBuilder.Game.SetGameContext(context); @@ -44,8 +74,7 @@ public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameCon public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. - var dataBase = gameBuilder.Services.GetService(); - dataBase.FileProvider = provider; + gameBuilder.DatabaseFileProvider = provider; return gameBuilder; } @@ -73,7 +102,7 @@ public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) public static IGameBuilder UseDefaultContentManager(this IGameBuilder gameBuilder) { - var services = gameBuilder.Services; + var services = gameBuilder.Game.Services; var content = new ContentManager(services); services.AddService(content); services.AddService(content); @@ -106,22 +135,12 @@ public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectComp public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameContext context) { gameBuilder.Game.SetGameContext(context); - return gameBuilder; } public static IGameBuilder AddInput(this IGameBuilder gameBuilder, IInputSource inputSource) { - var inputManager = gameBuilder.Services.GetService(); - - if (inputManager == null) - { - throw new InvalidOperationException("InputManager is not registered in the service registry."); - } - - inputManager.Sources.Add(inputSource); - + gameBuilder.InputSources.Add(inputSource); return gameBuilder; } - } diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index f658479a7b..9bb6a0060a 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -1,16 +1,27 @@ using System.Collections.Generic; using Stride.Core.Diagnostics; -using Stride.Core; using Stride.Games; +using System; +using Microsoft.Extensions.DependencyInjection; +using Stride.Input; +using Stride.Core.IO; namespace Stride.Engine.Builder; public interface IGameBuilder { - IServiceRegistry Services { get; } + Dictionary Services { get; } + + IServiceCollection DiServices { get; } GameSystemCollection GameSystems { get; } List LogListeners { get; } + List InputSources { get; } + + DatabaseFileProvider DatabaseFileProvider { get; set; } + GameBase Game { get; } + + GameContext Context { get; } } diff --git a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs index 62e77076ea..ec51270191 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/MinimalGame.cs @@ -1,4 +1,7 @@ using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; using Stride.Core.Diagnostics; using Stride.Core.Serialization; using Stride.Core.Serialization.Contents; @@ -11,7 +14,7 @@ namespace Stride.Engine.Builder; /// /// A game class with no registered systems by default. /// -public class MinimalGame : GameBase +public class MinimalGame : GameBase, IHostedService { /// @@ -85,4 +88,17 @@ protected override void PrepareContext() Content = contentManager; } + + public Task StartAsync(CancellationToken cancellationToken = default) + { + Run(); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken = default) + { + Exit(); + return Task.CompletedTask; + } + } diff --git a/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs new file mode 100644 index 0000000000..9de0f50191 --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Hosting; +using Stride.Games; + +namespace Stride.Engine.Hosting; + +public interface IStrideBuilder : IHostBuilder +{ + /// + /// Gets the game context. + /// + GameContext Context { get; } + /// + /// Gets the game. + /// + GameBase Game { get; } +} diff --git a/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs new file mode 100644 index 0000000000..f6bffe3367 --- /dev/null +++ b/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Stride.Games; + +namespace Stride.Engine.Hosting; + +public class StrideBuilder : IStrideBuilder +{ + public GameContext Context { get; set; } + + public GameBase Game { get; set; } + + public IDictionary Properties { get; set; } + + public IHost Build() + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureContainer(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder ConfigureServices(Action configureDelegate) + { + throw new NotImplementedException(); + } + + public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull + { + throw new NotImplementedException(); + } + + public IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull + { + throw new NotImplementedException(); + } +} diff --git a/sources/engine/Stride.Engine/Stride.Engine.csproj b/sources/engine/Stride.Engine/Stride.Engine.csproj index 7bded5b689..234f39796f 100644 --- a/sources/engine/Stride.Engine/Stride.Engine.csproj +++ b/sources/engine/Stride.Engine/Stride.Engine.csproj @@ -26,8 +26,10 @@ + + From 731610dcb1d5e1ca42167779ce860c928a44e64e Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Mon, 12 May 2025 15:47:29 -0600 Subject: [PATCH 25/37] clean up and docs --- .../Engine/Builder/GameBuilder.cs | 16 +++- .../Engine/Builder/GameBuilderExtensions.cs | 87 ++++++++++++++----- .../Engine/Builder/IGameBuilder.cs | 4 +- .../Engine/Hosting/IStrideBuilder.cs | 16 ---- .../Engine/Hosting/StrideBuilder.cs | 52 ----------- 5 files changed, 82 insertions(+), 93 deletions(-) delete mode 100644 sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs delete mode 100644 sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 82affae29c..af4d652806 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.Extensions.DependencyInjection; using Stride.Core; using Stride.Core.Diagnostics; @@ -58,6 +57,21 @@ public virtual GameBase Build() if (service.Value == null) { var instance = provider.GetService(service.Key); + + if(instance == null) + { + //check if the type is inherited from another instance in the services. + foreach (var kvp in Services) + { + if (kvp.Key.IsAssignableFrom(service.Key) && kvp.Value != null) + { + instance = provider.GetService(kvp.Key); + if(instance is not null) + break; + } + } + } + Game.Services.AddService(instance, service.Key); Services[service.Key] = instance; } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 83b0d1af70..2a1fe0d064 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -1,30 +1,39 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Stride.Audio; using Stride.Core; using Stride.Core.Diagnostics; using Stride.Core.IO; using Stride.Core.Serialization.Contents; using Stride.Core.Storage; -using Stride.Engine.Processors; using Stride.Games; using Stride.Input; -using Stride.Profiling; using Stride.Rendering; -using Stride.Rendering.Fonts; -using Stride.Rendering.Sprites; using Stride.Shaders.Compiler; -using Stride.Streaming; namespace Stride.Engine.Builder; public static class GameBuilderExtensions { + + /// + /// Adds cire systems to the game. Does not register the systems into the + /// + /// + /// + /// + /// public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gameSystem) where T : IGameSystemBase { gameBuilder.GameSystems.Add(gameSystem); return gameBuilder; } + /// + /// Registers a service into the . + /// + /// + /// + /// + /// public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class { gameBuilder.Services.Add(typeof(T), service); @@ -32,6 +41,12 @@ public static IGameBuilder AddService(this IGameBuilder gameBuilder, T servic return gameBuilder; } + /// + /// Registers a service into the . + /// + /// + /// + /// public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T : class { gameBuilder.Services.Add(typeof(T), null); @@ -39,13 +54,41 @@ public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T return gameBuilder; } + /// + /// Registers a service and its interface into the . + /// + /// + /// + /// + /// + public static IGameBuilder AddService(this IGameBuilder gameBuilder) where TClass : class, TInterface where TInterface : class + { + // This is a work around to allow DI to work the same way as the ServiceRegistry expects. + // Without registering both the interface and the class, the DI will not be able to resolve the interface on build. + gameBuilder.Services.Add(typeof(TInterface), null); + gameBuilder.Services.Add(typeof(TClass), null); + gameBuilder.DiServices.AddSingleton(); + return gameBuilder; + } + + /// + /// Adds a log listener to the game. This is used thoughout Stride systems for logging events. + /// + /// + /// + /// public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogListener logListener) { gameBuilder.LogListeners.Add(logListener); return gameBuilder; } - public static IGameBuilder AddStrideInput(this IGameBuilder gameBuilder) + /// + /// Adds the Stride input system to the game with no sources. + /// + /// + /// + public static IGameBuilder UseStrideInput(this IGameBuilder gameBuilder) { var services = gameBuilder.Services[typeof(IServiceRegistry)] as IServiceRegistry; @@ -61,17 +104,17 @@ public static IGameBuilder AddStrideInput(this IGameBuilder gameBuilder) public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameContext context) { - gameBuilder.Game.SetGameContext(context); + gameBuilder.Context = context; return gameBuilder; } /// - /// Allows the user to add a custom database file provider to the game. + /// Add a custom database file provider to the game. /// /// /// /// - public static IGameBuilder AddDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) + public static IGameBuilder SetDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. gameBuilder.DatabaseFileProvider = provider; @@ -94,7 +137,7 @@ public static IGameBuilder UseDefaultDb(this IGameBuilder gameBuilder) var mountPath = VirtualFileSystem.ResolveProviderUnsafe("/asset", true).Provider == null ? "/asset" : null; var result = new DatabaseFileProvider(objDatabase, mountPath); - gameBuilder.AddDbFileProvider(result); + gameBuilder.SetDbFileProvider(result); } return gameBuilder; @@ -112,11 +155,11 @@ public static IGameBuilder UseDefaultContentManager(this IGameBuilder gameBuilde /// /// Adds a default effect compiler to the game. This is used to compile shaders and effects. /// - /// + /// /// /// /// - public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectCompiler, IVirtualFileProvider fileProvider) + public static EffectSystem CreateDefaultEffectCompiler(this EffectSystem effectSystem, IVirtualFileProvider fileProvider) { EffectCompilerBase compiler = new EffectCompiler(fileProvider) { @@ -125,20 +168,20 @@ public static EffectSystem AddDefaultEffectCompiler(this EffectSystem effectComp if(fileProvider is DatabaseFileProvider databaseFileProvider) { - effectCompiler.Compiler = new EffectCompilerCache(compiler, databaseFileProvider); - return effectCompiler; + effectSystem.Compiler = new EffectCompilerCache(compiler, databaseFileProvider); + return effectSystem; } throw new ArgumentException("The file provider must be a DatabaseFileProvider", nameof(fileProvider)); } - public static IGameBuilder UseGameContext(this IGameBuilder gameBuilder, GameContext context) - { - gameBuilder.Game.SetGameContext(context); - return gameBuilder; - } - - public static IGameBuilder AddInput(this IGameBuilder gameBuilder, IInputSource inputSource) + /// + /// Adds an input source to the game. This requires the Stride input system to be used. + /// + /// + /// + /// + public static IGameBuilder AddStrideInputSource(this IGameBuilder gameBuilder, IInputSource inputSource) { gameBuilder.InputSources.Add(inputSource); return gameBuilder; diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index 9bb6a0060a..7b1d916455 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -21,7 +21,7 @@ public interface IGameBuilder DatabaseFileProvider DatabaseFileProvider { get; set; } - GameBase Game { get; } + GameBase Game { get; set; } - GameContext Context { get; } + GameContext Context { get; set; } } diff --git a/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs deleted file mode 100644 index 9de0f50191..0000000000 --- a/sources/engine/Stride.Engine/Engine/Hosting/IStrideBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.Extensions.Hosting; -using Stride.Games; - -namespace Stride.Engine.Hosting; - -public interface IStrideBuilder : IHostBuilder -{ - /// - /// Gets the game context. - /// - GameContext Context { get; } - /// - /// Gets the game. - /// - GameBase Game { get; } -} diff --git a/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs b/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs deleted file mode 100644 index f6bffe3367..0000000000 --- a/sources/engine/Stride.Engine/Engine/Hosting/StrideBuilder.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Stride.Games; - -namespace Stride.Engine.Hosting; - -public class StrideBuilder : IStrideBuilder -{ - public GameContext Context { get; set; } - - public GameBase Game { get; set; } - - public IDictionary Properties { get; set; } - - public IHost Build() - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureContainer(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder ConfigureServices(Action configureDelegate) - { - throw new NotImplementedException(); - } - - public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull - { - throw new NotImplementedException(); - } - - public IHostBuilder UseServiceProviderFactory(Func> factory) where TContainerBuilder : notnull - { - throw new NotImplementedException(); - } -} From 8c7eaeafb86bb4efb3785f633185ee2fd9209498 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 14:53:08 -0600 Subject: [PATCH 26/37] Kryptos feedback --- sources/Directory.Packages.props | 4 +--- sources/editor/Stride.Editor/Preview/PreviewGame.cs | 10 +++++----- sources/engine/Stride.Input/InputManager.cs | 6 ++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 12076223f2..f289648c35 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -11,9 +11,7 @@ - - - + diff --git a/sources/editor/Stride.Editor/Preview/PreviewGame.cs b/sources/editor/Stride.Editor/Preview/PreviewGame.cs index 143ab1c307..b7a5a8c9f1 100644 --- a/sources/editor/Stride.Editor/Preview/PreviewGame.cs +++ b/sources/editor/Stride.Editor/Preview/PreviewGame.cs @@ -2,19 +2,19 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using System.Threading.Tasks; -using Stride.Core.BuildEngine; -using Stride.Core; -using Stride.Core.Diagnostics; -using Stride.Core.Mathematics; using Stride.Assets; using Stride.Assets.SpriteFont; using Stride.Assets.SpriteFont.Compiler; +using Stride.Core; +using Stride.Core.BuildEngine; +using Stride.Core.Diagnostics; +using Stride.Core.Mathematics; using Stride.Engine; using Stride.Engine.Design; +using Stride.Games; using Stride.Graphics; using Stride.Rendering.Compositing; using Stride.Shaders.Compiler; -using Stride.Games; namespace Stride.Editor.Preview { diff --git a/sources/engine/Stride.Input/InputManager.cs b/sources/engine/Stride.Input/InputManager.cs index 7793f87679..eb9e1d788c 100644 --- a/sources/engine/Stride.Input/InputManager.cs +++ b/sources/engine/Stride.Input/InputManager.cs @@ -318,8 +318,6 @@ public void Initialize(GameContext gameContext) { this.gameContext = gameContext ?? throw new ArgumentNullException(nameof(gameContext)); - //AddSources(); - // After adding initial devices, reassign gamepad id's // this creates a beter index assignment in the case where you have both an xbox controller and another controller at startup var sortedGamePads = GamePads.OrderBy(x => x.CanChangeIndex); @@ -630,11 +628,11 @@ public void PoolInputEvent(InputEvent inputEvent) /// /// Resets the collection back to it's default values /// - [Obsolete] + [Obsolete("This should be managed manually instead by using the Sources collection")] public void ResetSources() { Sources.Clear(); - //AddSources(); + AddSources(); } /// From 415cd6f5b004d14bc9fd87eee12598882f7c0206 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 15:10:04 -0600 Subject: [PATCH 27/37] missed some duplicate packages --- sources/Directory.Packages.props | 3 +-- sources/engine/Stride.Engine/Stride.Engine.csproj | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index f289648c35..6368b59dc8 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -11,7 +11,7 @@ - + @@ -44,7 +44,6 @@ - diff --git a/sources/engine/Stride.Engine/Stride.Engine.csproj b/sources/engine/Stride.Engine/Stride.Engine.csproj index 234f39796f..7836b61cf4 100644 --- a/sources/engine/Stride.Engine/Stride.Engine.csproj +++ b/sources/engine/Stride.Engine/Stride.Engine.csproj @@ -29,7 +29,6 @@ - From 08dc3ce62bcc625f207d0fdc423d30094d562676 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 18:16:47 -0600 Subject: [PATCH 28/37] add option for DI in GameFontSystem --- .../engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs b/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs index 0db5f3866f..570f6895b3 100644 --- a/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/Fonts/GameFontSystem.cs @@ -15,11 +15,11 @@ public class GameFontSystem : GameSystemBase { public FontSystem FontSystem { get; private set; } - public GameFontSystem(IServiceRegistry registry) + public GameFontSystem(IServiceRegistry registry, FontSystem fontSystem = null) : base(registry) { Visible = true; - FontSystem = new FontSystem(); + FontSystem = fontSystem ?? new FontSystem(); } public override void Draw(GameTime gameTime) From a51d8cfb6a3bad5629b082c545c0489e62942db5 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Wed, 28 May 2025 19:37:36 -0600 Subject: [PATCH 29/37] Fix error caused by StreamingManager DI. --- .../Stride.Engine/Engine/Builder/GameBuilder.cs | 14 ++++++++------ sources/engine/Stride.Engine/Engine/Game.cs | 3 +++ .../Stride.Rendering/Streaming/StreamingManager.cs | 5 ++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index af4d652806..c3b6cbc3ec 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -46,6 +46,12 @@ public static GameBuilder Create() public virtual GameBase Build() { + + foreach (var logListener in LogListeners) + { + GlobalLogger.GlobalMessageLogged += logListener; + } + var provider = DiServices.BuildServiceProvider(); foreach (var service in Services) { @@ -82,7 +88,8 @@ public virtual GameBase Build() } catch (Exception ex) { - // TODO: check if service is already registered first. + // TODO: check if service is already registered first.' + GlobalLogger.GetLogger("GameBuilder").Error($"Failed to register service {service.Key.Name}.\n\n", ex); } } @@ -96,11 +103,6 @@ public virtual GameBase Build() } } - foreach (var logListener in LogListeners) - { - GlobalLogger.GlobalMessageLogged += logListener; - } - if (Context != null) { Game.SetGameContext(Context); diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index 0094175372..dbaaab9489 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -11,10 +11,12 @@ using Stride.Core.IO; using Stride.Core.Mathematics; using Stride.Core.Storage; +using Stride.Core.Streaming; using Stride.Engine.Design; using Stride.Engine.Processors; using Stride.Games; using Stride.Graphics; +using Stride.Graphics.Data; using Stride.Graphics.Font; using Stride.Input; using Stride.Profiling; @@ -220,6 +222,7 @@ public Game(GameContext context = null) : base() Services.AddService(SceneSystem); Streaming = new StreamingManager(Services); + Services.AddService(Streaming); Audio = new AudioSystem(Services); Services.AddService(Audio); diff --git a/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs b/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs index 394c791ec6..21ca774303 100644 --- a/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs +++ b/sources/engine/Stride.Rendering/Streaming/StreamingManager.cs @@ -92,9 +92,8 @@ public class StreamingManager : GameSystemBase, IStreamingManager, ITexturesStre public StreamingManager([NotNull] IServiceRegistry services) : base(services) { - services.AddService(this); - services.AddService(this); - services.AddService(this); + Services.AddService(this); + Services.AddService(this); ContentStreaming = new ContentStreamingService(); From 1bcc2e5a53f1e47c98772ff5888e596ddb8a1268 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:01:50 -0600 Subject: [PATCH 30/37] renaming to favour ServiceCollection --- .../Engine/Builder/GameBuilder.cs | 19 +++++++++---------- .../Engine/Builder/GameBuilderExtensions.cs | 16 ++++++++-------- .../Engine/Builder/IGameBuilder.cs | 4 ++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index c3b6cbc3ec..092440c1bc 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -15,9 +15,9 @@ namespace Stride.Engine.Builder; /// public class GameBuilder : IGameBuilder { - public Dictionary Services { get; internal set; } = []; + public Dictionary InternalServices { get; internal set; } = []; - public IServiceCollection DiServices { get; internal set; } = new ServiceCollection(); + public IServiceCollection Services { get; internal set; } = new ServiceCollection(); public GameSystemCollection GameSystems { get; internal set; } @@ -35,8 +35,8 @@ internal GameBuilder() { Game = new MinimalGame(null); GameSystems = Game.GameSystems; - DiServices.AddSingleton(Game.Services); - Services.Add(typeof(IServiceRegistry), Game.Services); + Services.AddSingleton(Game.Services); + InternalServices.Add(typeof(IServiceRegistry), Game.Services); } public static GameBuilder Create() @@ -46,14 +46,13 @@ public static GameBuilder Create() public virtual GameBase Build() { - foreach (var logListener in LogListeners) { GlobalLogger.GlobalMessageLogged += logListener; } - var provider = DiServices.BuildServiceProvider(); - foreach (var service in Services) + var provider = Services.BuildServiceProvider(); + foreach (var service in InternalServices) { if (service.Key == typeof(IServiceRegistry) || service.Key == typeof(IServiceProvider)) continue; @@ -67,7 +66,7 @@ public virtual GameBase Build() if(instance == null) { //check if the type is inherited from another instance in the services. - foreach (var kvp in Services) + foreach (var kvp in InternalServices) { if (kvp.Key.IsAssignableFrom(service.Key) && kvp.Value != null) { @@ -79,7 +78,7 @@ public virtual GameBase Build() } Game.Services.AddService(instance, service.Key); - Services[service.Key] = instance; + InternalServices[service.Key] = instance; } else { @@ -94,7 +93,7 @@ public virtual GameBase Build() } // Add all game systems to the game. - foreach (var service in Services) + foreach (var service in InternalServices) { var system = provider.GetService(service.Key); if (system is IGameSystemBase gameSystem && !Game.GameSystems.Contains(gameSystem)) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 2a1fe0d064..702d8f1912 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -36,8 +36,8 @@ public static IGameBuilder AddGameSystem(this IGameBuilder gameBuilder, T gam /// public static IGameBuilder AddService(this IGameBuilder gameBuilder, T service) where T : class { - gameBuilder.Services.Add(typeof(T), service); - gameBuilder.DiServices.AddSingleton(service); + gameBuilder.InternalServices.Add(typeof(T), service); + gameBuilder.Services.AddSingleton(service); return gameBuilder; } @@ -49,8 +49,8 @@ public static IGameBuilder AddService(this IGameBuilder gameBuilder, T servic /// public static IGameBuilder AddService(this IGameBuilder gameBuilder) where T : class { - gameBuilder.Services.Add(typeof(T), null); - gameBuilder.DiServices.AddSingleton(); + gameBuilder.InternalServices.Add(typeof(T), null); + gameBuilder.Services.AddSingleton(); return gameBuilder; } @@ -65,9 +65,9 @@ public static IGameBuilder AddService(this IGameBuilder game { // This is a work around to allow DI to work the same way as the ServiceRegistry expects. // Without registering both the interface and the class, the DI will not be able to resolve the interface on build. - gameBuilder.Services.Add(typeof(TInterface), null); - gameBuilder.Services.Add(typeof(TClass), null); - gameBuilder.DiServices.AddSingleton(); + gameBuilder.InternalServices.Add(typeof(TInterface), null); + gameBuilder.InternalServices.Add(typeof(TClass), null); + gameBuilder.Services.AddSingleton(); return gameBuilder; } @@ -90,7 +90,7 @@ public static IGameBuilder AddLogListener(this IGameBuilder gameBuilder, LogList /// public static IGameBuilder UseStrideInput(this IGameBuilder gameBuilder) { - var services = gameBuilder.Services[typeof(IServiceRegistry)] as IServiceRegistry; + var services = gameBuilder.InternalServices[typeof(IServiceRegistry)] as IServiceRegistry; var inputSystem = new InputSystem(services); diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index 7b1d916455..a56c041060 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -9,9 +9,9 @@ namespace Stride.Engine.Builder; public interface IGameBuilder { - Dictionary Services { get; } + Dictionary InternalServices { get; } - IServiceCollection DiServices { get; } + IServiceCollection Services { get; } GameSystemCollection GameSystems { get; } From c5d1c3dbb5de6227e98bc7deb74ee8ddb9bcd460 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:06:39 -0600 Subject: [PATCH 31/37] typo --- .../Stride.Engine/Engine/Builder/GameBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 702d8f1912..5e09032a5a 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -15,7 +15,7 @@ public static class GameBuilderExtensions { /// - /// Adds cire systems to the game. Does not register the systems into the + /// Adds core systems to the game. Does not register the systems into the /// /// /// From 1f8eba64b0ebc79f1743e3766845bd655db4253c Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:43:14 -0600 Subject: [PATCH 32/37] docs --- sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 092440c1bc..fee0db5890 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -15,8 +15,14 @@ namespace Stride.Engine.Builder; /// public class GameBuilder : IGameBuilder { + /// + /// This is used to allow the same instance to be registered multiple times as differenet interfaces or types. This was done due to how works."/> + /// public Dictionary InternalServices { get; internal set; } = []; + /// + /// This allows for Service to be registered through DI. + /// public IServiceCollection Services { get; internal set; } = new ServiceCollection(); public GameSystemCollection GameSystems { get; internal set; } From a315d2bcda47beabb90fc9ca1cf36d902ac42fe3 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 08:53:31 -0600 Subject: [PATCH 33/37] docs --- .../Engine/Builder/GameBuilder.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index fee0db5890..ec4a2dd27e 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -10,7 +10,7 @@ namespace Stride.Engine.Builder; /// -/// Helps build the game and preps it to be able to run after built. +/// Helps build the game and preps it to be able to run after . /// /// public class GameBuilder : IGameBuilder @@ -25,10 +25,19 @@ public class GameBuilder : IGameBuilder /// public IServiceCollection Services { get; internal set; } = new ServiceCollection(); + /// + /// This is a direct reference to the game systems collection of the game. + /// public GameSystemCollection GameSystems { get; internal set; } + /// + /// Adds log listeners to the game on . This is registered first so it will log build errors if they occur."/> + /// public List LogListeners { get; internal set; } = []; + /// + /// Adds input sources to the game on . + /// public List InputSources { get; internal set; } = []; public DatabaseFileProvider DatabaseFileProvider { get; set; } @@ -37,17 +46,21 @@ public class GameBuilder : IGameBuilder public GameContext Context { get; set; } - internal GameBuilder() + internal GameBuilder(GameBase game) { - Game = new MinimalGame(null); + Game = game ?? new MinimalGame(null); GameSystems = Game.GameSystems; Services.AddSingleton(Game.Services); InternalServices.Add(typeof(IServiceRegistry), Game.Services); } - public static GameBuilder Create() + /// + /// Creates a new instance of the class. + /// + /// + public static GameBuilder Create(GameBase game = null) { - return new GameBuilder(); + return new GameBuilder(game); } public virtual GameBase Build() From 0ed0012e4e21a843e3f75242596b9bbec752b0a9 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 12:42:05 -0600 Subject: [PATCH 34/37] clean up and build logging --- .../Engine/Builder/GameBuilder.cs | 27 ++++++++++++------- .../Engine/Builder/GameBuilderExtensions.cs | 4 ++- .../Engine/Builder/IGameBuilder.cs | 2 -- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index ec4a2dd27e..0e0fd4fa3c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection; using Stride.Core; using Stride.Core.Diagnostics; -using Stride.Core.IO; using Stride.Games; using Stride.Input; @@ -26,7 +25,7 @@ public class GameBuilder : IGameBuilder public IServiceCollection Services { get; internal set; } = new ServiceCollection(); /// - /// This is a direct reference to the game systems collection of the game. + /// This is a direct reference to the game systems collection of the . /// public GameSystemCollection GameSystems { get; internal set; } @@ -40,12 +39,12 @@ public class GameBuilder : IGameBuilder /// public List InputSources { get; internal set; } = []; - public DatabaseFileProvider DatabaseFileProvider { get; set; } - public GameBase Game { get; set; } public GameContext Context { get; set; } + private static Logger _log => GlobalLogger.GetLogger("GameBuilder"); + internal GameBuilder(GameBase game) { Game = game ?? new MinimalGame(null); @@ -96,18 +95,20 @@ public virtual GameBase Build() } } + _log.Info($"Registering service {service.Key.Name}."); Game.Services.AddService(instance, service.Key); InternalServices[service.Key] = instance; } else { + _log.Info($"Registering service {service.Key.Name}."); Game.Services.AddService(service.Value, service.Key); } } catch (Exception ex) { // TODO: check if service is already registered first.' - GlobalLogger.GetLogger("GameBuilder").Error($"Failed to register service {service.Key.Name}.\n\n", ex); + _log.Error($"Failed to register service {service.Key.Name}.\n\n", ex); } } @@ -117,27 +118,35 @@ public virtual GameBase Build() var system = provider.GetService(service.Key); if (system is IGameSystemBase gameSystem && !Game.GameSystems.Contains(gameSystem)) { + _log.Info($"Adding game system {gameSystem.GetType().Name} to the game systems collection."); Game.GameSystems.Add(gameSystem); } } if (Context != null) { + _log.Info($"Setting game context."); Game.SetGameContext(Context); } if(InputSources.Count > 0) { - var inputManager = Game.Services.GetService() ?? throw new InvalidOperationException("InputManager is not registered in the service registry."); + var inputManager = Game.Services.GetService(); + + if (inputManager is null) + { + _log.Info("No InputManager found in the game services, creating default."); + inputManager = new InputManager(); + Game.Services.AddService(inputManager); + } + foreach (var inputSource in InputSources) { + _log.Info($"Adding input source {inputSource.GetType().Name} to the input manager."); inputManager.Sources.Add(inputSource); } } - var dataBase = Game.Services.GetService(); - dataBase.FileProvider = DatabaseFileProvider; - return Game; } } diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs index 5e09032a5a..200289b29c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilderExtensions.cs @@ -117,7 +117,9 @@ public static IGameBuilder SetGameContext(this IGameBuilder gameBuilder, GameCon public static IGameBuilder SetDbFileProvider(this IGameBuilder gameBuilder, DatabaseFileProvider provider) { // Gets initialized by the GameBase constructor. - gameBuilder.DatabaseFileProvider = provider; + var fileProviderService = gameBuilder.Game.Services.GetService(); + + fileProviderService.FileProvider = provider; return gameBuilder; } diff --git a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs index a56c041060..eba6f13b5b 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/IGameBuilder.cs @@ -19,8 +19,6 @@ public interface IGameBuilder List InputSources { get; } - DatabaseFileProvider DatabaseFileProvider { get; set; } - GameBase Game { get; set; } GameContext Context { get; set; } From 4718339b396b1f2cc7974d13a6170e071649f53b Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Thu, 29 May 2025 18:49:07 -0600 Subject: [PATCH 35/37] remove some hardcoded Game references --- .../Stride.Debugger/Debugger/LiveAssemblyReloader.cs | 11 ++++++----- .../Stride.Engine/Engine/Builder/GameBuilder.cs | 4 ++-- sources/engine/Stride.Engine/Engine/Game.cs | 2 -- sources/engine/Stride.Engine/Engine/GameSystem.cs | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs b/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs index 67c314e85f..bb4d910626 100644 --- a/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs +++ b/sources/engine/Stride.Debugger/Debugger/LiveAssemblyReloader.cs @@ -1,29 +1,30 @@ -// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Stride.Core; using Stride.Core.Reflection; -using Stride.Core.Serialization; using Stride.Core.Yaml; using Stride.Core.Yaml.Events; using Stride.Core.Yaml.Serialization; using Stride.Debugger.Target; using Stride.Engine; +using Stride.Games; namespace Stride.Debugger { public static class LiveAssemblyReloader { - public static void Reload(Game game, AssemblyContainer assemblyContainer, List assembliesToUnregister, List assembliesToRegister) + public static void Reload(GameBase game, AssemblyContainer assemblyContainer, List assembliesToUnregister, List assembliesToRegister) { List entities = new List(); + var sceneSystem = game.Services.GetSafeServiceAs(); + if (game != null) - entities.AddRange(game.SceneSystem.SceneInstance); + entities.AddRange(sceneSystem.SceneInstance); CloneReferenceSerializer.References = new List(); diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 0e0fd4fa3c..0469f99e15 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -107,7 +107,7 @@ public virtual GameBase Build() } catch (Exception ex) { - // TODO: check if service is already registered first.' + // TODO: check if service is already registered first. _log.Error($"Failed to register service {service.Key.Name}.\n\n", ex); } } @@ -147,6 +147,6 @@ public virtual GameBase Build() } } - return Game; + return Game; } } diff --git a/sources/engine/Stride.Engine/Engine/Game.cs b/sources/engine/Stride.Engine/Engine/Game.cs index dbaaab9489..7064d566e6 100644 --- a/sources/engine/Stride.Engine/Engine/Game.cs +++ b/sources/engine/Stride.Engine/Engine/Game.cs @@ -11,12 +11,10 @@ using Stride.Core.IO; using Stride.Core.Mathematics; using Stride.Core.Storage; -using Stride.Core.Streaming; using Stride.Engine.Design; using Stride.Engine.Processors; using Stride.Games; using Stride.Graphics; -using Stride.Graphics.Data; using Stride.Graphics.Font; using Stride.Input; using Stride.Profiling; diff --git a/sources/engine/Stride.Engine/Engine/GameSystem.cs b/sources/engine/Stride.Engine/Engine/GameSystem.cs index 3ea3a8f9ec..545609b8c9 100644 --- a/sources/engine/Stride.Engine/Engine/GameSystem.cs +++ b/sources/engine/Stride.Engine/Engine/GameSystem.cs @@ -19,6 +19,6 @@ protected GameSystem(IServiceRegistry registry) : base(registry) /// /// The game. /// This value can be null - public new Game Game => (Game)base.Game; + public new GameBase Game => base.Game; } } From 5d34b28114122bacb8936f8b4071afd3a1fe3102 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Sat, 19 Jul 2025 20:45:59 -0600 Subject: [PATCH 36/37] GameWindowTest --- .../Engine/Builder/GameBuilder.cs | 2 +- .../Stride.Games/Windowing/IGameWindow.cs | 23 +++++++++++++++++++ .../Stride.Games/Windowing/IStrideSurface.cs | 8 +++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 sources/engine/Stride.Games/Windowing/IGameWindow.cs create mode 100644 sources/engine/Stride.Games/Windowing/IStrideSurface.cs diff --git a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs index 0469f99e15..5c9f60db0c 100644 --- a/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs +++ b/sources/engine/Stride.Engine/Engine/Builder/GameBuilder.cs @@ -43,7 +43,7 @@ public class GameBuilder : IGameBuilder public GameContext Context { get; set; } - private static Logger _log => GlobalLogger.GetLogger("GameBuilder"); + private static Logger _log => GlobalLogger.GetLogger(nameof(GameBuilder)); internal GameBuilder(GameBase game) { diff --git a/sources/engine/Stride.Games/Windowing/IGameWindow.cs b/sources/engine/Stride.Games/Windowing/IGameWindow.cs new file mode 100644 index 0000000000..643b1d6ac9 --- /dev/null +++ b/sources/engine/Stride.Games/Windowing/IGameWindow.cs @@ -0,0 +1,23 @@ +using System; +using Stride.Core.Mathematics; + +namespace Stride.Games.Windowing; +public interface IGameWindow : IStrideSurface +{ + public IntPtr WindowHandle { get; } + public Int2 Position { get; set; } + public Int2 Size { get; set; } + + public string Title { get; set; } + + public WindowState State { get; set; } +} + +public enum WindowState +{ + Normal, + Minimized, + Maximized, + FullscreenWindowed, + FullscreenExclusive, +} diff --git a/sources/engine/Stride.Games/Windowing/IStrideSurface.cs b/sources/engine/Stride.Games/Windowing/IStrideSurface.cs new file mode 100644 index 0000000000..7e7481ff54 --- /dev/null +++ b/sources/engine/Stride.Games/Windowing/IStrideSurface.cs @@ -0,0 +1,8 @@ +using System; +using Stride.Core.Mathematics; + +namespace Stride.Games.Windowing; +public interface IStrideSurface +{ + public Int2 Size { get; set; } +} From 850659de347b2d2956adf7763de04463d398e5c5 Mon Sep 17 00:00:00 2001 From: Doprez <73259914+Doprez@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:27:36 -0600 Subject: [PATCH 37/37] writable ConentManager --- .../Serialization/Contents/IContentManager.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sources/core/Stride.Core.Serialization/Serialization/Contents/IContentManager.cs b/sources/core/Stride.Core.Serialization/Serialization/Contents/IContentManager.cs index baf4983438..d495e40f7c 100644 --- a/sources/core/Stride.Core.Serialization/Serialization/Contents/IContentManager.cs +++ b/sources/core/Stride.Core.Serialization/Serialization/Contents/IContentManager.cs @@ -25,6 +25,19 @@ public interface IContentManager /// A stream to the raw asset. Stream OpenAsStream(string url, StreamFlags streamFlags = StreamFlags.None); + /// + /// Saves an asset at a specific URL. + /// + /// The URL. + /// The asset. + /// The custom storage type to use. Use null as default. + /// + /// url + /// or + /// asset + /// + void Save(string url, object asset, Type? storageType); + /// /// Loads content from the specified URL. ///