game version overlay, working on actors sync

This commit is contained in:
2025-07-16 23:03:48 +02:00
parent 0da6f275c0
commit 4ec3dedd42
31 changed files with 1826 additions and 680 deletions

View File

@@ -1,6 +1,68 @@
using Unity.Netcode;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Simulation;
using Unity.Netcode;
namespace RebootKit.Engine.Main {
public abstract class NetworkPlayerController : NetworkBehaviour {
static readonly Logger s_Logger = new Logger(nameof(NetworkPlayerController));
public Actor PossessedActor { get; private set; }
public void PossessActor(Actor actor) {
if (!IsServer) {
s_Logger.Error("PossessActor can only be called on the server.");
return;
}
if (actor == null) {
s_Logger.Error("Cannot possess a null actor.");
return;
}
PossessActorRpc(actor.ActorID, RpcTarget.Everyone);
}
[Rpc(SendTo.SpecifiedInParams)]
void PossessActorRpc(ulong actorID, RpcParams rpcParams) {
Actor actor = RR.FindSpawnedActor(actorID);
if (actor == null) {
s_Logger.Error($"Actor with ID {actorID} not found.");
return;
}
if (PossessedActor is not null) {
OnUnpossessActor(PossessedActor);
}
PossessedActor = actor;
OnPossessActor(actor);
}
public void UnPossessActor() {
if (!IsServer) {
s_Logger.Error("UnPossessActor can only be called on the server.");
return;
}
if (PossessedActor == null) {
return;
}
UnPossessActorRpc(RpcTarget.Everyone);
}
[Rpc(SendTo.SpecifiedInParams)]
void UnPossessActorRpc(RpcParams rpcParams) {
if (PossessedActor is not null) {
OnUnpossessActor(PossessedActor);
PossessedActor = null;
}
}
protected virtual void OnPossessActor(Actor actor) {
}
protected virtual void OnUnpossessActor(Actor actor) {
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Simulation;
@@ -24,7 +23,7 @@ namespace RebootKit.Engine.Main {
[field: SerializeField] public ActorsManager Actors { get; private set; }
readonly Dictionary<ulong, NetworkClientState> m_Clients = new Dictionary<ulong, NetworkClientState>();
internal readonly Dictionary<ulong, NetworkClientState> Clients = new Dictionary<ulong, NetworkClientState>();
FixedString512Bytes m_WorldID = new FixedString512Bytes("");
bool m_IsChangingWorld = false;
@@ -61,7 +60,7 @@ namespace RebootKit.Engine.Main {
IsReadyForActorsSync = false,
IsReady = false
};
m_Clients.Add(clientID, newClientState);
Clients.Add(clientID, newClientState);
if (!m_WorldID.IsEmpty) {
s_Logger.Info($"Synchronizing world load for client {clientID} with world ID '{m_WorldID}'");
@@ -75,11 +74,11 @@ namespace RebootKit.Engine.Main {
}
s_Logger.Info($"OnClientDisconnect: {clientID}");
m_Clients.Remove(clientID);
Clients.Remove(clientID);
}
internal NetworkClientState GetClientState(ulong clientID) {
if (m_Clients.TryGetValue(clientID, out NetworkClientState clientState)) {
if (Clients.TryGetValue(clientID, out NetworkClientState clientState)) {
return clientState;
}
@@ -106,7 +105,7 @@ namespace RebootKit.Engine.Main {
m_WorldID = worldID;
foreach (KeyValuePair<ulong, NetworkClientState> kv in m_Clients) {
foreach (KeyValuePair<ulong, NetworkClientState> kv in Clients) {
kv.Value.IsWorldLoaded = false;
kv.Value.AreActorsSynced = false;
kv.Value.IsReadyForActorsSync = false;
@@ -176,7 +175,7 @@ namespace RebootKit.Engine.Main {
return;
}
if (m_Clients.TryGetValue(clientID, out NetworkClientState clientState)) {
if (Clients.TryGetValue(clientID, out NetworkClientState clientState)) {
clientState.IsWorldLoaded = true;
clientState.IsReadyForActorsSync = false;
Actors.SynchronizeActorsForClient(clientID);

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
@@ -18,6 +19,8 @@ using Assert = UnityEngine.Assertions.Assert;
using Logger = RebootKit.Engine.Foundation.Logger;
using Object = UnityEngine.Object;
[assembly: InternalsVisibleTo("RebootKit.Editor")]
namespace RebootKit.Engine.Main {
public static class RR {
static readonly Logger s_Logger = new Logger("RR");
@@ -61,7 +64,7 @@ namespace RebootKit.Engine.Main {
s_Logger.Info("Initializing");
s_servicesBag = new DisposableBag();
s_disposableBag = new DisposableBag();
s_Logger.Info("Registering core services");
Console = CreateService<ConsoleService>();
Input = new InputService(EngineConfig.inputConfig);
@@ -70,7 +73,9 @@ namespace RebootKit.Engine.Main {
await InitializeAssetsAsync(cancellationToken);
// await SteamManager.InitializeAsync(cancellationToken);
#if RR_STEAM
await SteamManager.InitializeAsync(cancellationToken);
#endif
}
// @NOTE: This method is called after the main scene is loaded.
@@ -82,6 +87,14 @@ namespace RebootKit.Engine.Main {
NetworkManager.Singleton.OnServerStarted += OnServerStarted;
NetworkManager.Singleton.OnServerStopped += OnServerStopped;
#if RR_STEAM
if (NetworkManager.Singleton.TryGetComponent(out FacepunchTransport facepunchTransport)) {
NetworkManager.Singleton.NetworkConfig.NetworkTransport = facepunchTransport;
} else {
s_Logger.Error("Steam integration is enabled but FacepunchTransport is not found in NetworkManager.");
}
#endif
Observable.EveryUpdate()
.Subscribe(_ => Tick())
.AddTo(ref s_disposableBag);
@@ -116,7 +129,9 @@ namespace RebootKit.Engine.Main {
NetworkManager.Singleton.OnServerStopped -= OnServerStopped;
}
// SteamManager.Shutdown();
#if RR_STEAM
SteamManager.Shutdown();
#endif
s_servicesBag.Dispose();
s_disposableBag.Dispose();
@@ -195,28 +210,28 @@ namespace RebootKit.Engine.Main {
NetworkSystemInstance.SetCurrentWorld(worldID);
}
public static void SpawnActor(AssetReferenceGameObject assetReference,
Vector3 position,
Quaternion rotation) {
public static Actor SpawnActor(AssetReferenceGameObject assetReference,
Vector3 position,
Quaternion rotation) {
if (!IsServer()) {
s_Logger.Error("Cannot spawn actor. Not a server instance.");
return;
return null;
}
if (NetworkSystemInstance is null) {
s_Logger.Error("NetworkSystemInstance is not initialized. Cannot spawn actor.");
return;
return null;
}
if (!assetReference.RuntimeKeyIsValid()) {
s_Logger.Error("Asset reference is not valid. Cannot spawn actor.");
return;
return null;
}
s_Logger.Info($"Spawning actor from asset reference: {assetReference.RuntimeKey}");
NetworkSystemInstance.Actors.SpawnActor(assetReference, position, rotation);
return NetworkSystemInstance.Actors.SpawnActor(assetReference, position, rotation);
}
public static Actor FindSpawnedActor(ulong actorID) {
if (NetworkSystemInstance is null) {
s_Logger.Error("NetworkSystemInstance is not initialized. Cannot find actor.");
@@ -334,6 +349,8 @@ namespace RebootKit.Engine.Main {
}
public static void ConnectWithSteamID(ulong steamId) {
#if RR_STEAM
if (NetworkManager.Singleton.IsClient) {
s_Logger.Error("Already connected to a server");
return;
@@ -346,6 +363,9 @@ namespace RebootKit.Engine.Main {
} else {
s_Logger.Error("Network transport is not FacepunchTransport. Cannot connect with Steam ID.");
}
#else
s_Logger.Error("Steam integration is not enabled. Cannot connect with Steam ID.");
#endif
}
public static void Disconnect() { }
@@ -395,7 +415,7 @@ namespace RebootKit.Engine.Main {
GameInstance = Object.Instantiate(EngineConfig.gamePrefab);
GameInstance.NetworkObject.Spawn();
NetworkSystemInstance = Object.Instantiate(s_networkSystemPrefab);
NetworkSystemInstance.NetworkObject.Spawn();
}
@@ -407,7 +427,7 @@ namespace RebootKit.Engine.Main {
GameInstance.NetworkObject.Despawn();
GameInstance = null;
}
if (NetworkSystemInstance is not null) {
if (NetworkSystemInstance.NetworkObject is not null && NetworkSystemInstance.NetworkObject.IsSpawned) {
NetworkSystemInstance.NetworkObject.Despawn();