Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crops persistence #2137

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Nitrox.Test/Patcher/Patches/PatchesTranspilerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class PatchesTranspilerTest
[typeof(Flare_Update_Patch), 0],
[typeof(FootstepSounds_OnStep_Patch), 6],
[typeof(GameInput_Initialize_Patch), 5],
[typeof(GrowingPlant_SpawnGrownModelAsync_Patch), -1],
[typeof(Player_TriggerInfectionRevealAsync_Patch), 1],
[typeof(IngameMenu_OnSelect_Patch), -2],
[typeof(IngameMenu_QuitGameAsync_Patch), 2],
Expand All @@ -61,13 +62,15 @@ public class PatchesTranspilerTest
[typeof(MainGameController_StartGame_Patch), 1],
[typeof(MeleeAttack_CanDealDamageTo_Patch), 4],
[typeof(PDAScanner_Scan_Patch), 3],
[typeof(PickPrefab_AddToContainerAsync_Patch), 4],
[typeof(Player_OnKill_Patch), 0],
[typeof(Respawn_Start_Patch), 3],
[typeof(RocketConstructor_StartRocketConstruction_Patch), 3],
[typeof(SpawnConsoleCommand_SpawnAsync_Patch), 2],
[typeof(SpawnOnKill_OnKill_Patch), 3],
[typeof(SubConsoleCommand_OnConsoleCommand_sub_Patch), 0],
[typeof(SubRoot_OnPlayerEntered_Patch), 5],
[typeof(Trashcan_Update_Patch), 4],
[typeof(uGUI_PDA_Initialize_Patch), 2],
[typeof(uGUI_PDA_SetTabs_Patch), 3],
[typeof(uGUI_Pings_IsVisibleNow_Patch), 0],
Expand Down
8 changes: 7 additions & 1 deletion Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,13 @@ private static void EntityTest(Entity entity, Entity entityAfter)
Assert.AreEqual(metadata.Duration, metadataAfter.Duration);
break;
case PlantableMetadata metadata when entityAfter.Metadata is PlantableMetadata metadataAfter:
Assert.AreEqual(metadata.Progress, metadataAfter.Progress);
Assert.AreEqual(metadata.TimeStartGrowth, metadataAfter.TimeStartGrowth);
Assert.AreEqual(metadata.SlotID, metadataAfter.SlotID);
// FruitPlantMetadata field is not checked before it's only temporary
break;
case FruitPlantMetadata metadata when entityAfter.Metadata is FruitPlantMetadata metadataAfter:
Assert.AreEqual(metadata.PickedStates, metadataAfter.PickedStates);
Assert.AreEqual(metadata.TimeNextFruit, metadataAfter.TimeNextFruit);
break;
case CyclopsMetadata metadata when entityAfter.Metadata is CyclopsMetadata metadataAfter:
Assert.AreEqual(metadata.SilentRunningOn, metadataAfter.SilentRunningOn);
Expand Down
3 changes: 0 additions & 3 deletions NitroxClient/ClientAutoFacRegistrar.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
global using NitroxClient.Helpers;
global using NitroxModel.Logger;
global using static NitroxModel.Extensions;
using System.Reflection;
using Autofac;
using Autofac.Core;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public override void Process(EntityDestroyed packet)
}
else
{
DefaultDestroyAction(gameObject);
Entities.DestroyObject(gameObject);
}
}
}
Expand Down Expand Up @@ -92,9 +92,4 @@ private void DestroyVehicle(Vehicle vehicle)
Object.Destroy(vehicle.gameObject);
}
}

