adding multiplayer
This commit is contained in:
@@ -46,6 +46,7 @@ namespace RebootKit.Engine.Services.Console {
|
|||||||
string logFilePath = Application.persistentDataPath + "/rr_logs.txt";
|
string logFilePath = Application.persistentDataPath + "/rr_logs.txt";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
try {
|
||||||
m_LogFileStream = new FileStream(logFilePath, FileMode.Append, FileAccess.Write);
|
m_LogFileStream = new FileStream(logFilePath, FileMode.Append, FileAccess.Write);
|
||||||
m_LogFileWriter = new StreamWriter(m_LogFileStream);
|
m_LogFileWriter = new StreamWriter(m_LogFileStream);
|
||||||
|
|
||||||
@@ -57,6 +58,10 @@ namespace RebootKit.Engine.Services.Console {
|
|||||||
m_LogFileWriter.WriteLine("============================");
|
m_LogFileWriter.WriteLine("============================");
|
||||||
m_LogFileWriter.Flush();
|
m_LogFileWriter.Flush();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_logger.Error($"Failed to open log file at {logFilePath}: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
s_logger.Info("Waking up");
|
s_logger.Info("Waking up");
|
||||||
|
|
||||||
Load();
|
Load();
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using RebootKit.Engine.Foundation;
|
using RebootKit.Engine.Foundation;
|
||||||
using RebootKit.Engine.Main;
|
using RebootKit.Engine.Main;
|
||||||
|
using RebootKit.Engine.Services.ConsoleUI;
|
||||||
using RebootKit.Engine.Services.Input;
|
using RebootKit.Engine.Services.Input;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||||
|
|
||||||
namespace RebootKit.Engine.Services.ConsoleUI {
|
namespace RebootKit.Engine.ConsoleUI {
|
||||||
public class ConsoleUIService : ServiceMonoBehaviour {
|
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] ConsoleVC m_ConsoleVC;
|
||||||
[SerializeField] ScriptableInputAction m_ToggleAction;
|
[SerializeField] ScriptableInputAction m_ToggleAction;
|
||||||
@@ -22,7 +23,7 @@ namespace RebootKit.Engine.Services.ConsoleUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnEnable() {
|
void OnEnable() {
|
||||||
s_logger.Info("OnEnable console");
|
s_Logger.Info("OnEnable console");
|
||||||
|
|
||||||
m_ToggleAction.Action.Enable();
|
m_ToggleAction.Action.Enable();
|
||||||
m_ToggleAction.Action.performed += OnToggleAction;
|
m_ToggleAction.Action.performed += OnToggleAction;
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3d31f3fe40394defabe1b77f6ddf8861
|
|
||||||
timeCreated: 1746665551
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using RebootKit.Engine.Foundation;
|
|
||||||
|
|
||||||
namespace RebootKit.Engine.Services.Crosshair {
|
|
||||||
public class CrosshairService : IService {
|
|
||||||
|
|
||||||
public CrosshairService() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f23a52c23aae46e8bd6b77222b10e5e2
|
|
||||||
timeCreated: 1746665562
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using RebootKit.Engine.Main;
|
||||||
using RebootKit.Engine.Main;
|
|
||||||
using RebootKit.Engine.UI;
|
using RebootKit.Engine.UI;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
@@ -11,7 +10,6 @@ namespace RebootKit.Engine.Services.Development {
|
|||||||
VisualElement m_RootElement;
|
VisualElement m_RootElement;
|
||||||
|
|
||||||
Label m_FPSLabel;
|
Label m_FPSLabel;
|
||||||
Label m_GameModeLabel;
|
|
||||||
|
|
||||||
void Update() {
|
void Update() {
|
||||||
if (m_RootElement == null) {
|
if (m_RootElement == null) {
|
||||||
@@ -20,9 +18,6 @@ namespace RebootKit.Engine.Services.Development {
|
|||||||
|
|
||||||
Resolution resolution = Screen.currentResolution;
|
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_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() {
|
public override VisualElement Build() {
|
||||||
@@ -31,7 +26,6 @@ namespace RebootKit.Engine.Services.Development {
|
|||||||
CreateLabel($"Toggle Overlay [F3] | RebootKit | game: {Application.productName}, version: {Application.version}");
|
CreateLabel($"Toggle Overlay [F3] | RebootKit | game: {Application.productName}, version: {Application.version}");
|
||||||
m_FPSLabel = CreateLabel($"FPS: {Application.targetFrameRate}");
|
m_FPSLabel = CreateLabel($"FPS: {Application.targetFrameRate}");
|
||||||
|
|
||||||
m_GameModeLabel = CreateLabel("Game Mode: none");
|
|
||||||
return m_RootElement;
|
return m_RootElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
using RebootKit.Engine.Main;
|
using RebootKit.Engine.Main;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.AddressableAssets;
|
||||||
|
|
||||||
namespace RebootKit.Engine {
|
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 {
|
public class EngineConfigAsset : ScriptableObject {
|
||||||
|
[Header("Core")]
|
||||||
public bool initializeOnLoad = true;
|
public bool initializeOnLoad = true;
|
||||||
|
|
||||||
public EngineCoreServicesAsset coreServices;
|
public EngineCoreServicesAsset coreServices;
|
||||||
|
|
||||||
public GameAsset gameAsset;
|
[Header("Game")]
|
||||||
|
public Game gamePrefab;
|
||||||
|
public AssetReference mainMenuScene;
|
||||||
|
|
||||||
// @NOTE: Spacewar, change as needed
|
// @NOTE: Spacewar, change as needed
|
||||||
|
[Header("Steam")]
|
||||||
public uint steamAppID = 480;
|
public uint steamAppID = 480;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using RebootKit.Engine.Services.Console;
|
using RebootKit.Engine.Services.Console;
|
||||||
using RebootKit.Engine.Services.Input;
|
using RebootKit.Engine.Services.Input;
|
||||||
using RebootKit.Engine.Services.Simulation;
|
using RebootKit.Engine.Services.Simulation;
|
||||||
|
using RebootKit.Engine.Simulation;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RebootKit.Engine {
|
namespace RebootKit.Engine {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1177ea3903bdba5419ad3347250cb3b7
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -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<IGameMode> m_ActiveGameMode = new ReactiveProperty<IGameMode>(null);
|
|
||||||
|
|
||||||
public ReadOnlyReactiveProperty<IGameMode> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 82082a4d082be274bbec52347d25b836
|
|
||||||
@@ -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<IGameMode> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 562c4ff92afe4949b468003a0e997522
|
|
||||||
timeCreated: 1743456239
|
|
||||||
@@ -35,8 +35,12 @@ namespace RebootKit.Engine.Main {
|
|||||||
static async UniTask RunAsync(CancellationToken cancellationToken) {
|
static async UniTask RunAsync(CancellationToken cancellationToken) {
|
||||||
ConfigVarsContainer.Init();
|
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");
|
s_logger.Info("Loading boot scene");
|
||||||
SceneManager.LoadScene(RConsts.k_BootSceneBuildIndex, LoadSceneMode.Single);
|
SceneManager.LoadScene(RConsts.k_BootSceneBuildIndex, LoadSceneMode.Single);
|
||||||
|
#endif
|
||||||
|
|
||||||
s_logger.Info("Loading engine config");
|
s_logger.Info("Loading engine config");
|
||||||
EngineConfigAsset configAsset = Resources.Load<EngineConfigAsset>(RConsts.k_EngineConfigResourcesPath);
|
EngineConfigAsset configAsset = Resources.Load<EngineConfigAsset>(RConsts.k_EngineConfigResourcesPath);
|
||||||
@@ -53,10 +57,11 @@ namespace RebootKit.Engine.Main {
|
|||||||
await RR.InitAsync(configAsset, cancellationToken);
|
await RR.InitAsync(configAsset, cancellationToken);
|
||||||
|
|
||||||
s_logger.Info("Loading main scene");
|
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");
|
s_logger.Info("Starting RR");
|
||||||
RR.Run();
|
await RR.RunAsync(cancellationToken);
|
||||||
|
|
||||||
await UniTask.WaitUntilCanceled(Application.exitCancellationToken);
|
await UniTask.WaitUntilCanceled(Application.exitCancellationToken);
|
||||||
|
|
||||||
|
|||||||
78
Runtime/Engine/Code/Main/Game.cs
Normal file
78
Runtime/Engine/Code/Main/Game.cs
Normal file
@@ -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<FixedString128Bytes> m_CurrentWorldID =
|
||||||
|
new NetworkVariable<FixedString128Bytes>(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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Runtime/Engine/Code/Main/Game.cs.meta
Normal file
3
Runtime/Engine/Code/Main/Game.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3c1089fa01934ad69bf229123b190c07
|
||||||
|
timeCreated: 1750977602
|
||||||
6
Runtime/Engine/Code/Main/NetworkPlayerController.cs
Normal file
6
Runtime/Engine/Code/Main/NetworkPlayerController.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
using Unity.Netcode;
|
||||||
|
|
||||||
|
namespace RebootKit.Engine.Main {
|
||||||
|
public abstract class NetworkPlayerController : NetworkBehaviour {
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Runtime/Engine/Code/Main/NetworkPlayerController.cs.meta
Normal file
3
Runtime/Engine/Code/Main/NetworkPlayerController.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2010b0aabd68415bb4aea2846d2c59b1
|
||||||
|
timeCreated: 1751208839
|
||||||
@@ -4,52 +4,54 @@ using System.Threading;
|
|||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
using R3;
|
using R3;
|
||||||
using RebootKit.Engine.Foundation;
|
using RebootKit.Engine.Foundation;
|
||||||
using RebootKit.Engine.Multiplayer;
|
|
||||||
using RebootKit.Engine.Services.Console;
|
using RebootKit.Engine.Services.Console;
|
||||||
using RebootKit.Engine.Services.GameMode;
|
|
||||||
using RebootKit.Engine.Services.Input;
|
using RebootKit.Engine.Services.Input;
|
||||||
using RebootKit.Engine.Services.Simulation;
|
using RebootKit.Engine.Services.Simulation;
|
||||||
using RebootKit.Engine.Steam;
|
using RebootKit.Engine.Simulation;
|
||||||
using Unity.Collections;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.AddressableAssets;
|
using UnityEngine.AddressableAssets;
|
||||||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||||
|
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
using Assert = UnityEngine.Assertions.Assert;
|
using Assert = UnityEngine.Assertions.Assert;
|
||||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace RebootKit.Engine.Main {
|
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 {
|
public static class RR {
|
||||||
static readonly Logger s_Logger = new Logger("RR");
|
static readonly Logger s_Logger = new Logger("RR");
|
||||||
|
|
||||||
[ConfigVar("con.write_log", 1, "Enables writing game log to console output")]
|
[ConfigVar("con.write_log", 1, "Enables writing game log to console output")]
|
||||||
static ConfigVar s_writeLogToConsole;
|
static ConfigVar s_writeLogToConsole;
|
||||||
|
|
||||||
|
[ConfigVar("sv.tick_rate", 60, "Server tick rate in Hz")]
|
||||||
|
public static ConfigVar TickRate;
|
||||||
|
|
||||||
internal static EngineConfigAsset EngineConfig;
|
internal static EngineConfigAsset EngineConfig;
|
||||||
|
|
||||||
static DisposableBag s_disposableBag;
|
static DisposableBag s_disposableBag;
|
||||||
static DisposableBag s_servicesBag;
|
static DisposableBag s_servicesBag;
|
||||||
|
|
||||||
public static ConsoleService Console { get; private set; }
|
static AsyncOperationHandle<SceneInstance> s_mainMenuSceneHandle;
|
||||||
|
|
||||||
|
internal static ConsoleService Console { get; private set; }
|
||||||
public static InputService Input { get; private set; }
|
public static InputService Input { get; private set; }
|
||||||
public static WorldService World { get; private set; }
|
public static WorldService World { get; private set; }
|
||||||
public static GameModesService GameModes { get; private set; }
|
|
||||||
|
|
||||||
public static Camera MainCamera { get; internal 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<ulong> 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) {
|
internal static async UniTask InitAsync(EngineConfigAsset configAsset, CancellationToken cancellationToken) {
|
||||||
Assert.IsNotNull(configAsset, "Config asset is required");
|
Assert.IsNotNull(configAsset, "Config asset is required");
|
||||||
Assert.IsNotNull(configAsset.gameAsset, "Game asset is required");
|
|
||||||
|
|
||||||
EngineConfig = configAsset;
|
EngineConfig = configAsset;
|
||||||
|
|
||||||
@@ -57,36 +59,31 @@ namespace RebootKit.Engine.Main {
|
|||||||
s_servicesBag = new DisposableBag();
|
s_servicesBag = new DisposableBag();
|
||||||
s_disposableBag = new DisposableBag();
|
s_disposableBag = new DisposableBag();
|
||||||
|
|
||||||
s_Logger.Debug("Registering core services");
|
s_Logger.Info("Registering core services");
|
||||||
Console = CreateService(EngineConfig.coreServices.consoleService);
|
Console = CreateService(EngineConfig.coreServices.consoleService);
|
||||||
Input = CreateService(EngineConfig.coreServices.inputService);
|
Input = CreateService(EngineConfig.coreServices.inputService);
|
||||||
World = CreateService(EngineConfig.coreServices.worldService);
|
World = CreateService(EngineConfig.coreServices.worldService);
|
||||||
GameModes = CreateService<GameModesService>();
|
|
||||||
|
|
||||||
await InitializeAssetsAsync(cancellationToken);
|
await InitializeAssetsAsync(cancellationToken);
|
||||||
|
|
||||||
await SteamManager.InitializeAsync(cancellationToken);
|
// await SteamManager.InitializeAsync(cancellationToken);
|
||||||
|
|
||||||
if (SteamManager.IsInitialized) {
|
// if (SteamManager.IsInitialized) {
|
||||||
s_networkTransport = SteamManager.NetworkTransport;
|
// s_networkTransport = SteamManager.NetworkTransport;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
s_Logger.Debug("Creating game");
|
// @NOTE: This method is called after the main scene is loaded.
|
||||||
s_game = EngineConfig.gameAsset.CreateGame();
|
internal static async UniTask RunAsync(CancellationToken cancellationToken) {
|
||||||
|
NetworkManager.Singleton.OnConnectionEvent += OnConnectionEvent;
|
||||||
|
NetworkManager.Singleton.OnServerStarted += OnServerStarted;
|
||||||
|
NetworkManager.Singleton.OnServerStopped += OnServerStopped;
|
||||||
|
|
||||||
await s_game.InitAsync(cancellationToken);
|
Observable.EveryUpdate()
|
||||||
}
|
.Subscribe(_ => Tick())
|
||||||
|
.AddTo(ref s_disposableBag);
|
||||||
|
|
||||||
internal static void Shutdown() {
|
await OpenMainMenuAsync(cancellationToken);
|
||||||
SteamManager.Shutdown();
|
|
||||||
|
|
||||||
s_Logger.Info("Shutting down");
|
|
||||||
s_servicesBag.Dispose();
|
|
||||||
s_disposableBag.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Run() {
|
|
||||||
s_game.Run();
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
string scriptContent = UnityEditor.EditorPrefs.GetString("RebootKitEditor.OnGameRunScriptContent", "");
|
string scriptContent = UnityEditor.EditorPrefs.GetString("RebootKitEditor.OnGameRunScriptContent", "");
|
||||||
@@ -101,36 +98,40 @@ namespace RebootKit.Engine.Main {
|
|||||||
#endif
|
#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
|
// Assets API
|
||||||
static readonly List<GameModeAsset> s_GameModesAssets = new List<GameModeAsset>();
|
|
||||||
static readonly List<WorldConfigAsset> s_WorldConfigsAssets = new List<WorldConfigAsset>();
|
static readonly List<WorldConfigAsset> s_WorldConfigsAssets = new List<WorldConfigAsset>();
|
||||||
|
|
||||||
public static IReadOnlyList<GameModeAsset> GameModesAssets => s_GameModesAssets;
|
|
||||||
public static IReadOnlyList<WorldConfigAsset> WorldConfigsAssets => s_WorldConfigsAssets;
|
public static IReadOnlyList<WorldConfigAsset> WorldConfigsAssets => s_WorldConfigsAssets;
|
||||||
|
|
||||||
public static async UniTask InitializeAssetsAsync(CancellationToken cancellationToken) {
|
static async UniTask InitializeAssetsAsync(CancellationToken cancellationToken) {
|
||||||
s_GameModesAssets.Clear();
|
|
||||||
s_WorldConfigsAssets.Clear();
|
s_WorldConfigsAssets.Clear();
|
||||||
|
|
||||||
s_Logger.Info("Loading game assets");
|
s_Logger.Info("Loading game assets");
|
||||||
|
|
||||||
await Addressables.LoadAssetsAsync<GameModeAsset>("game_mode", asset => { s_GameModesAssets.Add(asset); }).ToUniTask(cancellationToken: cancellationToken);
|
await Addressables.LoadAssetsAsync<WorldConfigAsset>("world", asset => { s_WorldConfigsAssets.Add(asset); })
|
||||||
s_Logger.Info($"Loaded {s_GameModesAssets.Count} game modes");
|
.ToUniTask(cancellationToken: cancellationToken);
|
||||||
|
|
||||||
await Addressables.LoadAssetsAsync<WorldConfigAsset>("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) {
|
public static WorldConfigAsset GetWorldConfigAsset(string name) {
|
||||||
@@ -138,7 +139,8 @@ namespace RebootKit.Engine.Main {
|
|||||||
throw new ArgumentException("World config name cannot be null or empty", nameof(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) {
|
if (!worldConfig) {
|
||||||
throw new KeyNotFoundException($"World config '{name}' not found");
|
throw new KeyNotFoundException($"World config '{name}' not found");
|
||||||
}
|
}
|
||||||
@@ -147,22 +149,48 @@ namespace RebootKit.Engine.Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Game API
|
// Game API
|
||||||
public static void StartGameMode(GameModeAsset gameMode, WorldConfig world) {
|
public static async UniTask OpenMainMenuAsync(CancellationToken cancellationToken) {
|
||||||
if (!IsClient() || !IsHost()) {
|
s_Logger.Info("Opening main menu");
|
||||||
s_Logger.Error("Cannot start game mode: you must be connected to a server and be the host");
|
|
||||||
|
World.Unload();
|
||||||
|
|
||||||
|
if (!EngineConfig.mainMenuScene.RuntimeKeyIsValid()) {
|
||||||
|
s_Logger.Error("Main menu scene is not set in EngineConfig");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_Logger.Info($"Starting game mode: {gameMode.name} in world: {world.name}");
|
s_mainMenuSceneHandle = Addressables.LoadSceneAsync(EngineConfig.mainMenuScene, LoadSceneMode.Additive);
|
||||||
GameModes.Start(gameMode, world);
|
await s_mainMenuSceneHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TGame Game<TGame>() where TGame : IGame {
|
internal static void CloseMainMenu() {
|
||||||
if (s_game is TGame game) {
|
if (!s_mainMenuSceneHandle.IsValid()) {
|
||||||
return game;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"Game is not of type {typeof(TGame)}");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Service API
|
||||||
@@ -182,10 +210,9 @@ namespace RebootKit.Engine.Main {
|
|||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
// General API
|
// Logging API
|
||||||
public static void Log(string message) {
|
public static void Log(string message) {
|
||||||
Debug.Log(message);
|
Debug.Log(message);
|
||||||
|
|
||||||
Console?.WriteToOutput(message);
|
Console?.WriteToOutput(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +226,10 @@ namespace RebootKit.Engine.Main {
|
|||||||
Console?.WriteToOutput(message);
|
Console?.WriteToOutput(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void WriteToConsole(string message) {
|
||||||
|
Console?.WriteToOutput(message);
|
||||||
|
}
|
||||||
|
|
||||||
// CVar API
|
// CVar API
|
||||||
public static ConfigVar CVarIndex(string name, int defaultValue = -1) {
|
public static ConfigVar CVarIndex(string name, int defaultValue = -1) {
|
||||||
ConfigVar cvar = ConfigVarsContainer.Get(name);
|
ConfigVar cvar = ConfigVarsContainer.Get(name);
|
||||||
@@ -234,46 +265,110 @@ namespace RebootKit.Engine.Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Network API
|
// Network API
|
||||||
static GameLobby s_gameLobby;
|
public static bool IsServer() {
|
||||||
static INetworkTransport s_networkTransport;
|
return NetworkManager.Singleton.IsServer;
|
||||||
|
|
||||||
public static bool IsHost() {
|
|
||||||
return s_networkTransport.IsServer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsClient() {
|
public static bool IsClient() {
|
||||||
return s_networkTransport.IsClient();
|
return NetworkManager.Singleton.IsClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetPing() {
|
public static void StartHost() {
|
||||||
return -1;
|
if (NetworkManager.Singleton.IsHost) {
|
||||||
|
s_Logger.Error("Already hosting a server");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void HostServer(bool offline = false) {
|
s_Logger.Info("Starting host");
|
||||||
s_networkTransport.StartServer();
|
NetworkManager.Singleton.StartHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConnectToLobby() {
|
public static void StopServer() {
|
||||||
s_networkTransport.Connect(Steamworks.SteamNetworkingSockets.Identity.SteamId);
|
}
|
||||||
|
|
||||||
|
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() {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void OnDisconnected() {
|
if (string.IsNullOrEmpty(message)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void OnServerDataReceived(byte[] data) {
|
GameInstance.SendChatMessageRpc(message);
|
||||||
s_Logger.Debug($"[SERVER] Data received: {data.Length} bytes");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void OnClientDataReceived(byte[] data) {
|
static float s_tickTimer;
|
||||||
s_Logger.Debug($"[CLIENT] Data received: {data.Length} bytes");
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <message>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string message = string.Join(" ", args, 1, args.Length - 1);
|
||||||
|
SendChatMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 83d5dc53310c40caab8b7732b9cecbdc
|
|
||||||
timeCreated: 1750615656
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace RebootKit.Engine.Multiplayer {
|
|
||||||
public class GameLobby {
|
|
||||||
public string GameModeID { get; private set; }
|
|
||||||
public string WorldID { get; private set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: cf39e9785e4e4ad0a7ebb8b19c882b2a
|
|
||||||
timeCreated: 1750628631
|
|
||||||
@@ -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<byte> data, SendMode mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 702059b29fda4edc80caf8b22cd8c0d7
|
|
||||||
timeCreated: 1750759626
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
namespace RebootKit.Engine.Multiplayer {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 34d66ac5d1c443e8992a84edd5eb796e
|
|
||||||
timeCreated: 1750628495
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
using RebootKit.Engine.Foundation;
|
using Unity.Collections;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RebootKit.Engine.Services.Simulation {
|
namespace RebootKit.Engine.Simulation {
|
||||||
public abstract class Actor : MonoBehaviour {
|
public abstract class Actor : MonoBehaviour {
|
||||||
[field: SerializeField]
|
|
||||||
public SerializableGuid ActorGuid { get; private set; } = SerializableGuid.New();
|
|
||||||
|
|
||||||
bool m_IsPlaying = false;
|
bool m_IsPlaying = false;
|
||||||
public bool IsPlaying {
|
public bool IsPlaying {
|
||||||
get {
|
get {
|
||||||
@@ -39,7 +36,13 @@ namespace RebootKit.Engine.Services.Simulation {
|
|||||||
public virtual void OnEndPlay() {
|
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
using R3;
|
|
||||||
using RebootKit.Engine.Foundation;
|
using RebootKit.Engine.Foundation;
|
||||||
|
using RebootKit.Engine.Main;
|
||||||
|
using RebootKit.Engine.Services.Simulation;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.AddressableAssets;
|
using UnityEngine.AddressableAssets;
|
||||||
using UnityEngine.Assertions;
|
using UnityEngine.Assertions;
|
||||||
@@ -12,7 +12,7 @@ using UnityEngine.ResourceManagement.ResourceProviders;
|
|||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||||
|
|
||||||
namespace RebootKit.Engine.Services.Simulation {
|
namespace RebootKit.Engine.Simulation {
|
||||||
public interface IWorldContext { }
|
public interface IWorldContext { }
|
||||||
|
|
||||||
public class WorldService : IService {
|
public class WorldService : IService {
|
||||||
@@ -30,26 +30,28 @@ namespace RebootKit.Engine.Services.Simulation {
|
|||||||
|
|
||||||
struct ActorData {
|
struct ActorData {
|
||||||
public Actor Actor;
|
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;
|
Actor = actor;
|
||||||
ManagedByAddressabled = managedByAddressabled;
|
ManagedByAddressables = managedByAddressables;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly List<ActorData> m_Actors = new List<ActorData>();
|
readonly List<ActorData> m_Actors = new List<ActorData>();
|
||||||
readonly IDisposable m_UpdateSubscription;
|
|
||||||
|
|
||||||
public IWorldContext Context { get; private set; }
|
public IWorldContext Context { get; private set; }
|
||||||
|
|
||||||
|
public string WorldID {
|
||||||
|
get {
|
||||||
|
return string.IsNullOrEmpty(m_Config.name) ? string.Empty : m_Config.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public WorldService() {
|
public WorldService() {
|
||||||
m_UpdateSubscription = Observable.EveryUpdate()
|
|
||||||
.Subscribe(_ => { Tick(Time.deltaTime); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
m_UpdateSubscription.Dispose();
|
|
||||||
Unload();
|
Unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,9 +82,21 @@ namespace RebootKit.Engine.Services.Simulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_WorldState = WorldState.Loaded;
|
m_WorldState = WorldState.Loaded;
|
||||||
|
|
||||||
|
if (RR.GameInstance is not null) {
|
||||||
|
RR.GameInstance.OnWorldLoaded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unload() {
|
public void Unload() {
|
||||||
|
if (m_WorldState == WorldState.Unloaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RR.GameInstance is not null) {
|
||||||
|
RR.GameInstance.OnWorldUnload();
|
||||||
|
}
|
||||||
|
|
||||||
KillAllActors();
|
KillAllActors();
|
||||||
|
|
||||||
if (m_SceneInstance.IsValid()) {
|
if (m_SceneInstance.IsValid()) {
|
||||||
@@ -134,7 +148,7 @@ namespace RebootKit.Engine.Services.Simulation {
|
|||||||
actor.IsPlaying = false;
|
actor.IsPlaying = false;
|
||||||
actor.OnDespawned();
|
actor.OnDespawned();
|
||||||
|
|
||||||
if (actorData.ManagedByAddressabled) {
|
if (actorData.ManagedByAddressables) {
|
||||||
Addressables.ReleaseInstance(actor.gameObject);
|
Addressables.ReleaseInstance(actor.gameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +158,7 @@ namespace RebootKit.Engine.Services.Simulation {
|
|||||||
actorData.Actor.IsPlaying = false;
|
actorData.Actor.IsPlaying = false;
|
||||||
actorData.Actor.OnDespawned();
|
actorData.Actor.OnDespawned();
|
||||||
|
|
||||||
if (actorData.ManagedByAddressabled) {
|
if (actorData.ManagedByAddressables) {
|
||||||
Addressables.ReleaseInstance(actorData.Actor.gameObject);
|
Addressables.ReleaseInstance(actorData.Actor.gameObject);
|
||||||
} else {
|
} else {
|
||||||
UnityEngine.Object.Destroy(actorData.Actor.gameObject);
|
UnityEngine.Object.Destroy(actorData.Actor.gameObject);
|
||||||
@@ -154,14 +168,16 @@ namespace RebootKit.Engine.Services.Simulation {
|
|||||||
m_Actors.Clear();
|
m_Actors.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick(float deltaTime) {
|
public void Tick(float deltaTime) {
|
||||||
if (m_WorldState != WorldState.Loaded) {
|
if (m_WorldState != WorldState.Loaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ActorData actorData in m_Actors) {
|
foreach (ActorData actorData in m_Actors) {
|
||||||
if (actorData.Actor.IsPlaying) {
|
Actor actor = actorData.Actor;
|
||||||
actorData.Actor.OnTick(deltaTime);
|
|
||||||
|
if (actor.IsPlaying) {
|
||||||
|
actor.Tick(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using RebootKit.Engine.Foundation;
|
using RebootKit.Engine.Foundation;
|
||||||
|
using RebootKit.Engine.Simulation;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RebootKit.Engine.Services.Simulation {
|
namespace RebootKit.Engine.Services.Simulation {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Threading;
|
|||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
using R3;
|
using R3;
|
||||||
using RebootKit.Engine.Main;
|
using RebootKit.Engine.Main;
|
||||||
using RebootKit.Engine.Multiplayer;
|
|
||||||
using Steamworks;
|
using Steamworks;
|
||||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||||
|
|
||||||
@@ -13,8 +12,6 @@ namespace RebootKit.Engine.Steam {
|
|||||||
|
|
||||||
public static bool IsInitialized { get; private set; } = false;
|
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) {
|
internal static async UniTask InitializeAsync(CancellationToken cancellationToken = default) {
|
||||||
s_Logger.Info("Initializing Steam Manager...");
|
s_Logger.Info("Initializing Steam Manager...");
|
||||||
IsInitialized = false;
|
IsInitialized = false;
|
||||||
@@ -26,8 +23,6 @@ namespace RebootKit.Engine.Steam {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTransport.Initialize();
|
|
||||||
|
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
|
|
||||||
await UniTask.Yield(cancellationToken);
|
await UniTask.Yield(cancellationToken);
|
||||||
@@ -41,7 +36,6 @@ namespace RebootKit.Engine.Steam {
|
|||||||
|
|
||||||
s_Logger.Info("Shutting down Steam Manager...");
|
s_Logger.Info("Shutting down Steam Manager...");
|
||||||
|
|
||||||
NetworkTransport.Shutdown();
|
|
||||||
SteamClient.Shutdown();
|
SteamClient.Shutdown();
|
||||||
|
|
||||||
IsInitialized = false;
|
IsInitialized = false;
|
||||||
|
|||||||
237
Runtime/Engine/Code/Steam/SteamNetworkManager.cs
Normal file
237
Runtime/Engine/Code/Steam/SteamNetworkManager.cs
Normal file
@@ -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<SocketManager>(k_DefaultPort);
|
||||||
|
// m_SocketManager.Interface = new ServerCallbacks();
|
||||||
|
// m_ConnectionManager =
|
||||||
|
// SteamNetworkingSockets.ConnectRelay<ConnectionManager>(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<ConnectionManager>(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}");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -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<ServerCallbacks>(k_DefaultPort);
|
|
||||||
m_ConnectionManager = SteamNetworkingSockets.ConnectRelay<ClientCallbacks>(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<ClientCallbacks>(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<byte> 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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,10 @@
|
|||||||
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
||||||
"GUID:593a5b492d29ac6448b1ebf7f035ef33",
|
"GUID:593a5b492d29ac6448b1ebf7f035ef33",
|
||||||
"GUID:84651a3751eca9349aac36a66bba901b",
|
"GUID:84651a3751eca9349aac36a66bba901b",
|
||||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||||
|
"GUID:f2d49d9fa7e7eb3418e39723a7d3b92f",
|
||||||
|
"GUID:324caed91501a9c47a04ebfd87b68794",
|
||||||
|
"GUID:1491147abca9d7d4bb7105af628b223e"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
|
|||||||
@@ -358,6 +358,110 @@ RectTransform:
|
|||||||
m_AnchoredPosition: {x: 0, y: 0}
|
m_AnchoredPosition: {x: 0, y: 0}
|
||||||
m_SizeDelta: {x: 0, y: 0}
|
m_SizeDelta: {x: 0, y: 0}
|
||||||
m_Pivot: {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
|
--- !u!1 &1509941943
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -519,3 +623,4 @@ SceneRoots:
|
|||||||
- {fileID: 242831139}
|
- {fileID: 242831139}
|
||||||
- {fileID: 319523215}
|
- {fileID: 319523215}
|
||||||
- {fileID: 1530691767}
|
- {fileID: 1530691767}
|
||||||
|
- {fileID: 1456272200}
|
||||||
|
|||||||
Reference in New Issue
Block a user