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

Conversation

tornac1234
Copy link
Collaborator

@tornac1234 tornac1234 commented May 9, 2024

  • Add crops persistence (grow progress and exact slot)
  • Make sure crops sync works
  • Sync trash can behaviour (related because you often need to throw items when playing with crops, ig)
  • Sync deletion of giver objects (which give you some item when you click on them like melons)
  • Sync fruit growing (from grown plants) and harvesting (picking up)

@tornac1234 tornac1234 added the Area: bases Related to base building or interior game objects label May 9, 2024
@tornac1234 tornac1234 marked this pull request as ready for review May 10, 2024 09:21
@tornac1234
Copy link
Collaborator Author

New commit can be reviewed by itself

@tornac1234 tornac1234 added the Area: spawning Related to spawning and/or terrain label May 26, 2024
public static void Postfix(FruitPlant __instance, (float, NitroxId, Plantable) __state)
{
// If no change was made
if (__state.Item1 == __instance.timeNextFruit)
Copy link
Member

Choose a reason for hiding this comment

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

Direct float equality could be problematic here, no?

Copy link
Collaborator Author

@tornac1234 tornac1234 Feb 24, 2025

Choose a reason for hiding this comment

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

float a = __instance.timeNextFruit;

// bla bla bla

float b = __instance.timeNextFruit;

is there a reason why "a" could be different than "b" if __instance.timeNextFruit didn't change in the meantime ?

@tornac1234
Copy link
Collaborator Author

Note to fix something in StayAtLeashPositionMetadataProcessor (from leviathans PR)

@tornac1234 tornac1234 added this to the 1.8 milestone Jan 1, 2025
@kovadam69
Copy link

I merged this into the master branch on my local computer, and seems to work perfectly. Even restarting the server preserved the plant states, even the grow state. This seems to be OK.

@tornac1234 tornac1234 linked an issue Feb 23, 2025 that may be closed by this pull request
nickrbclark added a commit to NikeLaosClericus/Nitrox that referenced this pull request Feb 23, 2025
nickrbclark pushed a commit to NikeLaosClericus/Nitrox that referenced this pull request Feb 24, 2025
nickrbclark pushed a commit to NikeLaosClericus/Nitrox that referenced this pull request Feb 24, 2025
@NikeLaosClericus
Copy link

I followed the same process as kovadam69 to test this (delete ItemData.cs, remove referencing Method in Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs, and increment the ids).
It's definitely an improvement over current behavior, in that the plants are still there on relogs and server restarts, but there are a couple of problems.

  • Plant growth will sometimes reset to 0% on relog (even from a fully grown plant)
  • Planter overflow (marblemelons since they increase in size) can cause ghost plants to begin occupying slots. These can't be grabbed or removed, but can sometimes be caused to materialize by doing a base resync from options.

@NikeLaosClericus
Copy link

not visually full, but ghost plants in the way:
2025-02-24_00001
2025-02-24_00002

completely empty, but not:
2025-02-24_00005
2025-02-24_00006

Base Resync or Relogging can bring them back, but relog resets growth:
2025-02-24_00025

similar story outside:
2025-02-24_00020
2025-02-24_00021

server[crop-testing]-20250224.log
game[nike]-20250224.log

@tornac1234
Copy link
Collaborator Author

I'll be correcting this after all that happened since last year

…oring, fix plants death not broadcasted, fix trashcan sync, forcefully add and remove plants from containers (safer)
@tornac1234
Copy link
Collaborator Author

Corrected everything that was necessary, can I get more reviews and tests please ?

@NikeLaosClericus
Copy link

This is really good, fixed both issues I was having before.
Now for two more (minor) issues!

  • Sometimes when you harvest/grab a fully grown plant, and then immediately replant a seed of the same type, it will be instantly fully grown again. fastgrow was off. The max I was able to chain this was 5 times in a row, but it seems to "catch up" after a few seconds and realize the slot is no longer fully grown
  • growth state/percentage is not always consistent between players, but relogging will fix this.

game[Nike2]-20250225.log
server[crops]-20250225.log
game[Nike]-20250225.log
launcher-20250225.log

@tornac1234
Copy link
Collaborator Author

tornac1234 commented Feb 26, 2025

fastgrow command is not synced and will just break any source of syncing the crops

@tornac1234
Copy link
Collaborator Author

This is really good, fixed both issues I was having before. Now for two more (minor) issues!

* Sometimes when you harvest/grab a fully grown plant, and then immediately replant a seed of the same type, it will be instantly fully grown again. `fastgrow` was off. The max I was able to chain this was 5 times in a row, but it seems to "catch up" after a few seconds and realize the slot is no longer fully grown

* growth state/percentage is not always consistent between players, but relogging will fix this.

game[Nike2]-20250225.log server[crops]-20250225.log game[Nike]-20250225.log launcher-20250225.log

Do you have any example of seed to replant fast ?

@NikeLaosClericus
Copy link

fastgrow command is not synced and will just break any source of syncing the crops

That is probably why the two players were different.

@NikeLaosClericus
Copy link

This is really good, fixed both issues I was having before. Now for two more (minor) issues!

* Sometimes when you harvest/grab a fully grown plant, and then immediately replant a seed of the same type, it will be instantly fully grown again. `fastgrow` was off. The max I was able to chain this was 5 times in a row, but it seems to "catch up" after a few seconds and realize the slot is no longer fully grown

* growth state/percentage is not always consistent between players, but relogging will fix this.

game[Nike2]-20250225.log server[crops]-20250225.log game[Nike]-20250225.log launcher-20250225.log

Do you have any example of seed to replant fast ?

Marblemelons and AcidShrooms

@tornac1234
Copy link
Collaborator Author

I'm unfortunately unable to reproduce this bug... It might have been because of fastgrowth ? I'll try to sync the command but otherwise I'll just disable it

@NikeLaosClericus
Copy link

I'm unfortunately unable to reproduce this bug... It might have been because of fastgrowth ? I'll try to sync the command but otherwise I'll just disable it

Just retested and reproduced.
I created a new server world, and never touched fastgrow or bobthebuilder.

It seems to be easiest to trigger right after a rejoin, and coincides with these errors in the server console:

[20:21:53.894] [ERR] Error in packet processor NitroxServer.Communication.Packets.Processors.EntityDestroyedPacketProcessor
System.ArgumentNullException: Value cannot be null. (Parameter 'key')
   at System.Collections.Generic.Dictionary`2.Remove(TKey key)
   at NitroxServer.GameLogic.SimulationOwnershipData.RevokeOwnerOfId(NitroxId id) in NitroxServer\GameLogic\SimulationOwnership.cs:line 95
   at NitroxServer.GameLogic.Entities.EntitySimulation.EntityDestroyed(NitroxId id) in NitroxServer\GameLogic\Entities\EntitySimulation.cs:line 160
   at NitroxServer.Communication.Packets.Processors.EntityDestroyedPacketProcessor.Process(EntityDestroyed packet, Player destroyingPlayer) in NitroxServer\Communication\Packets\Processors\EntityDestroyedPacketProcessor.cs:line 26
   at NitroxServer.Communication.Packets.Processors.Abstract.AuthenticatedPacketProcessor`1.ProcessPacket(Packet packet, IProcessorContext player) in NitroxServer\Communication\Packets\Processors\Abstract\AuthenticatedPacketProcessor.cs:line 10
   at NitroxServer.Communication.Packets.PacketHandler.ProcessAuthenticated(Packet packet, Player player) in NitroxServer\Communication\Packets\PacketHandler.cs:line 51
[20:22:05.949] [ERR] Error in packet processor NitroxServer.Communication.Packets.Processors.EntityDestroyedPacketProcessor
System.ArgumentNullException: Value cannot be null. (Parameter 'key')
   at System.Collections.Generic.Dictionary`2.Remove(TKey key)
   at NitroxServer.GameLogic.SimulationOwnershipData.RevokeOwnerOfId(NitroxId id) in NitroxServer\GameLogic\SimulationOwnership.cs:line 95
   at NitroxServer.GameLogic.Entities.EntitySimulation.EntityDestroyed(NitroxId id) in NitroxServer\GameLogic\Entities\EntitySimulation.cs:line 160
   at NitroxServer.Communication.Packets.Processors.EntityDestroyedPacketProcessor.Process(EntityDestroyed packet, Player destroyingPlayer) in NitroxServer\Communication\Packets\Processors\EntityDestroyedPacketProcessor.cs:line 26
   at NitroxServer.Communication.Packets.Processors.Abstract.AuthenticatedPacketProcessor`1.ProcessPacket(Packet packet, IProcessorContext player) in NitroxServer\Communication\Packets\Processors\Abstract\AuthenticatedPacketProcessor.cs:line 10
   at NitroxServer.Communication.Packets.PacketHandler.ProcessAuthenticated(Packet packet, Player player) in NitroxServer\Communication\Packets\PacketHandler.cs:line 51
[20:22:11.757] [ERR] Error in packet processor NitroxServer.Communication.Packets.Processors.EntityDestroyedPacketProcessor
System.ArgumentNullException: Value cannot be null. (Parameter 'key')
   at System.Collections.Generic.Dictionary`2.Remove(TKey key)
   at NitroxServer.GameLogic.SimulationOwnershipData.RevokeOwnerOfId(NitroxId id) in NitroxServer\GameLogic\SimulationOwnership.cs:line 95
   at NitroxServer.GameLogic.Entities.EntitySimulation.EntityDestroyed(NitroxId id) in NitroxServer\GameLogic\Entities\EntitySimulation.cs:line 160
   at NitroxServer.Communication.Packets.Processors.EntityDestroyedPacketProcessor.Process(EntityDestroyed packet, Player destroyingPlayer) in NitroxServer\Communication\Packets\Processors\EntityDestroyedPacketProcessor.cs:line 26
   at NitroxServer.Communication.Packets.Processors.Abstract.AuthenticatedPacketProcessor`1.ProcessPacket(Packet packet, IProcessorContext player) in NitroxServer\Communication\Packets\Processors\Abstract\AuthenticatedPacketProcessor.cs:line 10
   at NitroxServer.Communication.Packets.PacketHandler.ProcessAuthenticated(Packet packet, Player player) in NitroxServer\Communication\Packets\PacketHandler.cs:line 51

server[crops2]-20250225.log
game[Nike]-20250225.log
launcher-20250225.log

@tornac1234
Copy link
Collaborator Author

That is a pretty bad error indeed. Can you explain to me the exact procedure you did to obtain these ? (please mention which commands you used to get the items, and when you picked up something/planted something, with the number of players)

@NikeLaosClericus
Copy link

That is a pretty bad error indeed. Can you explain to me the exact procedure you did to obtain these ? (please mention which commands you used to get the items, and when you picked up something/planted something, with the number of players)

  1. created and started a new creative mode world (started from the launcher)
  2. joined as admin client locally
  3. no commands used, menu or terminal, used the fabricator in the pod to make a habitat builder and a knife
  4. found and collected a patch of acid mushrooms (whole and seeds)
  5. created a single external grow bed on the seafloor
  6. filled grow bed with fully grown shrooms
  7. attempted to trigger glitch by knifing a shroom in the bed and planting a seed (failed to trigger)
  8. quit from client, left server running
  9. restart client and rejoin
  10. immediately attempt to trigger glitch by knifing shroom and planting seed (success, multiple times)
  11. glitch stops working after couple seconds.
  12. quit, rejoin, trigger glitch again (success, another 3 times)

possibly relevant client log slice:

[20:22:11.756] [DBG] PickedUp item [InventoryItemEntity ClassId: adad4264-23ee-4303-8369-9f6471d91c28 [Entity id: ed793ae7-ac40-4eaa-a46f-4275761e05db techType: AcidMushroomSpore Metadata: [PlantableMetadata TimeStartGrowth: 0, SlotID: -1, FruitPlantMetadata: ] ParentId: 7b99aa24-9ead-46e5-a71b-74130f314afe ChildEntities: ]]
[20:22:13.202] [DBG] Planted item [InventoryItemEntity ClassId: adad4264-23ee-4303-8369-9f6471d91c28 [Entity id: 3065ee7c-cae9-46c5-a456-ddedaf3b6cf8 techType: AcidMushroom Metadata: [PlantableMetadata TimeStartGrowth: 416.6447, SlotID: 24, FruitPlantMetadata: ] ParentId: c8c2c022-d5d2-4119-acc1-93c886d9c251 ChildEntities: ]]
[20:22:13.221] [ERR-UNITY] Unregistering id '3065ee7c-cae9-46c5-a456-ddedaf3b6cf8' (class 'adad4264-23ee-4303-8369-9f6471d91c28', registered class '61a5e0e6-01d5-4ae2-aea6-1186cd769025') failed because it already changed to 'Coral_reef_purple_mushrooms_01_02(Clone)' at (-171.4, -30.8, 92.6), used to be 'AcidMushroomSpore(Clone)' at (-171.4, -30.8, 92.6)
[20:22:15.441] [WRN] [LiveMixin_Kill_Patch.Postfix():L24] Couldn't find an id on Landscape/Global Root/FarmingTray(Clone)/slots_small/slot19/Coral_reef_purple_mushrooms_01_02(Clone)(Clone) -> LiveMixin.cs
[20:22:15.449] [DBG] PickedUp item [InventoryItemEntity ClassId: adad4264-23ee-4303-8369-9f6471d91c28 [Entity id: 219fd8cb-d887-4267-8bbd-d98cce4f696e techType: AcidMushroomSpore Metadata: [PlantableMetadata TimeStartGrowth: 0, SlotID: -1, FruitPlantMetadata: ] ParentId: 7b99aa24-9ead-46e5-a71b-74130f314afe ChildEntities: ]]
[20:22:16.832] [DBG] Planted item [InventoryItemEntity ClassId: adad4264-23ee-4303-8369-9f6471d91c28 [Entity id: 99c72733-408c-4430-b8cc-8deb9ab23b73 techType: AcidMushroom Metadata: [PlantableMetadata TimeStartGrowth: 820.2739, SlotID: 24, FruitPlantMetadata: ] ParentId: c8c2c022-d5d2-4119-acc1-93c886d9c251 ChildEntities: ]]

failed because it already changed to 'Coral_reef_purple_mushrooms_01_02(Clone)' at (-171.4, -30.8, 92.6), used to be 'AcidMushroomSpore(Clone)' at (-171.4, -30.8, 92.6)

@tornac1234
Copy link
Collaborator Author

@NikeLaosClericus Thanks, I was able to reproduce the issues and fix them and I even synced both multiplayer-relevant fast commands (fastgrow and fasthatch)

@NikeLaosClericus
Copy link

The problem is still there for me, but I'm going to restate it because I learned more:

(instead of depending on harvesting a fully grown plant first)
For a period of 30-60 seconds (variable) after joining a server, planting an acid mushroom seed into an exterior grow bed will cause a fully grown acid mushroom to appear in the bed.
This fully grown shroom can be picked or cut just like any other, and is correctly synced** across all players.
(to be clear, the seed becomes a fully grown shroom for all players, and may be harvested by any)
After the variable period of time, seeds will correctly be planted with 0% growth.

**Note: there are instances when, seemingly at the end of the variable 30-60 seconds, that planting will become desynced between players, and the planting player will see a filled grow bed slot, while the other sees it as empty.
The player that sees it as empty can then plant something else in that slot, and both continue to grow correctly for both players.

This can be seen here, as the top player planted a deep mushroom right at the end of the variable time, but this planting was not synced to bottom player. Bottom player then planted an acid mushroom in the same slot. both are independently harvestable by their respective player.
image

Another instance here, same circumstances:
sub-crops-2player-desync

The fast commands sync seems to be good.

game[Nike]-20250226.log
launcher-20250226.log
server[crops]-20250226.log
game[Nike2]-20250226.log

@tornac1234
Copy link
Collaborator Author

tornac1234 commented Feb 26, 2025

The bug I fixed requires you to use new mushrooms items tho, it's about how metadata is set onto them when you pick them up. Can you retry with a new world ?
EDIT: I was able to reproduce the issue and am fixing it

…anter, and a bug where the plant age wouldn't be calculated properly, and some refactoring
@tornac1234
Copy link
Collaborator Author

Thanks a lot for the bug reports @NikeLaosClericus, I could fix both of them and fortunately, no one will have to come across those after release :)