private void DefaultDestroyAction(GameObject gameObject)
{
Object.Destroy(gameObject);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using NitroxClient.Communication.Packets.Processors.Abstract;
using NitroxModel.Packets;

namespace NitroxClient.Communication.Packets.Processors;

public class FastCheatChangedProcessor : ClientPacketProcessor<FastCheatChanged>
{
public override void Process(FastCheatChanged packet)
{
switch (packet.Cheat)
{
case FastCheatChanged.FastCheat.HATCH:
NoCostConsoleCommand.main.fastHatchCheat = packet.Value;
break;

case FastCheatChanged.FastCheat.GROW:
NoCostConsoleCommand.main.fastGrowCheat = packet.Value;
break;
}
}
}
23 changes: 23 additions & 0 deletions NitroxClient/Debuggers/SceneExtraDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text.RegularExpressions;
using NitroxClient.MonoBehaviours;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures;
using NitroxModel.Helper;
using UnityEngine;
using Mathf = UnityEngine.Mathf;
Expand Down Expand Up @@ -251,6 +252,28 @@ private void GettingSearchbarResults()
GUILayout.Label($"There is no component named \"{gameObjectSearch.Substring(2)}\"", "error");
}
}
else if (gameObjectSearch.StartsWith("id:"))
{
string id = gameObjectSearch.Split(':')[1];
try
{
NitroxId foundId = new(id);
if (NitroxEntity.TryGetObjectFrom(foundId, out GameObject gameObject))
{
gameObjectResults = [gameObject];
}
else
{
GUILayout.Label($"No GameObject found with NitroxId \"{foundId}\"");
gameObjectResults = [];
}
}
catch
{
GUILayout.Label($"Id \"{id}\" is not a valid NitroxId");
gameObjectResults = [];
}
}
else
{
gameObjectResults = Resources.FindObjectsOfTypeAll<GameObject>().
Expand Down
23 changes: 21 additions & 2 deletions NitroxClient/GameLogic/Entities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public Entities(IPacketSender packetSender, ThrottledPacketSender throttledPacke
entitySpawnersByType[typeof(InstalledModuleEntity)] = new InstalledModuleEntitySpawner();
entitySpawnersByType[typeof(InstalledBatteryEntity)] = new InstalledBatteryEntitySpawner();
entitySpawnersByType[typeof(InventoryEntity)] = new InventoryEntitySpawner();
entitySpawnersByType[typeof(InventoryItemEntity)] = new InventoryItemEntitySpawner();
entitySpawnersByType[typeof(InventoryItemEntity)] = new InventoryItemEntitySpawner(entityMetadataManager);
entitySpawnersByType[typeof(WorldEntity)] = new WorldEntitySpawner(entityMetadataManager, playerManager, localPlayer, this, simulationOwnership);
entitySpawnersByType[typeof(PlaceholderGroupWorldEntity)] = entitySpawnersByType[typeof(WorldEntity)];
entitySpawnersByType[typeof(PrefabPlaceholderEntity)] = entitySpawnersByType[typeof(WorldEntity)];
Expand Down Expand Up @@ -256,11 +256,30 @@ public void CleanupExistingEntities(List<Entity> dirtyEntities)

if (gameObject.HasValue)
{
UnityEngine.Object.Destroy(gameObject.Value);
DestroyObject(gameObject.Value);
}
}
}

/// <summary>
/// Either perform a special operation (e.g. for plants) or a simple <see cref="UnityEngine.Object.Destroy"/>
/// </summary>
public static void DestroyObject(GameObject gameObject)
{
if (gameObject.TryGetComponent(out Plantable plantable))
{
plantable.FreeSpot();
return;
}
if (gameObject.TryGetComponent(out GrownPlant grownPlant))
{
grownPlant.seed.AliveOrNull()?.FreeSpot();
return;
}

UnityEngine.Object.Destroy(gameObject);
}

private void UpdateEntity(Entity entity)
{
if (!NitroxEntity.TryGetObjectFrom(entity.Id, out GameObject gameObject))
Expand Down
24 changes: 21 additions & 3 deletions NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Text;
using NitroxClient.GameLogic.InitialSync.Abstract;
using NitroxClient.MonoBehaviours;
using NitroxModel.DataStructures;
Expand Down Expand Up @@ -34,7 +35,7 @@ public PlayerInitialSyncProcessor(Items item, ItemContainers itemContainers, Loc
AddStep(sync => AddStartingItemsToPlayer(sync.FirstTimeConnecting));
AddStep(sync => SetPlayerStats(sync.PlayerStatsData));
AddStep(sync => SetPlayerGameMode(sync.GameMode));
AddStep(sync => SetPlayerKeepInventoryOnDeath(sync.KeepInventoryOnDeath));
AddStep(sync => ApplySettings(sync.KeepInventoryOnDeath, sync.SessionSettings.FastHatch, sync.SessionSettings.FastGrow));
}

private void SetPlayerPermissions(Perms permissions)
Expand Down Expand Up @@ -96,7 +97,7 @@ private IEnumerator AddStartingItemsToPlayer(bool firstTimeConnecting)
Pickupable pickupable = gameObject.GetComponent<Pickupable>();
pickupable.Initialize();

item.Created(gameObject);
item.PickedUp(gameObject, techType);
itemContainers.AddItem(gameObject, localPlayerId);
}
}
Expand Down Expand Up @@ -137,8 +138,25 @@ private static void SetPlayerGameMode(NitroxGameMode gameMode)
GameModeUtils.SetGameMode((GameModeOption)(int)gameMode, GameModeOption.None);
}

private void SetPlayerKeepInventoryOnDeath(bool keepInventoryOnDeath)
private void ApplySettings(bool keepInventoryOnDeath, bool fastHatch, bool fastGrow)
{
localPlayer.KeepInventoryOnDeath = keepInventoryOnDeath;
NoCostConsoleCommand.main.fastHatchCheat = fastHatch;
NoCostConsoleCommand.main.fastGrowCheat = fastGrow;
if (!fastHatch && !fastGrow)
{
return;
}

StringBuilder cheatsEnabled = new("Cheats enabled:");
if (fastHatch)
{
cheatsEnabled.Append(" fastHatch");
}
if (fastGrow)
{
cheatsEnabled.Append(" fastGrow");
}
Log.InGame(cheatsEnabled.ToString());
}
}
136 changes: 75 additions & 61 deletions NitroxClient/GameLogic/ItemContainers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,86 +13,100 @@
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;

namespace NitroxClient.GameLogic
namespace NitroxClient.GameLogic;

