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 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using HarmonyLib;
using NitroxTest.Patcher;

namespace NitroxPatcher.Patches.Dynamic;

[TestClass]
public class PickPrefab_AddToContainerAsync_PatchTest
{
[TestMethod]
public void Sanity()
{
IEnumerable<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(PickPrefab_AddToContainerAsync_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = PickPrefab_AddToContainerAsync_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count() + 4);
}
}
16 changes: 16 additions & 0 deletions Nitrox.Test/Patcher/Patches/Dynamic/Trashcan_Update_PatchTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using HarmonyLib;
using NitroxTest.Patcher;

namespace NitroxPatcher.Patches.Dynamic;

[TestClass]
public class Trashcan_Update_PatchTest
{
[TestMethod]
public void Sanity()
{
IEnumerable<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(Trashcan_Update_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = Trashcan_Update_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count() + 3);
}
}
5 changes: 1 addition & 4 deletions Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,6 @@ private static void ItemDataTest(ItemData itemData, ItemData itemDataAfter)
Assert.AreEqual(equippedItemData.Slot, equippedItemDataAfter.Slot);
Assert.AreEqual(equippedItemData.TechType, equippedItemDataAfter.TechType);
break;
case PlantableItemData plantableItemData when itemDataAfter is PlantableItemData plantableItemDataAfter:
Assert.AreEqual(plantableItemData.PlantedGameTime, plantableItemDataAfter.PlantedGameTime);
break;
default:
Assert.Fail($"Runtime types of {nameof(ItemData)} where not equal");
break;
Expand Down Expand Up @@ -229,7 +226,7 @@ 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);
break;
case CyclopsMetadata metadata when entityAfter.Metadata is CyclopsMetadata metadataAfter:
Assert.AreEqual(metadata.SilentRunningOn, metadataAfter.SilentRunningOn);
Expand Down
49 changes: 0 additions & 49 deletions NitroxClient/GameLogic/Containers/ContainerAddItemPostProcessor.cs