@NikeLaosClericus
Copy link

Thanks a lot for the bug reports @NikeLaosClericus, I could fix both of them and fortunately, no one will have to come across those after release :)

@tornac1234 Thank YOU for fixing these, it's been bugging my friends that they couldn't farm anything.

@NikeLaosClericus
Copy link

The bug I fixed requires you to use new mushrooms items tho, it's about how metadata is set onto them when you pick them up. Can you retry with a new world ? EDIT: I was able to reproduce the issue and am fixing it

Yeah, I had been using a new world, I do with each commit you push.

@NikeLaosClericus
Copy link

NikeLaosClericus commented Feb 27, 2025

It's much better, the 30-60 second window seems to be gone, no more adult shrooms immediately growing from seeds.

Now though, I've noticed ghost plants have (pretty rarely) come back.
(This problem could have been there the whole time, and was just missed because it was rarer than the others)

  1. While just planting/grabbing/harvesting shrooms for a bit in an exterior grow bed, I noticed one player could no longer plant in a slot as it appeared full.

2025-02-27_00002
2025-02-27_00001

The second player didn't have this issue, and could plant there safely.
The first player can then pick the shroom the second player planted in the slot, but then still sees it as occupied and can't use it.
Relogging does not fix it for the affected player, but restarting the server seemed to cause a the shroom to come back for them.

  1. I also noticed that if you enable and disable fastgrow, which does sync between players, if you relog the plants will revert to a their ungrown state just for the relogged player:

