shuhaowu / DeepWoodsMod

A mod for Stardew Valley using SMAPI. It adds an endless procedurally generated forest to the game.
0 stars 0 forks source link

Game crash when guest player in MP holds easter egg in inventory overnight (during save) #1

Open shuhaowu opened 4 years ago

shuhaowu commented 4 years ago

The multiplayer game crashes for both the guest and host when a guest player holds the easter egg in their inventory overnight. This is caused because the base game for the guest is attempting to serialize DeepWoodsMod.EasterEggItem into XML and send it over the network. This fails with the exception System.InvalidOperationException: There was an error generating the XML document., which causes a crash loop on the client (guest) and a freeze on the server (and eventually crashes). Proposed solution is below:

Steps to reproduce

  1. Create a MP game with two players. Both players should have DeepWoodsMod installed.
  2. The guest player (client) should pick up an easter egg and hold it in their inventory.
  3. The main player goes to sleep.
  4. The guest player goes to sleep.
  5. At this point, the guest player's game will crash.
  6. The main player's game will freeze and eventually can be killed.

Crash logs

Root cause analysis

It appears that DeepWoodsMod attempts to clear all known instances of EasterEggItem via the code EasterEggFunctions.RemoveAllEasterEggsFromGame(); before saving (in CleanupBeforeSave) and then is restored in RestoreAfterSave via the code EasterEggFunctions.RestoreAllEasterEggsInGame();(footnote1). In multiplayer, if we look at the logs, it appears that the base Stardew Valley attempts to transmit the guest player's state to the host before the events.GameLoop.Saving event handlers are called. This means that RemoveAllEasterEggsFromGame is not called. The game then will attempt to send EasterEggItem over the network via XML (from the client to host), which results in a serialization error.

Recommended fix

After speaking with the people on Stardew Valley's modding Discord channel, it appears that this is a known problem with respect to having custom classes extending StardewValley.Object.

There appears to be several solutions to this problem:

Unfortunately, I'm not familiar enough with StardewValley modding to complete the fix at this time and I won't have time over the next little while to do this. Also, both of these solutions looks like it will be relatively bigger code changes, which again is something I lack time for, for now.

Appendix: footnote1

Does this mean that EasterEggs will be wiped out if one restarts the game as they're not saved to the save file anywhere?

Appendix: crash log excerpt

