diff --git a/Runtime/Engine/Code/Console/ConsoleService.cs b/Runtime/Engine/Code/Console/ConsoleService.cs index 6360ded..f686f3c 100644 --- a/Runtime/Engine/Code/Console/ConsoleService.cs +++ b/Runtime/Engine/Code/Console/ConsoleService.cs @@ -46,16 +46,21 @@ namespace RebootKit.Engine.Services.Console { string logFilePath = Application.persistentDataPath + "/rr_logs.txt"; #endif - m_LogFileStream = new FileStream(logFilePath, FileMode.Append, FileAccess.Write); - m_LogFileWriter = new StreamWriter(m_LogFileStream); + try { + m_LogFileStream = new FileStream(logFilePath, FileMode.Append, FileAccess.Write); + m_LogFileWriter = new StreamWriter(m_LogFileStream); - m_LogFileWriter.WriteLine("============================"); - m_LogFileWriter.WriteLine("Starting new log"); - m_LogFileWriter.WriteLine($" > Game: {Application.productName}"); - m_LogFileWriter.WriteLine($" > Version: {Application.version}"); - m_LogFileWriter.WriteLine($" > Date: {DateTime.Now}"); - m_LogFileWriter.WriteLine("============================"); - m_LogFileWriter.Flush(); + m_LogFileWriter.WriteLine("============================"); + m_LogFileWriter.WriteLine("Starting new log"); + m_LogFileWriter.WriteLine($" > Game: {Application.productName}"); + m_LogFileWriter.WriteLine($" > Version: {Application.version}"); + m_LogFileWriter.WriteLine($" > Date: {DateTime.Now}"); + m_LogFileWriter.WriteLine("============================"); + m_LogFileWriter.Flush(); + + } catch (Exception e) { + s_logger.Error($"Failed to open log file at {logFilePath}: {e.Message}"); + } s_logger.Info("Waking up"); diff --git a/Runtime/Engine/Code/ConsoleUI/ConsoleUIService.cs b/Runtime/Engine/Code/ConsoleUI/ConsoleUIService.cs index 6f821a8..291cfec 100644 --- a/Runtime/Engine/Code/ConsoleUI/ConsoleUIService.cs +++ b/Runtime/Engine/Code/ConsoleUI/ConsoleUIService.cs @@ -1,14 +1,15 @@ using System.Text; using RebootKit.Engine.Foundation; using RebootKit.Engine.Main; +using RebootKit.Engine.Services.ConsoleUI; using RebootKit.Engine.Services.Input; using UnityEngine; using UnityEngine.InputSystem; using Logger = RebootKit.Engine.Foundation.Logger; -namespace RebootKit.Engine.Services.ConsoleUI { +namespace RebootKit.Engine.ConsoleUI { public class ConsoleUIService : ServiceMonoBehaviour { - static readonly Logger s_logger = new Logger(nameof(ConsoleUIService)); + static readonly Logger s_Logger = new Logger(nameof(ConsoleUIService)); [SerializeField] ConsoleVC m_ConsoleVC; [SerializeField] ScriptableInputAction m_ToggleAction; @@ -22,7 +23,7 @@ namespace RebootKit.Engine.Services.ConsoleUI { } void OnEnable() { - s_logger.Info("OnEnable console"); + s_Logger.Info("OnEnable console"); m_ToggleAction.Action.Enable(); m_ToggleAction.Action.performed += OnToggleAction; diff --git a/Runtime/Engine/Code/Crosshair.meta b/Runtime/Engine/Code/Crosshair.meta deleted file mode 100644 index 7493f37..0000000 --- a/Runtime/Engine/Code/Crosshair.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 3d31f3fe40394defabe1b77f6ddf8861 -timeCreated: 1746665551 \ No newline at end of file diff --git a/Runtime/Engine/Code/Crosshair/CrosshairService.cs b/Runtime/Engine/Code/Crosshair/CrosshairService.cs deleted file mode 100644 index 47245ce..0000000 --- a/Runtime/Engine/Code/Crosshair/CrosshairService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using RebootKit.Engine.Foundation; - -namespace RebootKit.Engine.Services.Crosshair { - public class CrosshairService : IService { - - public CrosshairService() { - - } - - public void Dispose() { - } - } -} \ No newline at end of file diff --git a/Runtime/Engine/Code/Crosshair/CrosshairService.cs.meta b/Runtime/Engine/Code/Crosshair/CrosshairService.cs.meta deleted file mode 100644 index a4f576f..0000000 --- a/Runtime/Engine/Code/Crosshair/CrosshairService.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: f23a52c23aae46e8bd6b77222b10e5e2 -timeCreated: 1746665562 \ No newline at end of file diff --git a/Runtime/Engine/Code/Development/DebugOverlayView.cs b/Runtime/Engine/Code/Development/DebugOverlayView.cs index 0c278a8..1eac12c 100644 --- a/Runtime/Engine/Code/Development/DebugOverlayView.cs +++ b/Runtime/Engine/Code/Development/DebugOverlayView.cs @@ -1,5 +1,4 @@ -using System; -using RebootKit.Engine.Main; +using RebootKit.Engine.Main; using RebootKit.Engine.UI; using UnityEngine; using UnityEngine.UIElements; @@ -11,7 +10,6 @@ namespace RebootKit.Engine.Services.Development { VisualElement m_RootElement; Label m_FPSLabel; - Label m_GameModeLabel; void Update() { if (m_RootElement == null) { @@ -20,9 +18,6 @@ namespace RebootKit.Engine.Services.Development { Resolution resolution = Screen.currentResolution; m_FPSLabel.text = $"fps: {Mathf.RoundToInt(1f / Time.deltaTime)} | dt: {Time.deltaTime:F4}ms | runtime: {Time.time:F4}s | resolution: {resolution.width}x{resolution.height}@{resolution.refreshRateRatio}Hz"; - m_GameModeLabel.text = RR.GameModes.ActiveGameMode.CurrentValue != null - ? $"Game Mode: {RR.GameModes.ActiveGameMode.CurrentValue.GetType().Name}" - : "Game Mode: none"; } public override VisualElement Build() { @@ -31,7 +26,6 @@ namespace RebootKit.Engine.Services.Development { CreateLabel($"Toggle Overlay [F3] | RebootKit | game: {Application.productName}, version: {Application.version}"); m_FPSLabel = CreateLabel($"FPS: {Application.targetFrameRate}"); - m_GameModeLabel = CreateLabel("Game Mode: none"); return m_RootElement; } diff --git a/Runtime/Engine/Code/EngineConfigAsset.cs b/Runtime/Engine/Code/EngineConfigAsset.cs index 5d2cd71..97d32e4 100755 --- a/Runtime/Engine/Code/EngineConfigAsset.cs +++ b/Runtime/Engine/Code/EngineConfigAsset.cs @@ -1,16 +1,21 @@ using RebootKit.Engine.Main; using UnityEngine; +using UnityEngine.AddressableAssets; namespace RebootKit.Engine { - [CreateAssetMenu(menuName = RConsts.k_AddComponentMenu + RConsts.k_EngineConfigAssetName, fileName = RConsts.k_EngineConfigAssetName)] + [CreateAssetMenu(menuName = RConsts.k_AddComponentMenu + RConsts.k_EngineConfigAssetName, + fileName = RConsts.k_EngineConfigAssetName)] public class EngineConfigAsset : ScriptableObject { + [Header("Core")] public bool initializeOnLoad = true; - public EngineCoreServicesAsset coreServices; - public GameAsset gameAsset; - + [Header("Game")] + public Game gamePrefab; + public AssetReference mainMenuScene; + // @NOTE: Spacewar, change as needed - public uint steamAppID = 480; + [Header("Steam")] + public uint steamAppID = 480; } -} +} \ No newline at end of file diff --git a/Runtime/Engine/Code/EngineCoreServicesAsset.cs b/Runtime/Engine/Code/EngineCoreServicesAsset.cs index 4e52bb7..d162778 100755 --- a/Runtime/Engine/Code/EngineCoreServicesAsset.cs +++ b/Runtime/Engine/Code/EngineCoreServicesAsset.cs @@ -2,6 +2,7 @@ using RebootKit.Engine.Services.Console; using RebootKit.Engine.Services.Input; using RebootKit.Engine.Services.Simulation; +using RebootKit.Engine.Simulation; using UnityEngine; namespace RebootKit.Engine { diff --git a/Runtime/Engine/Code/GameMode.meta b/Runtime/Engine/Code/GameMode.meta deleted file mode 100644 index cb41a80..0000000 --- a/Runtime/Engine/Code/GameMode.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 1177ea3903bdba5419ad3347250cb3b7 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Engine/Code/GameMode/GameModesService.cs b/Runtime/Engine/Code/GameMode/GameModesService.cs deleted file mode 100644 index 7e287e7..0000000 --- a/Runtime/Engine/Code/GameMode/GameModesService.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Threading; -using Cysharp.Threading.Tasks; -using R3; -using RebootKit.Engine.Foundation; -using RebootKit.Engine.Main; -using RebootKit.Engine.Services.Simulation; - -namespace RebootKit.Engine.Services.GameMode { - public class GameModesService : IService { - static readonly Logger s_logger = new Logger(nameof(GameModesService)); - - [Inject] DIContext m_DIContext; - - GameModeAsset m_GameModeAsset; - - readonly CancellationTokenSource m_DestroyCancellationTokenSource = new CancellationTokenSource(); - DisposableBag m_ActiveGameModeDisposableBag; - readonly ReactiveProperty m_ActiveGameMode = new ReactiveProperty(null); - - public ReadOnlyReactiveProperty ActiveGameMode => m_ActiveGameMode; - - public void Dispose() { - m_DestroyCancellationTokenSource.Cancel(); - m_DestroyCancellationTokenSource.Dispose(); - m_ActiveGameModeDisposableBag.Dispose(); - } - - public void Start(GameModeAsset asset, WorldConfig worldConfig) { - if (m_ActiveGameMode.Value != null) { - s_logger.Warning("Game is already running"); - return; - } - - Stop(); - - m_ActiveGameModeDisposableBag = new DisposableBag(); - m_ActiveGameMode.Value = asset.Create(); - m_ActiveGameModeDisposableBag.Add(m_ActiveGameMode); - - InitializeGameModeAsync(worldConfig, CancellationToken.None).Forget(); - } - - async UniTask InitializeGameModeAsync(WorldConfig worldConfig, CancellationToken cancellationToken) { - await m_ActiveGameMode.Value.OnInit(m_DestroyCancellationTokenSource.Token); - - await RR.World.LoadAsync(worldConfig, cancellationToken); - m_ActiveGameMode.Value.OnStart(); - Observable.EveryUpdate().Subscribe(_ => { m_ActiveGameMode.Value?.OnTick(); }).AddTo(ref m_ActiveGameModeDisposableBag); - } - - public void Stop() { - if (m_ActiveGameMode.Value == null) { - return; - } - - m_ActiveGameMode.Value.OnStop(); - m_ActiveGameMode.Value = null; - - m_ActiveGameModeDisposableBag.Dispose(); - } - } -} \ No newline at end of file diff --git a/Runtime/Engine/Code/GameMode/GameModesService.cs.meta b/Runtime/Engine/Code/GameMode/GameModesService.cs.meta deleted file mode 100644 index b95ba9e..0000000 --- a/Runtime/Engine/Code/GameMode/GameModesService.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 82082a4d082be274bbec52347d25b836 \ No newline at end of file diff --git a/Runtime/Engine/Code/GameMode/IGameMode.cs b/Runtime/Engine/Code/GameMode/IGameMode.cs deleted file mode 100644 index 9c8e309..0000000 --- a/Runtime/Engine/Code/GameMode/IGameMode.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Threading; -using Cysharp.Threading.Tasks; -using RebootKit.Engine.Foundation; - -namespace RebootKit.Engine.Services.GameMode { - public interface IGameMode : IDisposable { - UniTask OnInit(CancellationToken cancellationToken); - - void OnStart(); - void OnStop(); - - void OnTick(); - } - - public abstract class GameModeAsset : FactoryAsset { - } -} \ No newline at end of file diff --git a/Runtime/Engine/Code/GameMode/IGameMode.cs.meta b/Runtime/Engine/Code/GameMode/IGameMode.cs.meta deleted file mode 100644 index 522d6ea..0000000 --- a/Runtime/Engine/Code/GameMode/IGameMode.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 562c4ff92afe4949b468003a0e997522 -timeCreated: 1743456239 \ No newline at end of file diff --git a/Runtime/Engine/Code/Main/EntryPoint.cs b/Runtime/Engine/Code/Main/EntryPoint.cs index ebf2f89..c9260c4 100755 --- a/Runtime/Engine/Code/Main/EntryPoint.cs +++ b/Runtime/Engine/Code/Main/EntryPoint.cs @@ -35,8 +35,12 @@ namespace RebootKit.Engine.Main { static async UniTask RunAsync(CancellationToken cancellationToken) { ConfigVarsContainer.Init(); + // We are loading the boot scene only in the editor. + // In the player, we assume that the boot scene is already loaded. +#if UNITY_EDITOR s_logger.Info("Loading boot scene"); SceneManager.LoadScene(RConsts.k_BootSceneBuildIndex, LoadSceneMode.Single); +#endif s_logger.Info("Loading engine config"); EngineConfigAsset configAsset = Resources.Load(RConsts.k_EngineConfigResourcesPath); @@ -53,10 +57,11 @@ namespace RebootKit.Engine.Main { await RR.InitAsync(configAsset, cancellationToken); s_logger.Info("Loading main scene"); - await SceneManager.LoadSceneAsync(RConsts.k_MainSceneBuildIndex, LoadSceneMode.Single).ToUniTask(cancellationToken: cancellationToken); + await SceneManager.LoadSceneAsync(RConsts.k_MainSceneBuildIndex, LoadSceneMode.Single) + .ToUniTask(cancellationToken: cancellationToken); s_logger.Info("Starting RR"); - RR.Run(); + await RR.RunAsync(cancellationToken); await UniTask.WaitUntilCanceled(Application.exitCancellationToken); diff --git a/Runtime/Engine/Code/Main/Game.cs b/Runtime/Engine/Code/Main/Game.cs new file mode 100644 index 0000000..7a37d6f --- /dev/null +++ b/Runtime/Engine/Code/Main/Game.cs @@ -0,0 +1,78 @@ +using System; +using Cysharp.Threading.Tasks; +using RebootKit.Engine.Services.Simulation; +using Unity.Collections; +using Unity.Netcode; +using UnityEngine; +using Logger = RebootKit.Engine.Foundation.Logger; + +namespace RebootKit.Engine.Main { + public abstract class Game : NetworkBehaviour { + static readonly Logger s_GameLogger = new Logger(nameof(Game)); + + protected NetworkVariable m_CurrentWorldID = + new NetworkVariable(new FixedString128Bytes("")); + + // Event callbacks + public virtual void OnWorldLoaded() { + } + + public virtual void OnWorldUnload() { + } + + public virtual void OnChatMessage(string message) { + s_GameLogger.Info($"Chat: {message}"); + } + + // Network + public override void OnNetworkSpawn() { + base.OnNetworkSpawn(); + RR.GameInstance = this; + + m_CurrentWorldID.OnValueChanged += OnCurrentWorldIDChanged; + LoadWorld(m_CurrentWorldID.Value.Value); + } + + public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); + + m_CurrentWorldID.OnValueChanged -= OnCurrentWorldIDChanged; + RR.GameInstance = null; + } + + [ServerRpc] + public void SetCurrentWorldServerRpc(string worldID) { + m_CurrentWorldID.Value = new FixedString128Bytes(worldID); + } + + // Chat + [Rpc(SendTo.Server)] + public void SendChatMessageRpc(string message) { + PrintChatMessageClientRpc(message); + } + + [ClientRpc(Delivery = RpcDelivery.Reliable)] + void PrintChatMessageClientRpc(string message) { + OnChatMessage(message); + } + + void OnCurrentWorldIDChanged(FixedString128Bytes previousValue, FixedString128Bytes newValue) { + string worldID = newValue.Value; + LoadWorld(worldID); + } + + void LoadWorld(string worldID) { + if (string.IsNullOrEmpty(worldID)) { + RR.World.Unload(); + } else { + WorldConfigAsset worldConfigAsset = RR.GetWorldConfigAsset(worldID); + if (worldConfigAsset is not null) { + RR.CloseMainMenu(); + RR.World.LoadAsync(worldConfigAsset.Config, Application.exitCancellationToken).Forget(); + } else { + s_GameLogger.Error($"World config asset for '{worldID}' not found."); + } + } + } + } +} \ No newline at end of file diff --git a/Runtime/Engine/Code/Main/Game.cs.meta b/Runtime/Engine/Code/Main/Game.cs.meta new file mode 100644 index 0000000..e1524a1 --- /dev/null +++ b/Runtime/Engine/Code/Main/Game.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3c1089fa01934ad69bf229123b190c07 +timeCreated: 1750977602 \ No newline at end of file diff --git a/Runtime/Engine/Code/Main/NetworkPlayerController.cs b/Runtime/Engine/Code/Main/NetworkPlayerController.cs new file mode 100644 index 0000000..b023ece --- /dev/null +++ b/Runtime/Engine/Code/Main/NetworkPlayerController.cs @@ -0,0 +1,6 @@ +using Unity.Netcode; + +namespace RebootKit.Engine.Main { + public abstract class NetworkPlayerController : NetworkBehaviour { + } +} \ No newline at end of file diff --git a/Runtime/Engine/Code/Main/NetworkPlayerController.cs.meta b/Runtime/Engine/Code/Main/NetworkPlayerController.cs.meta new file mode 100644 index 0000000..aaad94f --- /dev/null +++ b/Runtime/Engine/Code/Main/NetworkPlayerController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2010b0aabd68415bb4aea2846d2c59b1 +timeCreated: 1751208839 \ No newline at end of file diff --git a/Runtime/Engine/Code/Main/RR.cs b/Runtime/Engine/Code/Main/RR.cs index 53c94d9..bb8ef42 100755 --- a/Runtime/Engine/Code/Main/RR.cs +++ b/Runtime/Engine/Code/Main/RR.cs @@ -4,52 +4,54 @@ using System.Threading; using Cysharp.Threading.Tasks; using R3; using RebootKit.Engine.Foundation; -using RebootKit.Engine.Multiplayer; using RebootKit.Engine.Services.Console; -using RebootKit.Engine.Services.GameMode; using RebootKit.Engine.Services.Input; using RebootKit.Engine.Services.Simulation; -using RebootKit.Engine.Steam; -using Unity.Collections; +using RebootKit.Engine.Simulation; +using Unity.Netcode; using UnityEngine; using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceProviders; +using UnityEngine.SceneManagement; using Assert = UnityEngine.Assertions.Assert; using Logger = RebootKit.Engine.Foundation.Logger; +using Object = UnityEngine.Object; namespace RebootKit.Engine.Main { - public interface IGame : IDisposable { - UniTask InitAsync(CancellationToken cancellationToken); - void Run(); - } - - public abstract class GameAsset : ScriptableObject { - public abstract IGame CreateGame(); - } - public static class RR { static readonly Logger s_Logger = new Logger("RR"); [ConfigVar("con.write_log", 1, "Enables writing game log to console output")] static ConfigVar s_writeLogToConsole; + + [ConfigVar("sv.tick_rate", 60, "Server tick rate in Hz")] + public static ConfigVar TickRate; internal static EngineConfigAsset EngineConfig; static DisposableBag s_disposableBag; static DisposableBag s_servicesBag; - public static ConsoleService Console { get; private set; } + static AsyncOperationHandle s_mainMenuSceneHandle; + + internal static ConsoleService Console { get; private set; } public static InputService Input { get; private set; } public static WorldService World { get; private set; } - public static GameModesService GameModes { get; private set; } public static Camera MainCamera { get; internal set; } - static IGame s_game; + internal static Game GameInstance; - // Core + public static ulong TickCount { get; private set; } + public static event Action ServerTick = delegate { }; + public static event Action ClientTick = delegate { }; + + // Lifecycle API + + // @NOTE: This method is called at the very start of the game, when boot scene loaded. internal static async UniTask InitAsync(EngineConfigAsset configAsset, CancellationToken cancellationToken) { Assert.IsNotNull(configAsset, "Config asset is required"); - Assert.IsNotNull(configAsset.gameAsset, "Game asset is required"); EngineConfig = configAsset; @@ -57,36 +59,31 @@ namespace RebootKit.Engine.Main { s_servicesBag = new DisposableBag(); s_disposableBag = new DisposableBag(); - s_Logger.Debug("Registering core services"); + s_Logger.Info("Registering core services"); Console = CreateService(EngineConfig.coreServices.consoleService); Input = CreateService(EngineConfig.coreServices.inputService); World = CreateService(EngineConfig.coreServices.worldService); - GameModes = CreateService(); await InitializeAssetsAsync(cancellationToken); - - await SteamManager.InitializeAsync(cancellationToken); - if (SteamManager.IsInitialized) { - s_networkTransport = SteamManager.NetworkTransport; - } + // await SteamManager.InitializeAsync(cancellationToken); - s_Logger.Debug("Creating game"); - s_game = EngineConfig.gameAsset.CreateGame(); - - await s_game.InitAsync(cancellationToken); + // if (SteamManager.IsInitialized) { + // s_networkTransport = SteamManager.NetworkTransport; + // } } - internal static void Shutdown() { - SteamManager.Shutdown(); - - s_Logger.Info("Shutting down"); - s_servicesBag.Dispose(); - s_disposableBag.Dispose(); - } + // @NOTE: This method is called after the main scene is loaded. + internal static async UniTask RunAsync(CancellationToken cancellationToken) { + NetworkManager.Singleton.OnConnectionEvent += OnConnectionEvent; + NetworkManager.Singleton.OnServerStarted += OnServerStarted; + NetworkManager.Singleton.OnServerStopped += OnServerStopped; - internal static void Run() { - s_game.Run(); + Observable.EveryUpdate() + .Subscribe(_ => Tick()) + .AddTo(ref s_disposableBag); + + await OpenMainMenuAsync(cancellationToken); #if UNITY_EDITOR string scriptContent = UnityEditor.EditorPrefs.GetString("RebootKitEditor.OnGameRunScriptContent", ""); @@ -101,44 +98,49 @@ namespace RebootKit.Engine.Main { #endif } + internal static void Shutdown() { + s_Logger.Info("Shutting down"); + + if (GameInstance is not null) { + GameInstance.NetworkObject.Despawn(); + Object.Destroy(GameInstance); + GameInstance = null; + } + + + if (NetworkManager.Singleton is not null) { + NetworkManager.Singleton.OnConnectionEvent -= OnConnectionEvent; + NetworkManager.Singleton.OnServerStarted -= OnServerStarted; + NetworkManager.Singleton.OnServerStopped -= OnServerStopped; + } + + // SteamManager.Shutdown(); + + s_servicesBag.Dispose(); + s_disposableBag.Dispose(); + } + // Assets API - static readonly List s_GameModesAssets = new List(); static readonly List s_WorldConfigsAssets = new List(); - public static IReadOnlyList GameModesAssets => s_GameModesAssets; public static IReadOnlyList WorldConfigsAssets => s_WorldConfigsAssets; - public static async UniTask InitializeAssetsAsync(CancellationToken cancellationToken) { - s_GameModesAssets.Clear(); + static async UniTask InitializeAssetsAsync(CancellationToken cancellationToken) { s_WorldConfigsAssets.Clear(); s_Logger.Info("Loading game assets"); - await Addressables.LoadAssetsAsync("game_mode", asset => { s_GameModesAssets.Add(asset); }).ToUniTask(cancellationToken: cancellationToken); - s_Logger.Info($"Loaded {s_GameModesAssets.Count} game modes"); - - await Addressables.LoadAssetsAsync("world", asset => { s_WorldConfigsAssets.Add(asset); }).ToUniTask(cancellationToken: cancellationToken); + await Addressables.LoadAssetsAsync("world", asset => { s_WorldConfigsAssets.Add(asset); }) + .ToUniTask(cancellationToken: cancellationToken); } - - public static GameModeAsset GetGameMode(string name) { - if (string.IsNullOrEmpty(name)) { - throw new ArgumentException("Game mode name cannot be null or empty", nameof(name)); - } - GameModeAsset gameMode = s_GameModesAssets.Find(asset => asset.name.Equals(name, StringComparison.Ordinal)); - if (!gameMode) { - throw new KeyNotFoundException($"Game mode '{name}' not found"); - } - - return gameMode; - } - public static WorldConfigAsset GetWorldConfigAsset(string name) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("World config name cannot be null or empty", nameof(name)); } - WorldConfigAsset worldConfig = s_WorldConfigsAssets.Find(asset => asset.Config.name.Equals(name, StringComparison.Ordinal)); + WorldConfigAsset worldConfig = + s_WorldConfigsAssets.Find(asset => asset.Config.name.Equals(name, StringComparison.Ordinal)); if (!worldConfig) { throw new KeyNotFoundException($"World config '{name}' not found"); } @@ -147,22 +149,48 @@ namespace RebootKit.Engine.Main { } // Game API - public static void StartGameMode(GameModeAsset gameMode, WorldConfig world) { - if (!IsClient() || !IsHost()) { - s_Logger.Error("Cannot start game mode: you must be connected to a server and be the host"); + public static async UniTask OpenMainMenuAsync(CancellationToken cancellationToken) { + s_Logger.Info("Opening main menu"); + + World.Unload(); + + if (!EngineConfig.mainMenuScene.RuntimeKeyIsValid()) { + s_Logger.Error("Main menu scene is not set in EngineConfig"); return; } - s_Logger.Info($"Starting game mode: {gameMode.name} in world: {world.name}"); - GameModes.Start(gameMode, world); + s_mainMenuSceneHandle = Addressables.LoadSceneAsync(EngineConfig.mainMenuScene, LoadSceneMode.Additive); + await s_mainMenuSceneHandle; } - public static TGame Game() where TGame : IGame { - if (s_game is TGame game) { - return game; + internal static void CloseMainMenu() { + if (!s_mainMenuSceneHandle.IsValid()) { + return; + } + + Addressables.UnloadSceneAsync(s_mainMenuSceneHandle); + } + + public static void SetServerWorld(string worldID) { + if (!IsServer()) { + s_Logger.Error("Cannot set server world. Not a server instance."); + return; + } + + if (GameInstance is null) { + s_Logger.Error("Game is not initialized. Cannot set server world."); + return; } - throw new InvalidOperationException($"Game is not of type {typeof(TGame)}"); + s_Logger.Info($"Setting server world: {worldID}"); + + WorldConfigAsset worldConfigAsset = GetWorldConfigAsset(worldID); + if (worldConfigAsset is null) { + s_Logger.Error($"World '{worldID}' not found"); + return; + } + + GameInstance.SetCurrentWorldServerRpc(worldID); } // Service API @@ -170,7 +198,7 @@ namespace RebootKit.Engine.Main { if (asset is null) { throw new ArgumentNullException($"Null asset of type {typeof(TService)}"); } - + TService service = asset.Create(); s_servicesBag.Add(service); return service; @@ -182,10 +210,9 @@ namespace RebootKit.Engine.Main { return service; } - // General API + // Logging API public static void Log(string message) { Debug.Log(message); - Console?.WriteToOutput(message); } @@ -199,6 +226,10 @@ namespace RebootKit.Engine.Main { Console?.WriteToOutput(message); } + public static void WriteToConsole(string message) { + Console?.WriteToOutput(message); + } + // CVar API public static ConfigVar CVarIndex(string name, int defaultValue = -1) { ConfigVar cvar = ConfigVarsContainer.Get(name); @@ -232,48 +263,112 @@ namespace RebootKit.Engine.Main { ConfigVarsContainer.Register(cvar); return cvar; } - + // Network API - static GameLobby s_gameLobby; - static INetworkTransport s_networkTransport; - - public static bool IsHost() { - return s_networkTransport.IsServer(); + public static bool IsServer() { + return NetworkManager.Singleton.IsServer; } public static bool IsClient() { - return s_networkTransport.IsClient(); - } - - public static int GetPing() { - return -1; - } - - public static void HostServer(bool offline = false) { - s_networkTransport.StartServer(); - } - - public static void ConnectToLobby() { - s_networkTransport.Connect(Steamworks.SteamNetworkingSockets.Identity.SteamId); + return NetworkManager.Singleton.IsClient; } + public static void StartHost() { + if (NetworkManager.Singleton.IsHost) { + s_Logger.Error("Already hosting a server"); + return; + } + + s_Logger.Info("Starting host"); + NetworkManager.Singleton.StartHost(); + } + + public static void StopServer() { + } + + public static void Connect() { + if (NetworkManager.Singleton.IsClient) { + s_Logger.Error("Already connected to a server"); + return; + } + + s_Logger.Info($"Connecting to server."); + NetworkManager.Singleton.StartClient(); + } + public static void Disconnect() { - s_networkTransport.Disconnect(); } - internal static void OnConnected(GameLobby lobby) { + public static void SendChatMessage(string message) { + if (!IsClient()) { + s_Logger.Error("Cannot send chat message. Not connected to a server."); + return; + } + + if (string.IsNullOrEmpty(message)) { + return; + } + + GameInstance.SendChatMessageRpc(message); } - internal static void OnDisconnected() { - } - - internal static void OnServerDataReceived(byte[] data) { - s_Logger.Debug($"[SERVER] Data received: {data.Length} bytes"); + static float s_tickTimer; + + static void Tick() { + float deltaTime = Time.deltaTime; + float minTickTime = 1.0f / TickRate.IndexValue; + s_tickTimer += deltaTime; + + while (s_tickTimer >= minTickTime) { + s_tickTimer -= minTickTime; + + if (IsServer()) { + ServerTick?.Invoke(TickCount); + } + + if (IsClient()) { + ClientTick?.Invoke(); + } + + TickCount++; + } + + World.Tick(deltaTime); } - internal static void OnClientDataReceived(byte[] data) { - s_Logger.Debug($"[CLIENT] Data received: {data.Length} bytes"); + static void OnConnectionEvent(NetworkManager network, ConnectionEventData data) { + s_Logger.Info("Connection event: " + data.EventType); + } + + static void OnServerStarted() { + s_Logger.Info("Server started"); + + GameInstance = Object.Instantiate(EngineConfig.gamePrefab); + GameInstance.NetworkObject.Spawn(); + } + + static void OnServerStopped(bool obj) { + s_Logger.Info("Server stopped"); + + if (GameInstance is not null) { + GameInstance.NetworkObject.Despawn(); + Object.Destroy(GameInstance.gameObject); + } + + GameInstance = null; + } + + // Console Commands + [RCCMD("say", "Sends chat message")] + static void Say(string[] args) { + if (args.Length < 2) { + Console.WriteToOutput("Usage: say "); + return; + } + + string message = string.Join(" ", args, 1, args.Length - 1); + SendChatMessage(message); } } } \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer.meta b/Runtime/Engine/Code/Multiplayer.meta deleted file mode 100644 index be0188b..0000000 --- a/Runtime/Engine/Code/Multiplayer.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 83d5dc53310c40caab8b7732b9cecbdc -timeCreated: 1750615656 \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer/GameLobby.cs b/Runtime/Engine/Code/Multiplayer/GameLobby.cs deleted file mode 100644 index 7d5aedc..0000000 --- a/Runtime/Engine/Code/Multiplayer/GameLobby.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace RebootKit.Engine.Multiplayer { - public class GameLobby { - public string GameModeID { get; private set; } - public string WorldID { get; private set; } - - } -} \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer/GameLobby.cs.meta b/Runtime/Engine/Code/Multiplayer/GameLobby.cs.meta deleted file mode 100644 index ef18829..0000000 --- a/Runtime/Engine/Code/Multiplayer/GameLobby.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: cf39e9785e4e4ad0a7ebb8b19c882b2a -timeCreated: 1750628631 \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer/INetworkTransport.cs b/Runtime/Engine/Code/Multiplayer/INetworkTransport.cs deleted file mode 100644 index 0695a3e..0000000 --- a/Runtime/Engine/Code/Multiplayer/INetworkTransport.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace RebootKit.Engine.Multiplayer { - public enum SendMode { - Reliable, - Unreliable - } - - public interface INetworkTransport { - void Initialize(); - void Shutdown(); - - bool IsServer(); - bool IsClient(); - - bool StartServer(); - void StopServer(); - - bool Connect(ulong serverID); - void Disconnect(); - - void Send(ulong clientID, ArraySegment data, SendMode mode); - } -} \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer/INetworkTransport.cs.meta b/Runtime/Engine/Code/Multiplayer/INetworkTransport.cs.meta deleted file mode 100644 index ab6100d..0000000 --- a/Runtime/Engine/Code/Multiplayer/INetworkTransport.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 702059b29fda4edc80caf8b22cd8c0d7 -timeCreated: 1750759626 \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer/NetworkPacket.cs b/Runtime/Engine/Code/Multiplayer/NetworkPacket.cs deleted file mode 100644 index 746d5d4..0000000 --- a/Runtime/Engine/Code/Multiplayer/NetworkPacket.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace RebootKit.Engine.Multiplayer { - - -} \ No newline at end of file diff --git a/Runtime/Engine/Code/Multiplayer/NetworkPacket.cs.meta b/Runtime/Engine/Code/Multiplayer/NetworkPacket.cs.meta deleted file mode 100644 index ffd148f..0000000 --- a/Runtime/Engine/Code/Multiplayer/NetworkPacket.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 34d66ac5d1c443e8992a84edd5eb796e -timeCreated: 1750628495 \ No newline at end of file diff --git a/Runtime/Engine/Code/Simulation/Actor.cs b/Runtime/Engine/Code/Simulation/Actor.cs index a1e2151..7356517 100644 --- a/Runtime/Engine/Code/Simulation/Actor.cs +++ b/Runtime/Engine/Code/Simulation/Actor.cs @@ -1,11 +1,8 @@ -using RebootKit.Engine.Foundation; +using Unity.Collections; using UnityEngine; -namespace RebootKit.Engine.Services.Simulation { +namespace RebootKit.Engine.Simulation { public abstract class Actor : MonoBehaviour { - [field: SerializeField] - public SerializableGuid ActorGuid { get; private set; } = SerializableGuid.New(); - bool m_IsPlaying = false; public bool IsPlaying { get { @@ -39,7 +36,13 @@ namespace RebootKit.Engine.Services.Simulation { public virtual void OnEndPlay() { } - public virtual void OnTick(float deltaTime) { + public virtual void Tick(float deltaTime) { + } + + public virtual void SerializeNetworkState(ref DataStreamWriter writer) { + } + + public virtual void DeserializeNetworkState(ref DataStreamReader reader) { } } } \ No newline at end of file diff --git a/Runtime/Engine/Code/Simulation/WorldService.cs b/Runtime/Engine/Code/Simulation/WorldService.cs index 7db9646..879be62 100644 --- a/Runtime/Engine/Code/Simulation/WorldService.cs +++ b/Runtime/Engine/Code/Simulation/WorldService.cs @@ -1,9 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; -using R3; using RebootKit.Engine.Foundation; +using RebootKit.Engine.Main; +using RebootKit.Engine.Services.Simulation; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Assertions; @@ -12,7 +12,7 @@ using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; using Logger = RebootKit.Engine.Foundation.Logger; -namespace RebootKit.Engine.Services.Simulation { +namespace RebootKit.Engine.Simulation { public interface IWorldContext { } public class WorldService : IService { @@ -30,26 +30,28 @@ namespace RebootKit.Engine.Services.Simulation { struct ActorData { public Actor Actor; - public readonly bool ManagedByAddressabled; + public readonly bool ManagedByAddressables; - public ActorData(Actor actor, bool managedByAddressabled) { + public ActorData(Actor actor, bool managedByAddressables) { Actor = actor; - ManagedByAddressabled = managedByAddressabled; + ManagedByAddressables = managedByAddressables; } } readonly List m_Actors = new List(); - readonly IDisposable m_UpdateSubscription; public IWorldContext Context { get; private set; } + + public string WorldID { + get { + return string.IsNullOrEmpty(m_Config.name) ? string.Empty : m_Config.name; + } + } public WorldService() { - m_UpdateSubscription = Observable.EveryUpdate() - .Subscribe(_ => { Tick(Time.deltaTime); }); } public void Dispose() { - m_UpdateSubscription.Dispose(); Unload(); } @@ -80,9 +82,21 @@ namespace RebootKit.Engine.Services.Simulation { } m_WorldState = WorldState.Loaded; + + if (RR.GameInstance is not null) { + RR.GameInstance.OnWorldLoaded(); + } } public void Unload() { + if (m_WorldState == WorldState.Unloaded) { + return; + } + + if (RR.GameInstance is not null) { + RR.GameInstance.OnWorldUnload(); + } + KillAllActors(); if (m_SceneInstance.IsValid()) { @@ -134,7 +148,7 @@ namespace RebootKit.Engine.Services.Simulation { actor.IsPlaying = false; actor.OnDespawned(); - if (actorData.ManagedByAddressabled) { + if (actorData.ManagedByAddressables) { Addressables.ReleaseInstance(actor.gameObject); } } @@ -144,7 +158,7 @@ namespace RebootKit.Engine.Services.Simulation { actorData.Actor.IsPlaying = false; actorData.Actor.OnDespawned(); - if (actorData.ManagedByAddressabled) { + if (actorData.ManagedByAddressables) { Addressables.ReleaseInstance(actorData.Actor.gameObject); } else { UnityEngine.Object.Destroy(actorData.Actor.gameObject); @@ -154,14 +168,16 @@ namespace RebootKit.Engine.Services.Simulation { m_Actors.Clear(); } - void Tick(float deltaTime) { + public void Tick(float deltaTime) { if (m_WorldState != WorldState.Loaded) { return; } - + foreach (ActorData actorData in m_Actors) { - if (actorData.Actor.IsPlaying) { - actorData.Actor.OnTick(deltaTime); + Actor actor = actorData.Actor; + + if (actor.IsPlaying) { + actor.Tick(deltaTime); } } } diff --git a/Runtime/Engine/Code/Simulation/WorldServiceAsset.cs b/Runtime/Engine/Code/Simulation/WorldServiceAsset.cs index 619ff4a..4e2cfe5 100644 --- a/Runtime/Engine/Code/Simulation/WorldServiceAsset.cs +++ b/Runtime/Engine/Code/Simulation/WorldServiceAsset.cs @@ -1,4 +1,5 @@ using RebootKit.Engine.Foundation; +using RebootKit.Engine.Simulation; using UnityEngine; namespace RebootKit.Engine.Services.Simulation { diff --git a/Runtime/Engine/Code/Steam/SteamManager.cs b/Runtime/Engine/Code/Steam/SteamManager.cs index 7c51ba6..97cb1c6 100644 --- a/Runtime/Engine/Code/Steam/SteamManager.cs +++ b/Runtime/Engine/Code/Steam/SteamManager.cs @@ -3,7 +3,6 @@ using System.Threading; using Cysharp.Threading.Tasks; using R3; using RebootKit.Engine.Main; -using RebootKit.Engine.Multiplayer; using Steamworks; using Logger = RebootKit.Engine.Foundation.Logger; @@ -13,8 +12,6 @@ namespace RebootKit.Engine.Steam { public static bool IsInitialized { get; private set; } = false; - public static INetworkTransport NetworkTransport { get; private set; } = new SteamNetworkTransport(); - internal static async UniTask InitializeAsync(CancellationToken cancellationToken = default) { s_Logger.Info("Initializing Steam Manager..."); IsInitialized = false; @@ -26,8 +23,6 @@ namespace RebootKit.Engine.Steam { return; } - NetworkTransport.Initialize(); - IsInitialized = true; await UniTask.Yield(cancellationToken); @@ -41,7 +36,6 @@ namespace RebootKit.Engine.Steam { s_Logger.Info("Shutting down Steam Manager..."); - NetworkTransport.Shutdown(); SteamClient.Shutdown(); IsInitialized = false; diff --git a/Runtime/Engine/Code/Steam/SteamNetworkManager.cs b/Runtime/Engine/Code/Steam/SteamNetworkManager.cs new file mode 100644 index 0000000..936916e --- /dev/null +++ b/Runtime/Engine/Code/Steam/SteamNetworkManager.cs @@ -0,0 +1,237 @@ +#define RR_LOCALHOST_ONLY + +using System; +using R3; +using RebootKit.Engine.Foundation; +using RebootKit.Engine.Main; +using Steamworks; +using Steamworks.Data; +using Logger = RebootKit.Engine.Foundation.Logger; + +namespace RebootKit.Engine.Steam { +// class SteamNetworkManager : INetworkManager { +// static readonly Logger s_Logger = new Logger(nameof(SteamNetworkManager)); +// +// const int k_DefaultPort = 420; +// +// SocketManager m_SocketManager; +// ConnectionManager m_ConnectionManager; +// IDisposable m_TickDisposable; +// +// SteamId m_HostSteamID; +// +// public void Initialize(INetworkManagerEventHandler eventHandler) { +// m_TickDisposable = Observable.EveryUpdate() +// .Subscribe(_ => Tick()); +// +// SteamNetworkingUtils.DebugLevel = NetDebugOutput.Debug; +// +// SteamNetworkingSockets.OnConnectionStatusChanged += OnConnectionStatusChanged; +// SteamNetworkingUtils.OnDebugOutput += OnSteamNetworkDebugOutput; +// +// #if !RR_LOCALHOST_ONLY +// SteamNetworkingUtils.InitRelayNetworkAccess(); +// #endif +// } +// +// public void Shutdown() { +// Disconnect(); +// +// SteamNetworkingUtils.OnDebugOutput -= OnSteamNetworkDebugOutput; +// SteamNetworkingSockets.OnConnectionStatusChanged -= OnConnectionStatusChanged; +// +// m_TickDisposable.Dispose(); +// } +// +// public bool IsServer() { +// return m_SocketManager != null; +// } +// +// public bool IsClient() { +// return m_ConnectionManager != null; +// } +// +// public bool StartServer() { +// Disconnect(); +// +// s_Logger.Info("Creating server..."); +// +// try { +// m_HostSteamID = SteamNetworkingSockets.Identity.SteamId; +// +// #if RR_LOCALHOST_ONLY +// m_SocketManager = SteamNetworkingSockets.CreateNormalSocket(NetAddress.LocalHost(2137), new ServerCallbacks()); +// m_ConnectionManager = SteamNetworkingSockets.ConnectNormal(NetAddress.LocalHost(2137), new ClientCallbacks()); +// #else +// m_SocketManager = SteamNetworkingSockets.CreateRelaySocket(k_DefaultPort); +// m_SocketManager.Interface = new ServerCallbacks(); +// m_ConnectionManager = +// SteamNetworkingSockets.ConnectRelay(m_HostSteamID, k_DefaultPort); +// m_ConnectionManager.Interface = new ClientCallbacks(); +// #endif +// } catch (Exception e) { +// s_Logger.Error($"Failed to create server: {e.Message}"); +// m_SocketManager = null; +// m_ConnectionManager = null; +// +// return false; +// } +// +// return true; +// } +// +// public void StopServer() { +// Disconnect(); +// } +// +// public bool Connect(ulong serverID) { +// if (IsServer()) { +// s_Logger.Error("Cannot connect to a server while running as a server."); +// return false; +// } +// +// Disconnect(); +// +// s_Logger.Info("Connecting to server with steam ID: " + serverID); +// try { +// #if RR_LOCALHOST_ONLY +// m_ConnectionManager = +// SteamNetworkingSockets.ConnectNormal(NetAddress.LocalHost(2137), new ClientCallbacks()); +// #else +// m_ConnectionManager = SteamNetworkingSockets.ConnectRelay(serverID, k_DefaultPort); +// m_ConnectionManager.Interface = new ClientCallbacks(); +// #endif +// +// m_HostSteamID = serverID; +// } catch (Exception e) { +// s_Logger.Error($"Failed to connect to server with ID {serverID}: {e.Message}"); +// m_ConnectionManager = null; +// +// return false; +// } +// +// return true; +// } +// +// public void Disconnect() { +// if (m_ConnectionManager != null) { +// s_Logger.Info("Disconnecting from the server..."); +// m_ConnectionManager.Close(); +// m_ConnectionManager = null; +// } +// +// if (m_SocketManager != null) { +// s_Logger.Info("Shutting down the server..."); +// m_SocketManager.Close(); +// m_SocketManager = null; +// } +// } +// public void Send(ulong clientID, NetworkMessage message, SendMode mode) { +// if (!IsServer()) { +// return; +// } +// +// foreach (Connection connection in m_SocketManager.Connected) { +// connection.SendMessage(new byte[] { +// 0xDE, +// 0xAD, +// 0xBE, +// 0xEF +// }, SendType.Reliable); +// } +// } +// +// public void Broadcast(NetworkMessage message, SendMode mode) { +// if (!IsServer()) { +// return; +// } +// +// foreach (Connection connection in m_SocketManager.Connected) { +// connection.SendMessage(new byte[] { +// 0xDE, +// 0xAD, +// 0xBE, +// 0xEF +// }, SendType.Reliable); +// } +// } +// +// void OnSteamNetworkDebugOutput(NetDebugOutput level, string message) { +// LogLevel logLevel = level switch { +// NetDebugOutput.Debug => LogLevel.Debug, +// NetDebugOutput.Msg => LogLevel.Info, +// NetDebugOutput.Warning => LogLevel.Warning, +// NetDebugOutput.Error => LogLevel.Error, +// _ => LogLevel.Info +// }; +// +// s_Logger.Log(logLevel, message); +// } +// +// void Tick() { +// m_SocketManager?.Receive(); +// m_ConnectionManager?.Receive(); +// } +// +// void OnConnectionStatusChanged(Connection connection, ConnectionInfo info) { +// s_Logger.Info($"OnConnectionStatusChanged: {connection.Id} - {info.Identity} - Status: {info.State}"); +// } +// +// class ServerCallbacks : ISocketManager { +// public void OnConnecting(Connection connection, ConnectionInfo data) { +// s_Logger.Info($"OnConnecting: {connection.Id} - {data.Identity}"); +// +// connection.Accept(); +// } +// +// public void OnConnected(Connection connection, ConnectionInfo data) { +// s_Logger.Info($"OnConnected: {connection.Id} - {data.Identity}"); +// +// connection.SendMessage(new byte[] { +// 0xBE, +// 0xFE, +// 0x00, +// 0x00 +// }, 0, 4, 0); +// } +// +// public void OnDisconnected(Connection connection, ConnectionInfo data) { +// s_Logger.Info($"OnDisconnected: {connection.Id} - {data.Identity}"); +// } +// +// public void OnMessage(Connection connection, +// NetIdentity identity, +// IntPtr data, +// int size, +// long messageNum, +// long recvTime, +// int channel) { +// byte[] buffer = new byte[size]; +// System.Runtime.InteropServices.Marshal.Copy(data, buffer, 0, size); +// +// s_Logger.Info($"OnMessage: {connection.Id} - {identity} - Size: {size} - Channel: {channel}"); +// } +// } +// +// class ClientCallbacks : IConnectionManager { +// public void OnConnected(ConnectionInfo info) { +// s_Logger.Info("ConnectionOnConnected"); +// } +// +// public void OnConnecting(ConnectionInfo info) { +// s_Logger.Info("ConnectionOnConnecting"); +// } +// +// public void OnDisconnected(ConnectionInfo info) { +// s_Logger.Info("ConnectionOnDisconnected"); +// } +// +// public void OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel) { +// byte[] buffer = new byte[size]; +// System.Runtime.InteropServices.Marshal.Copy(data, buffer, 0, size); +// +// s_Logger.Info($"OnMessage: Size: {size} - Channel: {channel}"); +// } +// } +// } +} \ No newline at end of file diff --git a/Runtime/Engine/Code/Steam/SteamNetworkTransport.cs.meta b/Runtime/Engine/Code/Steam/SteamNetworkManager.cs.meta similarity index 100% rename from Runtime/Engine/Code/Steam/SteamNetworkTransport.cs.meta rename to Runtime/Engine/Code/Steam/SteamNetworkManager.cs.meta diff --git a/Runtime/Engine/Code/Steam/SteamNetworkTransport.cs b/Runtime/Engine/Code/Steam/SteamNetworkTransport.cs deleted file mode 100644 index a5de7b8..0000000 --- a/Runtime/Engine/Code/Steam/SteamNetworkTransport.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using R3; -using RebootKit.Engine.Foundation; -using RebootKit.Engine.Main; -using RebootKit.Engine.Multiplayer; -using Steamworks; -using Steamworks.Data; -using Logger = RebootKit.Engine.Foundation.Logger; - -namespace RebootKit.Engine.Steam { - class SteamNetworkTransport : INetworkTransport { - static readonly Logger s_Logger = new Logger(nameof(SteamNetworkTransport)); - - const int k_DefaultPort = 420; - - ServerCallbacks m_SocketManager; - ClientCallbacks m_ConnectionManager; - IDisposable m_TickDisposable; - - SteamId m_HostSteamID; - - public void Initialize() { - m_TickDisposable = Observable.EveryUpdate() - .Subscribe(_ => Tick()); - - SteamNetworkingUtils.DebugLevel = NetDebugOutput.Debug; - - SteamNetworkingSockets.OnConnectionStatusChanged += OnConnectionStatusChanged; - SteamNetworkingUtils.OnDebugOutput += OnSteamNetworkDebugOutput; - - SteamNetworkingUtils.InitRelayNetworkAccess(); - } - - public void Shutdown() { - Disconnect(); - - SteamNetworkingUtils.OnDebugOutput -= OnSteamNetworkDebugOutput; - SteamNetworkingSockets.OnConnectionStatusChanged -= OnConnectionStatusChanged; - - m_TickDisposable.Dispose(); - } - - public bool IsServer() { - return m_SocketManager != null; - } - - public bool IsClient() { - return m_ConnectionManager != null; - } - - public bool StartServer() { - Disconnect(); - - s_Logger.Info("Creating server..."); - - try { - m_HostSteamID = SteamNetworkingSockets.Identity.SteamId; - - m_SocketManager = SteamNetworkingSockets.CreateRelaySocket(k_DefaultPort); - m_ConnectionManager = SteamNetworkingSockets.ConnectRelay(m_HostSteamID, k_DefaultPort); - } catch (Exception e) { - s_Logger.Error($"Failed to create server: {e.Message}"); - m_SocketManager = null; - m_ConnectionManager = null; - - return false; - } - - return true; - } - - public void StopServer() { - Disconnect(); - } - - public bool Connect(ulong serverID) { - if (IsServer()) { - s_Logger.Error("Cannot connect to a server while running as a server."); - return false; - } - - Disconnect(); - - s_Logger.Info("Connecting to server with steam ID: " + serverID); - try { - m_ConnectionManager = SteamNetworkingSockets.ConnectRelay(serverID, k_DefaultPort); - m_HostSteamID = serverID; - } catch (Exception e) { - s_Logger.Error($"Failed to connect to server with ID {serverID}: {e.Message}"); - m_ConnectionManager = null; - - return false; - } - - return true; - } - - public void Disconnect() { - if (m_ConnectionManager != null) { - s_Logger.Info("Disconnecting from the server..."); - m_ConnectionManager.Close(); - m_ConnectionManager = null; - } - - if (m_SocketManager != null) { - s_Logger.Info("Shutting down the server..."); - m_SocketManager.Close(); - m_SocketManager = null; - } - } - - public void Send(ulong clientID, ArraySegment data, SendMode mode) { - if (clientID == 0) { - clientID = m_HostSteamID; - } - - } - - void OnSteamNetworkDebugOutput(NetDebugOutput level, string message) { - LogLevel logLevel = level switch { - NetDebugOutput.Debug => LogLevel.Debug, - NetDebugOutput.Msg => LogLevel.Info, - NetDebugOutput.Warning => LogLevel.Warning, - NetDebugOutput.Error => LogLevel.Error, - _ => LogLevel.Info - }; - - s_Logger.Log(logLevel, message); - } - - void Tick() { - m_SocketManager?.Receive(); - m_ConnectionManager?.Receive(); - } - - void OnConnectionStatusChanged(Connection connection, ConnectionInfo info) { - s_Logger.Info($"OnConnectionStatusChanged: {connection.Id} - {info.Identity} - Status: {info.State}"); - } - - class ServerCallbacks : SocketManager { - public override void OnConnecting(Connection connection, ConnectionInfo data) { - base.OnConnecting(connection, data); - connection.Accept(); - - s_Logger.Info($"OnConnecting: {connection.Id} - {data.Identity}"); - } - - public override void OnConnected(Connection connection, ConnectionInfo data) { - base.OnConnected(connection, data); - - s_Logger.Info($"OnConnected: {connection.Id} - {data.Identity}"); - - connection.SendMessage(new byte[] { - 0xBE, - 0xFE, - 0x00, - 0x00 - }, 0, 4, 0); - } - - public override void OnDisconnected(Connection connection, ConnectionInfo data) { - base.OnDisconnected(connection, data); - - s_Logger.Info($"OnDisconnected: {connection.Id} - {data.Identity}"); - } - - public override void OnMessage(Connection connection, - NetIdentity identity, - IntPtr data, - int size, - long messageNum, - long recvTime, - int channel) { - base.OnMessage(connection, identity, data, size, messageNum, recvTime, channel); - - byte[] buffer = new byte[size]; - System.Runtime.InteropServices.Marshal.Copy(data, buffer, 0, size); - RR.OnServerDataReceived(buffer); - - s_Logger.Info($"OnMessage: {connection.Id} - {identity} - Size: {size} - Channel: {channel}"); - } - } - - class ClientCallbacks : ConnectionManager { - public override void OnConnected(ConnectionInfo info) { - base.OnConnected(info); - s_Logger.Info("ConnectionOnConnected"); - } - - public override void OnConnecting(ConnectionInfo info) { - base.OnConnecting(info); - s_Logger.Info("ConnectionOnConnecting"); - } - - public override void OnDisconnected(ConnectionInfo info) { - base.OnDisconnected(info); - s_Logger.Info("ConnectionOnDisconnected"); - } - - public override void OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel) { - byte[] buffer = new byte[size]; - System.Runtime.InteropServices.Marshal.Copy(data, buffer, 0, size); - RR.OnClientDataReceived(buffer); - - s_Logger.Info($"OnMessage: Size: {size} - Channel: {channel}"); - } - } - } -} \ No newline at end of file diff --git a/Runtime/Engine/RebootKit.Engine.asmdef b/Runtime/Engine/RebootKit.Engine.asmdef index d3a9a5b..9b62f61 100755 --- a/Runtime/Engine/RebootKit.Engine.asmdef +++ b/Runtime/Engine/RebootKit.Engine.asmdef @@ -13,7 +13,10 @@ "GUID:f51ebe6a0ceec4240a699833d6309b23", "GUID:593a5b492d29ac6448b1ebf7f035ef33", "GUID:84651a3751eca9349aac36a66bba901b", - "GUID:d8b63aba1907145bea998dd612889d6b" + "GUID:d8b63aba1907145bea998dd612889d6b", + "GUID:f2d49d9fa7e7eb3418e39723a7d3b92f", + "GUID:324caed91501a9c47a04ebfd87b68794", + "GUID:1491147abca9d7d4bb7105af628b223e" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Runtime/Engine/core_assets/boot/scn_boot.unity b/Runtime/Engine/core_assets/boot/scn_boot.unity index dc14cec..c809c7d 100755 --- a/Runtime/Engine/core_assets/boot/scn_boot.unity +++ b/Runtime/Engine/core_assets/boot/scn_boot.unity @@ -358,6 +358,110 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!1 &1456272197 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1456272200} + - component: {fileID: 1456272199} + - component: {fileID: 1456272198} + m_Layer: 0 + m_Name: network + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1456272198 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1456272197} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ProtocolType: 0 + m_UseWebSockets: 0 + m_UseEncryption: 0 + m_MaxPacketQueueSize: 128 + m_MaxPayloadSize: 6144 + m_HeartbeatTimeoutMS: 500 + m_ConnectTimeoutMS: 1000 + m_MaxConnectAttempts: 60 + m_DisconnectTimeoutMS: 30000 + ConnectionData: + Address: 127.0.0.1 + Port: 7777 + ServerListenAddress: 127.0.0.1 + DebugSimulator: + PacketDelayMS: 0 + PacketJitterMS: 0 + PacketDropRate: 0 +--- !u!114 &1456272199 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1456272197} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkManagerExpanded: 0 + NetworkConfig: + ProtocolVersion: 0 + NetworkTransport: {fileID: 1456272198} + PlayerPrefab: {fileID: 0} + Prefabs: + NetworkPrefabsLists: + - {fileID: 11400000, guid: d30094ba881a39a4197e324b492d5db7, type: 2} + TickRate: 30 + ClientConnectionBufferTimeout: 10 + ConnectionApproval: 0 + ConnectionData: + EnableTimeResync: 0 + TimeResyncInterval: 30 + EnsureNetworkVariableLengthSafety: 0 + EnableSceneManagement: 0 + ForceSamePrefabs: 1 + RecycleNetworkIds: 1 + NetworkIdRecycleDelay: 120 + RpcHashSize: 0 + LoadSceneTimeOut: 120 + SpawnTimeout: 10 + EnableNetworkLogs: 1 + NetworkTopology: 0 + UseCMBService: 0 + AutoSpawnPlayerPrefabClientSide: 1 + NetworkProfilingMetrics: 1 + OldPrefabList: [] + RunInBackground: 1 + LogLevel: 1 +--- !u!4 &1456272200 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1456272197} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1509941943 GameObject: m_ObjectHideFlags: 0 @@ -519,3 +623,4 @@ SceneRoots: - {fileID: 242831139} - {fileID: 319523215} - {fileID: 1530691767} + - {fileID: 1456272200}