image
I don't know if this is actually expected behavior for fastgrow, ie, the growth state isn't persisted to the server? But the plants are still there, so it's not like anything but stolen time is lost.

(I'd like to just say that these are very small issues compared to what we came from, and so much has been fixed, that I would be thrilled with this getting merged even in this state.)

launcher-20250227.log
server[crops]-20250227.log
game[Nike]-20250227.log
game[Nike2]-20250227.log

@tornac1234
Copy link
Collaborator Author

I am currently unable to reproduce the "ghost shroom" bug but I suspect it happens because of lag... And the second one happens because we never update the state of a plant when it finally grows up. I could do it but will give it a bit more thinking

@tornac1234 tornac1234 requested a review from Jannify February 28, 2025 14:33
@@ -24,6 +24,11 @@ public class Items
public static GameObject PickingUpObject { get; private set; }
private readonly EntityMetadataManager entityMetadataManager;

/// <summary>
/// Whether or not <see cref="Inventory.Pickup"/> is running. It's useful to discriminate between Inventory.Pickup from a regular
Copy link
Member

Choose a reason for hiding this comment

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

I don't really understand this comment. What is regular?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I cut the comment again 💀 "a regular Pickupable.Pickup"

@tornac1234
Copy link
Collaborator Author

I have to fix one more thing

@tornac1234
Copy link
Collaborator Author

Two last commits needs review


if (parent.GetComponent<Constructable>() || parent.GetComponent<IBaseModule>().AliveOrNull())
// This check must happen before the GetComponent<IBaseModule> which will be triggered (wrong result)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm unclear what this is trying to say. Is it saying that if the GetComponent call is done first it will produce an unwanted outcome? If so I think the end of the comment should be reworded it's a little unclear if being triggered is wrong or if this check prevents that

{
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;
}
// 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

}

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

return;
}

NitroxId planterId = waterParkId.Increment();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't have the increment code in front of me but I wanted to check this gives not the same as the other waterpark increment call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: bases Related to base building or interior game objects Area: spawning Related to spawning and/or terrain
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crops grow state aren't saved on logout
8 participants