This file was deleted.

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion NitroxClient/GameLogic/SimulationOwnership.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public SimulationOwnership(IMultiplayerSession muliplayerSession, IPacketSender
}
public bool PlayerHasMinLockType(NitroxId id, SimulationLockType lockType)
{
if (simulatedIdsByLockType.TryGetValue(id, out SimulationLockType playerLock))
if (id != null && simulatedIdsByLockType.TryGetValue(id, out SimulationLockType playerLock))
{
return playerLock <= lockType;
}
Expand Down
65 changes: 64 additions & 1 deletion NitroxClient/GameLogic/Spawning/InventoryItemEntitySpawner.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections;
using NitroxClient.Communication;
using NitroxClient.GameLogic.Helper;
Expand All @@ -6,10 +7,12 @@
using NitroxClient.MonoBehaviours;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Util;
using NitroxModel.Packets;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using UWE;

namespace NitroxClient.GameLogic.Spawning;

Expand Down Expand Up @@ -87,15 +90,75 @@ private void SetupObject(InventoryItemEntity entity, GameObject gameObject, Game
Pickupable pickupable = gameObject.RequireComponent<Pickupable>();
pickupable.Initialize();

InventoryItem inventoryItem = new(pickupable);

// Items eventually get "secured" once a player gets into a SubRoot (or for other reasons) so we need to force this state by default
// so that player don't risk their whole inventory if they reconnect in the water.
pickupable.destroyOnDeath = false;

bool isPlanter = parentObject.TryGetComponent(out Planter planter);
bool subscribedValue = false;
if (isPlanter)
{
subscribedValue = planter.subscribed;
planter.Subscribe(false);
}

using (PacketSuppressor<EntityReparented>.Suppress())
using (PacketSuppressor<PlayerQuickSlotsBindingChanged>.Suppress())
using (PacketSuppressor<EntityMetadataUpdate>.Suppress())
{
container.UnsafeAdd(new InventoryItem(pickupable));
container.UnsafeAdd(inventoryItem);
Log.Debug($"Received: Added item {pickupable.GetTechType()} ({entity.Id}) to container {parentObject.GetFullHierarchyPath()}");
}

if (isPlanter)
{
planter.Subscribe(subscribedValue);

PostponeAddNotification(() => planter.subscribed, () => planter, true, () =>
{
if (entity.Metadata is PlantableMetadata metadata)
{
// Adapted from Planter.AddItem(InventoryItem) to be able to call directly AddItem(Plantable, slotID) with our parameters
Plantable component = pickupable.GetComponent<Plantable>();
pickupable.SetTechTypeOverride(component.plantTechType, false);
inventoryItem.isEnabled = false;
planter.AddItem(component, metadata.SlotID);
}
});
}
else if (parentObject.TryGetComponent(out Trashcan trashcan))
{
PostponeAddNotification(() => trashcan.subscribed, () => trashcan, false, () =>
{
trashcan.AddItem(inventoryItem);
});
}
}

private static void PostponeAddNotification(Func<bool> subscribed, Func<bool> instanceValid, bool callbackIfAlreadySubscribed, Action callback)
{
IEnumerator PostponedAddCallback()
{
yield return new WaitUntil(() => subscribed() || !instanceValid());
if (instanceValid())
{
using (PacketSuppressor<EntityReparented>.Suppress())
using (PacketSuppressor<EntityMetadataUpdate>.Suppress())
{
callback();
}
}
}

if (!subscribed())
{
CoroutineHost.StartCoroutine(PostponedAddCallback());
}
else if (callbackIfAlreadySubscribed)
{
callback();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@ namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;

public class PlantableMetadataExtractor : EntityMetadataExtractor<Plantable, PlantableMetadata>
{
public override PlantableMetadata Extract(Plantable entity)
public override PlantableMetadata Extract(Plantable plantable)
{
GrowingPlant growingPlant = entity.growingPlant;

// The growing plant will only spawn in the proper container. In other containers, just consider progress as 0.
float progress = (growingPlant != null) ? growingPlant.GetProgress() : 0;

return new(progress);
return new(plantable.growingPlant ? plantable.growingPlant.timeStartGrowth : 0, plantable.GetSlotID());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,21 @@ public class PlantableMetadataProcessor : EntityMetadataProcessor<PlantableMetad
{
public override void ProcessMetadata(GameObject gameObject, PlantableMetadata metadata)
{
Plantable plantable = gameObject.GetComponent<Plantable>();

// Plantable will only have a growing plant when residing in the proper container.
if (plantable && plantable.growingPlant)
if (gameObject.TryGetComponent(out Plantable plantable))
{
if (plantable.growingPlant)
{
plantable.growingPlant.timeStartGrowth = metadata.TimeStartGrowth;
}
else if (plantable.model.TryGetComponent(out GrowingPlant growingPlant))
Copy link
Member

Choose a reason for hiding this comment

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

Would like a comment here explaining the state difference of the Plantable. Having the field growingPlant and getting it by hand seems like the same thing, so I guess the Plantable has a different state.

{
// Calculation from GrowingPlant.SetProgress (reversed because we're looking for "progress" while we already know timeStartGrowth)
plantable.plantAge = (DayNightCycle.main.timePassedAsFloat - metadata.TimeStartGrowth) / growingPlant.GetGrowthDuration();
}
}
else
{
plantable.growingPlant.SetProgress(metadata.Progress);
Log.Error($"[{nameof(PlantableMetadataProcessor)}] Could not find {nameof(Plantable)} on {gameObject.name}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,25 @@ namespace NitroxModel.DataStructures.GameLogic.Entities.Metadata;
public class PlantableMetadata : EntityMetadata
{
[DataMember(Order = 1)]
public float Progress { get; }
public float TimeStartGrowth { get; }

[DataMember(Order = 2)]
public int SlotID { get; }

[IgnoreConstructor]
protected PlantableMetadata()
{
// Constructor for serialization. Has to be "protected" for json serialization.
}

public PlantableMetadata(float progress)
public PlantableMetadata(float timeStartGrowth, int slotID)
{
Progress = progress;
TimeStartGrowth = timeStartGrowth;
SlotID = slotID;
}

public override string ToString()
{
return $"[PlantableMetadata Time: {Progress}]";
return $"[{nameof(PlantableMetadata)} TimeStartGrowth: {TimeStartGrowth}, SlotID: {SlotID}]";
}
}
3 changes: 1 addition & 2 deletions NitroxModel/DataStructures/GameLogic/ItemData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.Serialization;
using BinaryPack.Attributes;
using ProtoBufNet;
Expand All @@ -9,7 +9,6 @@ namespace NitroxModel.DataStructures.GameLogic
[DataContract]
[ProtoInclude(50, typeof(BasicItemData))]
[ProtoInclude(51, typeof(EquippedItemData))]
[ProtoInclude(52, typeof(PlantableItemData))]
public abstract class ItemData
{
[DataMember(Order = 1)]
Expand Down
Loading