forked from SubnauticaNitrox/Nitrox
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added save file upgradability (SubnauticaNitrox#1370)
* Added save file upgradability * Changed config value setting with commands * Moved waiting to server * Moved from string to JObject in SaveDataUpgrade
- Loading branch information
Showing
18 changed files
with
317 additions
and
105 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using NitroxModel.DataStructures.GameLogic; | ||
using NitroxModel.Serialization; | ||
using NitroxModel.Server; | ||
using NitroxServer.ConsoleCommands.Abstract; | ||
using NitroxServer.Serialization; | ||
using NitroxServer.Serialization.World; | ||
|
||
namespace NitroxServer.ConsoleCommands | ||
{ | ||
internal sealed class SwapSerializerCommand : Command | ||
{ | ||
private readonly WorldPersistence worldPersistence; | ||
private readonly ServerProtoBufSerializer protoBufSerializer; | ||
private readonly ServerJsonSerializer jsonSerializer; | ||
|
||
public SwapSerializerCommand(WorldPersistence worldPersistence, ServerProtoBufSerializer protoBufSerializer, ServerJsonSerializer jsonSerializer) : base("swapSerializer", Perms.CONSOLE, "Swaps the world data serializer") | ||
{ | ||
this.worldPersistence = worldPersistence; | ||
this.protoBufSerializer = protoBufSerializer; | ||
this.jsonSerializer = jsonSerializer; | ||
} | ||
|
||
protected override void Execute(CallArgs args) | ||
{ | ||
ServerConfig serverConfig = NitroxConfig.Deserialize<ServerConfig>(); | ||
serverConfig.SerializerMode = serverConfig.SerializerMode == ServerSerializerMode.PROTOBUF ? ServerSerializerMode.JSON : ServerSerializerMode.PROTOBUF; | ||
NitroxConfig.Serialize(serverConfig); | ||
|
||
worldPersistence.UpdateSerializer(serverConfig.SerializerMode == ServerSerializerMode.PROTOBUF ? (IServerSerializer)protoBufSerializer : jsonSerializer); | ||
SendMessage(args.Sender, $"Swapped to {serverConfig.SerializerMode}"); | ||
} | ||
} | ||
} |
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,59 @@ | ||
using System.IO; | ||
using System.Threading; | ||
using NitroxModel.DataStructures.GameLogic; | ||
using NitroxModel.Helper; | ||
using NitroxModel.Server; | ||
using NitroxServer.ConsoleCommands.Abstract; | ||
using NitroxServer.Serialization; | ||
using NitroxServer.Serialization.Upgrade; | ||
using NitroxServer.Serialization.World; | ||
|
||
namespace NitroxServer.ConsoleCommands | ||
{ | ||
internal sealed class UpgradeCommand : Command | ||
{ | ||
private readonly WorldPersistence worldPersistence; | ||
private readonly ServerConfig serverConfig; | ||
private readonly SaveDataUpgrade[] upgrades; | ||
private readonly Server server; | ||
|
||
public UpgradeCommand(WorldPersistence worldPersistence, ServerConfig serverConfig, SaveDataUpgrade[] upgrades, Server server) : base("upgrade", Perms.CONSOLE, "Upgrades the save file to the next version") | ||
{ | ||
this.worldPersistence = worldPersistence; | ||
this.serverConfig = serverConfig; | ||
this.upgrades = upgrades; | ||
this.server = server; | ||
} | ||
|
||
protected override void Execute(CallArgs args) | ||
{ | ||
server.DisablePeriodicSaving(); | ||
string saveDir = serverConfig.SaveName; | ||
string fileEnding = worldPersistence.SaveDataSerializer.GetFileEnding(); | ||
SaveFileVersion saveFileVersion = worldPersistence.SaveDataSerializer.Deserialize<SaveFileVersion>(Path.Combine(saveDir, "Version" + fileEnding)); | ||
|
||
|
||
if (saveFileVersion.Version == NitroxEnvironment.Version) | ||
{ | ||
SendMessage(args.Sender, "Save files are already at the newest version"); | ||
} | ||
else if (serverConfig.SerializerMode == ServerSerializerMode.PROTOBUF) | ||
{ | ||
SendMessage(args.Sender, "Can't upgrade while using ProtoBuf as serializer"); | ||
} | ||
else | ||
{ | ||
foreach (SaveDataUpgrade upgrade in upgrades) | ||
{ | ||
if (upgrade.TargetVersion > saveFileVersion.Version) | ||
{ | ||
upgrade.UpgradeData(saveDir, fileEnding); | ||
} | ||
} | ||
worldPersistence.SaveDataSerializer.Serialize(Path.Combine(saveDir, "Version" + fileEnding), new SaveFileVersion()); | ||
SendMessage(args.Sender, $"Save file was upgraded to {NitroxEnvironment.Version}"); | ||
server.StopAndWait(false); | ||
} | ||
} | ||
} | ||
} |
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
48 changes: 48 additions & 0 deletions
48
NitroxServer/Serialization/SaveDataUpgrades/NewtonsoftExtensions.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,48 @@ | ||
#nullable enable | ||
using System; | ||
using Newtonsoft.Json.Linq; | ||
|
||
namespace NitroxServer.Serialization.SaveDataUpgrades | ||
{ | ||
public static class NewtonsoftExtensions | ||
{ | ||
public static void Rename(this JToken token, string newName) | ||
{ | ||
if (token == null) | ||
{ | ||
throw new ArgumentNullException("token", "Cannot rename a null token"); | ||
} | ||
|
||
JProperty property; | ||
|
||
if (token.Type == JTokenType.Property) | ||
{ | ||
if (token.Parent == null) | ||
{ | ||
throw new InvalidOperationException("Cannot rename a property with no parent"); | ||
} | ||
|
||
property = (JProperty)token; | ||
} | ||
else | ||
{ | ||
if (token.Parent == null || token.Parent.Type != JTokenType.Property) | ||
{ | ||
throw new InvalidOperationException("This token's parent is not a JProperty; cannot rename"); | ||
} | ||
|
||
property = (JProperty)token.Parent; | ||
} | ||
|
||
// Note: to avoid triggering a clone of the existing property's value, | ||
// we need to save a reference to it and then null out property.Value | ||
// before adding the value to the new JProperty. | ||
// Thanks to @dbc for the suggestion. | ||
|
||
JToken? existingValue = property.Value; | ||
property.Value = null!; | ||
JProperty? newProperty = new JProperty(newName, existingValue); | ||
property.Replace(newProperty); | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
NitroxServer/Serialization/SaveDataUpgrades/SaveDataUpgrade.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,56 @@ | ||
using System; | ||
using System.IO; | ||
using Newtonsoft.Json; | ||
using Newtonsoft.Json.Converters; | ||
using Newtonsoft.Json.Linq; | ||
using NitroxModel.DataStructures.JsonConverter; | ||
using NitroxModel.Logger; | ||
|
||
namespace NitroxServer.Serialization.Upgrade | ||
{ | ||
public abstract class SaveDataUpgrade | ||
{ | ||
private readonly JsonConverter[] converters = { new NitroxIdConverter(), new TechTypeConverter(), new VersionConverter(), new KeyValuePairConverter(), new StringEnumConverter() }; | ||
|
||
public abstract Version TargetVersion { get; } | ||
|
||
public void UpgradeData(string saveDir, string fileEnding) | ||
{ | ||
Log.Info($"Executing {GetType().Name}"); | ||
string baseDataPath = Path.Combine(saveDir, "BaseData" + fileEnding); | ||
string playerDataPath = Path.Combine(saveDir, "PlayerData" + fileEnding); | ||
string worldDataPath = Path.Combine(saveDir, "WorldData" + fileEnding); | ||
string entityDataPath = Path.Combine(saveDir, "EntityData" + fileEnding); | ||
|
||
try | ||
{ | ||
Log.Info("├── Parsing raw json"); | ||
JObject baseData = JObject.Parse(File.ReadAllText(baseDataPath)); | ||
JObject playerData = JObject.Parse(File.ReadAllText(playerDataPath)); | ||
JObject worldData = JObject.Parse(File.ReadAllText(worldDataPath)); | ||
JObject entityData = JObject.Parse(File.ReadAllText(entityDataPath)); | ||
|
||
Log.Info("├── Applying upgrade scripts"); | ||
UpgradeBaseData(baseData); | ||
UpgradePlayerData(playerData); | ||
UpgradeWorldData(worldData); | ||
UpgradeEntityData(entityData); | ||
|
||
Log.Info("└── Saving to disk"); | ||
File.WriteAllText(baseDataPath, baseData.ToString(Formatting.None, converters)); | ||
File.WriteAllText(playerDataPath, playerData.ToString(Formatting.None, converters)); | ||
File.WriteAllText(worldDataPath, worldData.ToString(Formatting.None, converters)); | ||
File.WriteAllText(entityDataPath, entityData.ToString(Formatting.None, converters)); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Log.Error(ex, $"Error while upgrading save file with {GetType().Name}"); | ||
} | ||
} | ||
|
||
protected virtual void UpgradeBaseData(JObject data) { } | ||
protected virtual void UpgradePlayerData(JObject data) { } | ||
protected virtual void UpgradeWorldData(JObject data) { } | ||
protected virtual void UpgradeEntityData(JObject data) { } | ||
} | ||
} |
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
Oops, something went wrong.