public class ItemContainers
{
public class ItemContainers
private readonly IPacketSender packetSender;
private readonly EntityMetadataManager entityMetadataManager;
private readonly Items items;

public ItemContainers(IPacketSender packetSender, EntityMetadataManager entityMetadataManager, Items items)
{
private readonly IPacketSender packetSender;
private readonly EntityMetadataManager entityMetadataManager;
this.packetSender = packetSender;
this.entityMetadataManager = entityMetadataManager;
this.items = items;
}

public ItemContainers(IPacketSender packetSender, EntityMetadataManager entityMetadataManager)
public void BroadcastItemAdd(Pickupable pickupable, Transform containerTransform, ItemsContainer container)
{
// We don't want to broadcast that event if it's from another player's inventory
if (containerTransform.GetComponentInParent<RemotePlayerIdentifier>(true))
{
this.packetSender = packetSender;
this.entityMetadataManager = entityMetadataManager;
return;
}

public void BroadcastItemAdd(Pickupable pickupable, Transform containerTransform)
if (!InventoryContainerHelper.TryGetOwnerId(containerTransform, out NitroxId ownerId))
{
// We don't want to broadcast that event if it's from another player's inventory
if (containerTransform.GetComponentInParent<RemotePlayerIdentifier>(true))
{
return;
}
// Error logging is done in the function
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Slight reword maybe, In the try function?

return;
}

if (!pickupable.TryGetIdOrWarn(out NitroxId itemId))
{
return;
}
// For planters, we'll always forcefully recreate the entity to ensure there's no desync
if (container.containerType == ItemsContainerType.LandPlants || container.containerType == ItemsContainerType.WaterPlants)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth us having Plants as a type then inherit? Then we can only have one check here and work for all plants

{
items.Planted(pickupable.gameObject, ownerId);
return;
}

if (!InventoryContainerHelper.TryGetOwnerId(containerTransform, out NitroxId ownerId))
{
// Error logging is done in the function
return;
}

if (packetSender.Send(new EntityReparented(itemId, ownerId)))
{
Log.Debug($"Sent: Added item ({itemId}) of type {pickupable.GetTechType()} to container {containerTransform.gameObject.GetFullHierarchyPath()}");
}
if (!pickupable.TryGetIdOrWarn(out NitroxId itemId))
{
return;
}

public void AddItem(GameObject item, NitroxId containerId)
// calls from Inventory.Pickup are managed by Items.PickedUp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V minor

Suggested change
// calls from Inventory.Pickup are managed by Items.PickedUp
// Calls from Inventory.Pickup are managed by Items.PickedUp

if (items.IsInventoryPickingUp)
{
return;
}

if (packetSender.Send(new EntityReparented(itemId, ownerId)))
{
Optional<GameObject> owner = NitroxEntity.GetObjectFrom(containerId);
if (!owner.HasValue)
{
Log.Error($"Unable to find inventory container with id {containerId} for {item.name}");
return;
}
Optional<ItemsContainer> opContainer = InventoryContainerHelper.TryGetContainerByOwner(owner.Value);
if (!opContainer.HasValue)
{
Log.Error($"Could not find container field on GameObject {owner.Value.GetFullHierarchyPath()}");
return;
}
Log.Debug($"Sent: Added item ({itemId}) of type {pickupable.GetTechType()} to container {containerTransform.gameObject.GetFullHierarchyPath()}");
}
}

ItemsContainer container = opContainer.Value;
Pickupable pickupable = item.RequireComponent<Pickupable>();
public void AddItem(GameObject item, NitroxId containerId)
{
Optional<GameObject> owner = NitroxEntity.GetObjectFrom(containerId);
if (!owner.HasValue)
{
Log.Error($"Unable to find inventory container with id {containerId} for {item.name}");
return;
}
Optional<ItemsContainer> opContainer = InventoryContainerHelper.TryGetContainerByOwner(owner.Value);
if (!opContainer.HasValue)
{
Log.Error($"Could not find container field on GameObject {owner.Value.GetFullHierarchyPath()}");
return;
}

ItemsContainer container = opContainer.Value;
Pickupable pickupable = item.RequireComponent<Pickupable>();

using (PacketSuppressor<EntityReparented>.Suppress())
{
container.UnsafeAdd(new InventoryItem(pickupable));
Log.Debug($"Received: Added item {pickupable.GetTechType()} to container {owner.Value.GetFullHierarchyPath()}");
}
using (PacketSuppressor<EntityReparented>.Suppress())
{
container.UnsafeAdd(new InventoryItem(pickupable));
Log.Debug($"Received: Added item {pickupable.GetTechType()} to container {owner.Value.GetFullHierarchyPath()}");
}
}

public void BroadcastBatteryAdd(GameObject gameObject, GameObject parent, TechType techType)
public void BroadcastBatteryAdd(GameObject gameObject, GameObject parent, TechType techType)
{
if (!gameObject.TryGetIdOrWarn(out NitroxId id))
{
return;
}
if (!parent.TryGetIdOrWarn(out NitroxId parentId))
{
if (!gameObject.TryGetIdOrWarn(out NitroxId id))
{
return;
}
if (!parent.TryGetIdOrWarn(out NitroxId parentId))
{
return;
}
return;
}

Optional<EntityMetadata> metadata = entityMetadataManager.Extract(gameObject);
Optional<EntityMetadata> metadata = entityMetadataManager.Extract(gameObject);

InstalledBatteryEntity installedBattery = new(id, techType.ToDto(), metadata.OrNull(), parentId, new());
InstalledBatteryEntity installedBattery = new(id, techType.ToDto(), metadata.OrNull(), parentId, new());

EntitySpawnedByClient spawnedPacket = new EntitySpawnedByClient(installedBattery);
packetSender.Send(spawnedPacket);
}
EntitySpawnedByClient spawnedPacket = new EntitySpawnedByClient(installedBattery);
packetSender.Send(spawnedPacket);
}
}
Loading