[23:53:59 ERROR game] An error occured in the base update loop: System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type DeepWoodsMod.EasterEggItem was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
  at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterFarmer.Write43_Item (System.String n, System.String ns, StardewValley.Item o, Boolean isNullable, Boolean needType) <0x42256000 + 0x0120b> in <filename unknown>:0 
  at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterFarmer.Write74_Farmer (System.String n, System.String ns, StardewValley.Farmer o, Boolean isNullable, Boolean needType) <0x4223e9e0 + 0x00f2b> in <filename unknown>:0 
  at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterFarmer.Write75_Farmer (System.Object o) <0x4223dd60 + 0x000bb> in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) <0x41c2e230 + 0x000a3> in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at System.Xml.Serialization.XmlSerializer.Serialize (System.Xml.XmlWriter xmlWriter, System.Object o, System.Xml.Serialization.XmlSerializerNamespaces namespaces, System.String encodingStyle, System.String id) <0x41fd12c0 + 0x0044f> in <filename unknown>:0 
  at System.Xml.Serialization.XmlSerializer.Serialize (System.Xml.XmlWriter xmlWriter, System.Object o, System.Xml.Serialization.XmlSerializerNamespaces namespaces, System.String encodingStyle) <0x41fd1280 + 0x00023> in <filename unknown>:0 
  at System.Xml.Serialization.XmlSerializer.Serialize (System.Xml.XmlWriter xmlWriter, System.Object o, System.Xml.Serialization.XmlSerializerNamespaces namespaces) <0x41fd1250 + 0x0001f> in <filename unknown>:0 
  at System.Xml.Serialization.XmlSerializer.Serialize (System.IO.Stream stream, System.Object o, System.Xml.Serialization.XmlSerializerNamespaces namespaces) <0x4223d8d0 + 0x0007b> in <filename unknown>:0 
  at System.Xml.Serialization.XmlSerializer.Serialize (System.IO.Stream stream, System.Object o) <0x4223d8a0 + 0x0001b> in <filename unknown>:0 
  at Netcode.NetRefBase`2[T,TSelf].serialize (System.IO.BinaryWriter writer, System.Xml.Serialization.XmlSerializer serializer) <0x4223d780 + 0x00073> in <filename unknown>:0 
  at Netcode.NetRefBase`2[T,TSelf].writeBaseValue (System.IO.BinaryWriter writer) <0x4223d720 + 0x0002b> in <filename unknown>:0 
  at Netcode.NetRefBase`2+<>c__DisplayClass26_0[T,TSelf].<WriteDelta>b__0 () <0x422d1630 + 0x00043> in <filename unknown>:0 
  at Netcode.BinaryReaderWriterExtensions.WriteSkippable (System.IO.BinaryWriter writer, System.Action writeAction) <0x42245d90 + 0x0005e> in <filename unknown>:0 
  at Netcode.NetRefBase`2[T,TSelf].WriteDelta (System.IO.BinaryWriter writer) <0x422cc880 + 0x001eb> in <filename unknown>:0 
  at Netcode.NetFieldBase`2[T,TSelf].Write (System.IO.BinaryWriter writer) <0x422cc850 + 0x00020> in <filename unknown>:0 
  at Netcode.NetRoot`1[T].Write (System.IO.BinaryWriter writer) <0x422cc7e0 + 0x0003f> in <filename unknown>:0 
  at StardewValley.Multiplayer.writeObjectDeltaBytes[T] (Netcode.NetRoot`1 root) <0x422cc6d0 + 0x0006f> in <filename unknown>:0 
  at StardewValley.Multiplayer.broadcastFarmerDeltas () <0x422cbe20 + 0x000cd> in <filename unknown>:0 
  at StardewValley.Multiplayer.UpdateLate (Boolean forceSync) <0x420488f0 + 0x0005f> in <filename unknown>:0 
  at StardewValley.NewDaySynchronizer.processMessages () <0x42574230 + 0x00016> in <filename unknown>:0 
  at StardewValley.Game1._newDayAfterFade () <0x42568000 + 0x02d72> in <filename unknown>:0 
  at StardewValley.Game1+<>c__DisplayClass607_0.<newDayAfterFade>b__0 () <0x42555560 + 0x0000f> in <filename unknown>:0 
  at StardewModdingAPI.Framework.SModHooks.OnGame1_NewDayAfterFade (System.Action action) <0x42567fb0 + 0x00031> in <filename unknown>:0 
  at StardewValley.Game1.newDayAfterFade (System.Action after) <0x425553f0 + 0x000f3> in <filename unknown>:0 
  at StardewValley.Game1.onFadeToBlackComplete () <0x423c1fc0 + 0x010af> in <filename unknown>:0 
  at StardewValley.BellsAndWhistles.ScreenFade.UpdateFade (Microsoft.Xna.Framework.GameTime time) <0x42339240 + 0x000a3> in <filename unknown>:0 
  at StardewValley.Game1.UpdateOther (Microsoft.Xna.Framework.GameTime time) <0x42337500 + 0x00093> in <filename unknown>:0 
  at StardewValley.Game1._update (Microsoft.Xna.Framework.GameTime gameTime) <0x42036d80 + 0x03243> in <filename unknown>:0 
  at StardewValley.Game1.Update (Microsoft.Xna.Framework.GameTime gameTime) <0x42036b80 + 0x000bb> in <filename unknown>:0 
  at StardewModdingAPI.Framework.SGame.Update (Microsoft.Xna.Framework.GameTime gameTime) <0x42013000 + 0x04b47> in <filename unknown>:0 
shuhaowu commented 4 years ago

image

maxvollmer commented 4 years ago

That is the single best bug report I have ever seen in my life. Thank you so much for the work you put into documenting all this. I should be able to fix the bug based on your information. I will hopfeully get to it one of the coming weekends.