-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'SubnauticaNitrox:master' into master
- Loading branch information
Showing
8 changed files
with
145 additions
and
106 deletions.
There are no files selected for viewing
82 changes: 32 additions & 50 deletions
82
NitroxClient/Communication/Packets/Processors/SimulationOwnershipChangeProcessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,71 +1,53 @@ | ||
using NitroxClient.Communication.Abstract; | ||
using NitroxClient.Communication.Abstract; | ||
using NitroxClient.Communication.Packets.Processors.Abstract; | ||
using NitroxClient.GameLogic; | ||
using NitroxClient.MonoBehaviours; | ||
using NitroxModel.DataStructures; | ||
using NitroxModel.DataStructures.Util; | ||
using NitroxModel.Packets; | ||
using UnityEngine; | ||
|
||
namespace NitroxClient.Communication.Packets.Processors | ||
namespace NitroxClient.Communication.Packets.Processors; | ||
|
||
public class SimulationOwnershipChangeProcessor : ClientPacketProcessor<SimulationOwnershipChange> | ||
{ | ||
public class SimulationOwnershipChangeProcessor : ClientPacketProcessor<SimulationOwnershipChange> | ||
{ | ||
private readonly IMultiplayerSession multiplayerSession; | ||
private readonly SimulationOwnership simulationOwnershipManager; | ||
private readonly IMultiplayerSession multiplayerSession; | ||
private readonly SimulationOwnership simulationOwnershipManager; | ||
|
||
public SimulationOwnershipChangeProcessor(IMultiplayerSession multiplayerSession, SimulationOwnership simulationOwnershipManager) | ||
{ | ||
this.multiplayerSession = multiplayerSession; | ||
this.simulationOwnershipManager = simulationOwnershipManager; | ||
} | ||
public SimulationOwnershipChangeProcessor(IMultiplayerSession multiplayerSession, SimulationOwnership simulationOwnershipManager) | ||
{ | ||
this.multiplayerSession = multiplayerSession; | ||
this.simulationOwnershipManager = simulationOwnershipManager; | ||
} | ||
|
||
public override void Process(SimulationOwnershipChange simulationOwnershipChange) | ||
public override void Process(SimulationOwnershipChange simulationOwnershipChange) | ||
{ | ||
foreach (SimulatedEntity simulatedEntity in simulationOwnershipChange.Entities) | ||
{ | ||
foreach (SimulatedEntity simulatedEntity in simulationOwnershipChange.Entities) | ||
if (multiplayerSession.Reservation.PlayerId == simulatedEntity.PlayerId) | ||
{ | ||
if (multiplayerSession.Reservation.PlayerId == simulatedEntity.PlayerId) | ||
if (simulatedEntity.ChangesPosition) | ||
{ | ||
if (simulatedEntity.ChangesPosition) | ||
{ | ||
StartBroadcastingEntityPosition(simulatedEntity.Id); | ||
} | ||
|
||
simulationOwnershipManager.SimulateEntity(simulatedEntity.Id, SimulationLockType.TRANSIENT); | ||
EntityPositionBroadcaster.WatchEntity(simulatedEntity.Id); | ||
} | ||
else if (simulationOwnershipManager.HasAnyLockType(simulatedEntity.Id)) | ||
{ | ||
// The server has forcibly removed this lock from the client. This is generally fine for | ||
// transient locks because it is only broadcasting position. However, exclusive locks may | ||
// need additional cleanup (such as a person piloting a vehicle - they need to be kicked out) | ||
// We can later add a forcibly removed callback but as of right now we have no use-cases for | ||
// forcibly removing an exclusive lock. Just log it if it happens.... | ||
|
||
if (simulationOwnershipManager.HasExclusiveLock(simulatedEntity.Id)) | ||
{ | ||
Log.Warn($"The server has forcibly revoked an exlusive lock - this may cause undefined behaviour. GUID: {simulatedEntity.Id}"); | ||
} | ||
|
||
simulationOwnershipManager.StopSimulatingEntity(simulatedEntity.Id); | ||
EntityPositionBroadcaster.StopWatchingEntity(simulatedEntity.Id); | ||
} | ||
simulationOwnershipManager.SimulateEntity(simulatedEntity.Id, SimulationLockType.TRANSIENT); | ||
} | ||
} | ||
else if (simulationOwnershipManager.HasAnyLockType(simulatedEntity.Id)) | ||
{ | ||
// The server has forcibly removed this lock from the client. This is generally fine for | ||
// transient locks because it is only broadcasting position. However, exclusive locks may | ||
// need additional cleanup (such as a person piloting a vehicle - they need to be kicked out) | ||
// We can later add a forcibly removed callback but as of right now we have no use-cases for | ||
// forcibly removing an exclusive lock. Just log it if it happens.... | ||
|
||
private void StartBroadcastingEntityPosition(NitroxId id) | ||
{ | ||
Optional<GameObject> gameObject = NitroxEntity.GetObjectFrom(id); | ||
if (simulationOwnershipManager.HasExclusiveLock(simulatedEntity.Id)) | ||
{ | ||
Log.Warn($"The server has forcibly revoked an exlusive lock - this may cause undefined behaviour. GUID: {simulatedEntity.Id}"); | ||
} | ||
|
||
if (gameObject.HasValue) | ||
{ | ||
EntityPositionBroadcaster.WatchEntity(id, gameObject.Value); | ||
simulationOwnershipManager.StopSimulatingEntity(simulatedEntity.Id); | ||
EntityPositionBroadcaster.StopWatchingEntity(simulatedEntity.Id); | ||
} | ||
#if DEBUG && ENTITY_LOG | ||
else | ||
{ | ||
Log.Error($"Expected to simulate an unknown entity: {id}"); | ||
} | ||
#endif | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,61 @@ | ||
using System.Collections.Generic; | ||
using System.Collections.Generic; | ||
using NitroxClient.GameLogic; | ||
using NitroxModel.Core; | ||
using NitroxModel.DataStructures; | ||
using NitroxModel.DataStructures.Util; | ||
using UnityEngine; | ||
|
||
namespace NitroxClient.MonoBehaviours | ||
namespace NitroxClient.MonoBehaviours; | ||
|
||
public class EntityPositionBroadcaster : MonoBehaviour | ||
{ | ||
public class EntityPositionBroadcaster : MonoBehaviour | ||
{ | ||
public static readonly float BROADCAST_INTERVAL = 0.25f; | ||
public static readonly float BROADCAST_INTERVAL = 0.25f; | ||
|
||
private static Dictionary<NitroxId, GameObject> watchingEntitiesById = new Dictionary<NitroxId, GameObject>(); | ||
private Entities entityBroadcaster; | ||
private static HashSet<NitroxId> watchingEntityIds = new(); | ||
private Entities entityBroadcaster; | ||
|
||
private float time; | ||
private float time; | ||
|
||
public void Awake() | ||
{ | ||
entityBroadcaster = NitroxServiceLocator.LocateService<Entities>(); | ||
} | ||
public void Awake() | ||
{ | ||
entityBroadcaster = NitroxServiceLocator.LocateService<Entities>(); | ||
} | ||
|
||
public void Update() | ||
{ | ||
time += Time.deltaTime; | ||
|
||
public void Update() | ||
// Only do on a specific cadence to avoid hammering server | ||
if (time >= BROADCAST_INTERVAL) | ||
{ | ||
time += Time.deltaTime; | ||
time = 0; | ||
|
||
// Only do on a specific cadence to avoid hammering server | ||
if (time >= BROADCAST_INTERVAL) | ||
if (watchingEntityIds.Count > 0) | ||
{ | ||
time = 0; | ||
|
||
if (watchingEntitiesById.Count > 0) | ||
{ | ||
entityBroadcaster.BroadcastTransforms(watchingEntitiesById); | ||
} | ||
Dictionary<NitroxId, GameObject> gameObjectsById = NitroxEntity.GetObjectsFrom(watchingEntityIds); | ||
entityBroadcaster.BroadcastTransforms(gameObjectsById); | ||
} | ||
} | ||
} | ||
|
||
public static void WatchEntity(NitroxId id, GameObject gameObject) | ||
{ | ||
watchingEntitiesById[id] = gameObject; | ||
public static void WatchEntity(NitroxId id) | ||
{ | ||
watchingEntityIds.Add(id); | ||
|
||
RemotelyControlled remotelyControlled = gameObject.GetComponent<RemotelyControlled>(); | ||
Object.Destroy(remotelyControlled); | ||
} | ||
// The game object may not exist at this very moment (due to being spawned in async). This is OK as we will | ||
// automatically start sending updates when we finally get it in the world. This behavior will also allow us | ||
// to resync or respawn entities while still have broadcasting enabled without doing anything extra. | ||
Optional<GameObject> gameObject = NitroxEntity.GetObjectFrom(id); | ||
|
||
public static void StopWatchingEntity(NitroxId id) | ||
if (gameObject.HasValue) | ||
{ | ||
watchingEntitiesById.Remove(id); | ||
RemotelyControlled remotelyControlled = gameObject.Value.GetComponent<RemotelyControlled>(); | ||
Object.Destroy(remotelyControlled); | ||
} | ||
} | ||
|
||
public static void StopWatchingEntity(NitroxId id) | ||
{ | ||
watchingEntityIds.Remove(id); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 0 additions & 24 deletions
24
NitroxPatcher/Patches/Dynamic/EntityCell_QueueForSleep_Patch.cs
This file was deleted.
Oops, something went wrong.
54 changes: 54 additions & 0 deletions
54
NitroxPatcher/Patches/Dynamic/EntityCell_SleepAsync_Patch.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
using System.Reflection.Emit; | ||
using HarmonyLib; | ||
using NitroxClient.GameLogic; | ||
using NitroxModel.Helper; | ||
|
||
namespace NitroxPatcher.Patches.Dynamic; | ||
|
||
/// <summary> | ||
/// Entity cells will go sleep when the player gets out of range. This needs to be reported to the server so they can lose simulation locks. | ||
/// </summary> | ||
public class EntityCell_SleepAsync_Patch : NitroxPatch, IDynamicPatch | ||
{ | ||
public static readonly MethodInfo TARGET_METHOD_ORIGINAL = Reflect.Method((EntityCell t) => t.SleepAsync(default(ProtobufSerializer))); | ||
public static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(TARGET_METHOD_ORIGINAL); | ||
|
||
public static readonly OpCode INJECTION_OPCODE = OpCodes.Stfld; | ||
public static readonly object INJECTION_OPERAND = Reflect.Field((EntityCell entityCell) => entityCell.state); | ||
|
||
public static int INJECTION_POSITION = 2; | ||
|
||
public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions) | ||
{ | ||
int validSpotsSeen = 0; | ||
|
||
foreach (CodeInstruction instruction in instructions) | ||
{ | ||
yield return instruction; | ||
|
||
bool validInjectionInstruction = instruction.opcode.Equals(INJECTION_OPCODE) && instruction.operand.Equals(INJECTION_OPERAND); | ||
|
||
if (validInjectionInstruction && ++validSpotsSeen == INJECTION_POSITION) | ||
{ | ||
/* | ||
* Injects: Callback(this); | ||
*/ | ||
yield return TranspilerHelper.Ldloc<EntityCell>(original); | ||
yield return new CodeInstruction(OpCodes.Call, Reflect.Method(() => Callback(default(EntityCell)))); | ||
} | ||
} | ||
} | ||
|
||
public static void Callback(EntityCell entityCell) | ||
{ | ||
Resolve<Terrain>().CellUnloaded(entityCell.BatchId, entityCell.CellId, entityCell.Level); | ||
} | ||
|
||
public override void Patch(Harmony harmony) | ||
{ | ||
PatchTranspiler(harmony, TARGET_METHOD); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters