Skip to content

Commit

Permalink
Version 0.6.0.4, fix Def serialization and join-point related desyncs
Browse files Browse the repository at this point in the history
  • Loading branch information
Zetrith committed Sep 26, 2021
1 parent 6847e44 commit 13a4dea
Show file tree
Hide file tree
Showing 23 changed files with 299 additions and 191 deletions.
2 changes: 1 addition & 1 deletion Languages
20 changes: 10 additions & 10 deletions Source/Client/Debug/DebugActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,16 @@ public static void SaveGameLocal()
public static void DumpSyncTypes()
{
var dict = new Dictionary<string, Type[]>() {
{"ThingComp", SyncSerialization.thingCompTypes},
{"AbilityComp", SyncSerialization.abilityCompTypes},
{"Designator", SyncSerialization.designatorTypes},
{"WorldObjectComp", SyncSerialization.worldObjectCompTypes},
{"IStoreSettingsParent", SyncSerialization.storageParents},
{"IPlantToGrowSettable", SyncSerialization.plantToGrowSettables},

{"GameComponent", SyncSerialization.gameCompTypes},
{"WorldComponent", SyncSerialization.worldCompTypes},
{"MapComponent", SyncSerialization.mapCompTypes},
{"ThingComp", ImplSerialization.thingCompTypes},
{"AbilityComp", ImplSerialization.abilityCompTypes},
{"Designator", ImplSerialization.designatorTypes},
{"WorldObjectComp", ImplSerialization.worldObjectCompTypes},
{"IStoreSettingsParent", ImplSerialization.storageParents},
{"IPlantToGrowSettable", ImplSerialization.plantToGrowSettables},

{"GameComponent", ImplSerialization.gameCompTypes},
{"WorldComponent", ImplSerialization.worldCompTypes},
{"MapComponent", ImplSerialization.mapCompTypes},
};

foreach(var kv in dict) {
Expand Down
2 changes: 1 addition & 1 deletion Source/Client/Multiplayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private static void EarlyPatches()
private static void InitSync()
{
using (DeepProfilerWrapper.Section("Multiplayer CollectTypes"))
SyncSerialization.CollectTypes();
SyncSerialization.Init();

using (DeepProfilerWrapper.Section("Multiplayer SyncGame"))
SyncGame.Init();
Expand Down
22 changes: 11 additions & 11 deletions Source/Client/MultiplayerData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,17 @@ internal static void CollectDefInfos()

int TypeHash(Type type) => GenText.StableStringHash(type.FullName);

dict["ThingComp"] = GetDefInfo(SyncSerialization.thingCompTypes, TypeHash);
dict["AbilityComp"] = GetDefInfo(SyncSerialization.abilityCompTypes, TypeHash);
dict["Designator"] = GetDefInfo(SyncSerialization.designatorTypes, TypeHash);
dict["WorldObjectComp"] = GetDefInfo(SyncSerialization.worldObjectCompTypes, TypeHash);
dict["IStoreSettingsParent"] = GetDefInfo(SyncSerialization.storageParents, TypeHash);
dict["IPlantToGrowSettable"] = GetDefInfo(SyncSerialization.plantToGrowSettables, TypeHash);
dict["DefTypes"] = GetDefInfo(SyncSerialization.defTypes, TypeHash);

dict["GameComponent"] = GetDefInfo(SyncSerialization.gameCompTypes, TypeHash);
dict["WorldComponent"] = GetDefInfo(SyncSerialization.worldCompTypes, TypeHash);
dict["MapComponent"] = GetDefInfo(SyncSerialization.mapCompTypes, TypeHash);
dict["ThingComp"] = GetDefInfo(ImplSerialization.thingCompTypes, TypeHash);
dict["AbilityComp"] = GetDefInfo(ImplSerialization.abilityCompTypes, TypeHash);
dict["Designator"] = GetDefInfo(ImplSerialization.designatorTypes, TypeHash);
dict["WorldObjectComp"] = GetDefInfo(ImplSerialization.worldObjectCompTypes, TypeHash);
dict["IStoreSettingsParent"] = GetDefInfo(ImplSerialization.storageParents, TypeHash);
dict["IPlantToGrowSettable"] = GetDefInfo(ImplSerialization.plantToGrowSettables, TypeHash);
dict["DefTypes"] = GetDefInfo(DefSerialization.DefTypes, TypeHash);

dict["GameComponent"] = GetDefInfo(ImplSerialization.gameCompTypes, TypeHash);
dict["WorldComponent"] = GetDefInfo(ImplSerialization.worldCompTypes, TypeHash);
dict["MapComponent"] = GetDefInfo(ImplSerialization.mapCompTypes, TypeHash);

dict["PawnBio"] = GetDefInfo(SolidBioDatabase.allBios, b => b.name.GetHashCode());
dict["Backstory"] = GetDefInfo(BackstoryDatabase.allBackstories.Keys, b => b.GetHashCode());
Expand Down
2 changes: 0 additions & 2 deletions Source/Client/MultiplayerGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ public MultiplayerGame()

SetThingMakerSeed(1);

Prefs.PauseOnLoad = false; // causes immediate desyncs on load if misaligned between host and clients

foreach (var field in typeof(DebugSettings).GetFields(BindingFlags.Public | BindingFlags.Static))
if (!field.IsLiteral && field.FieldType == typeof(bool))
field.SetValue(null, default(bool));
Expand Down
11 changes: 9 additions & 2 deletions Source/Client/MultiplayerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,13 @@ public void ProcessDisconnectPacket(MpDisconnectReason reason, byte[] data)
string strVersion = reader.ReadString();
int proto = reader.ReadInt32();

disconnectInfo.titleTranslated = "MpWrongMultiplayerVersionInfo".Translate(strVersion, proto);
disconnectInfo.wideWindow = true;
disconnectInfo.descTranslated = "MpWrongMultiplayerVersionInfo".Translate(strVersion, proto, MpVersion.Version);

if (proto < MpVersion.Protocol)
disconnectInfo.descTranslated += "\n" + "MpWrongVersionUpdateInfoHost".Translate();
else
disconnectInfo.descTranslated += "\n" + "MpWrongVersionUpdateInfo".Translate();
}

if (reason == MpDisconnectReason.ConnectingFailed)
Expand Down Expand Up @@ -214,7 +220,7 @@ public void ProcessTimeControl()

public void ScheduleCommand(ScheduledCommand cmd)
{
MpLog.Debug($"Cmd: {cmd.type}, faction: {cmd.factionId}, map: {cmd.mapId}, ticks: {cmd.ticks}");
MpLog.Debug(cmd.ToString());
dataSnapshot.mapCmds.GetOrAddNew(cmd.mapId).Add(cmd);

if (Current.ProgramState != ProgramState.Playing) return;
Expand Down Expand Up @@ -303,6 +309,7 @@ public struct SessionDisconnectInfo
public string descTranslated;
public string specialButtonTranslated;
public Action specialButtonAction;
public bool wideWindow;
}

public class GameDataSnapshot
Expand Down
3 changes: 2 additions & 1 deletion Source/Client/MultiplayerStatic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ static MultiplayerStatic()
using (DeepProfilerWrapper.Section("MultiplayerData PrecacheMods"))
MultiplayerData.PrecacheMods();

SimpleProfiler.Print("mp_prof_out.txt");
if (GenCommandLine.CommandLineArgPassed("profiler"))
SimpleProfiler.Print("mp_prof_out.txt");
}

private static void DoubleLongEvent(Action action, string textKey)
Expand Down
2 changes: 2 additions & 0 deletions Source/Client/Patches/TickPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ public static void Tick(out bool worked)
{
ScheduledCommand cmd = tickable.Cmds.Dequeue();
tickable.ExecuteCmd(cmd);

if (LongEventHandler.eventQueue.Count > 0) return; // Yield to e.g. join-point creation
}
}

Expand Down
40 changes: 40 additions & 0 deletions Source/Client/Syncing/DefSerialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using Multiplayer.Common;
using Verse;

namespace Multiplayer.Client
{
public static class DefSerialization
{
public static Type[] DefTypes { get; private set; }
private static Dictionary<Type, Type> hashableType = new();

public static void Init()
{
DefTypes = ImplSerialization.AllSubclassesNonAbstractOrdered(typeof(Def));

foreach (var defType in DefTypes)
{
var hashable = defType;
for (var t = defType; t != typeof(Def); t = t.BaseType)
if (!t.IsAbstract)
hashable = t;

hashableType[defType] = hashable;
}
}

private static Dictionary<Type, MethodInfo> methodCache = new();

public static Def GetDef(Type defType, ushort hash)
{
return (Def)methodCache.AddOrGet(
hashableType[defType],
static t => typeof(DefDatabase<>).MakeGenericType(t).GetMethod("GetByShortHash")
).Invoke(null, new[] { (object)hash });
}
}
}
1 change: 1 addition & 0 deletions Source/Client/Syncing/Dict/SyncDictRimWorld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Verse.AI;
using Verse.AI.Group;
using static Multiplayer.Client.SyncSerialization;
using static Multiplayer.Client.ImplSerialization;
// ReSharper disable RedundantLambdaParameterType

namespace Multiplayer.Client
Expand Down
22 changes: 22 additions & 0 deletions Source/Client/Syncing/ExposableSerialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using Multiplayer.Common;
using Verse;

namespace Multiplayer.Client
{
public static class ExposableSerialization
{
private static readonly MethodInfo ReadExposableDefinition =
AccessTools.Method(typeof(ScribeUtil), nameof(ScribeUtil.ReadExposable));

private static Dictionary<Type, MethodInfo> readExposableCache = new();

public static IExposable ReadExposable(Type type, byte[] data)
{
return (IExposable)readExposableCache.AddOrGet(type, newType => ReadExposableDefinition.MakeGenericMethod(newType)).Invoke(null, new[] { (object)data, null });
}
}
}
7 changes: 6 additions & 1 deletion Source/Client/Syncing/Handler/SyncMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public record SyncTransformer(Type LiveType, Type NetworkType, Delegate Writer,

public delegate void SyncMethodWriter(object obj, SyncType type, string debugInfo);

[HotSwappable]
public class SyncMethod : SyncHandler, ISyncMethod
{
public readonly Type targetType;
Expand Down Expand Up @@ -163,7 +164,11 @@ public override void Handle(ByteReader data)
else
target = ReadTarget(data);

if (targetType != null && target == null) return;
if (targetType != null && target == null)
{
MpLog.Debug($"SyncMethod {this} read target null");
return;
}

if (!instancePath.NullOrEmpty())
target = target.GetPropertyOrField(instancePath);
Expand Down
116 changes: 116 additions & 0 deletions Source/Client/Syncing/ImplSerialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Multiplayer.Common;
using RimWorld;
using RimWorld.Planet;
using Verse;

namespace Multiplayer.Client
{
public static class ImplSerialization
{
public static Type[] storageParents;
public static Type[] plantToGrowSettables;

public static Type[] thingCompTypes;
public static Type[] abilityCompTypes;
public static Type[] designatorTypes;
public static Type[] worldObjectCompTypes;

public static Type[] gameCompTypes;
public static Type[] worldCompTypes;
public static Type[] mapCompTypes;

internal static Type[] supportedThingHolders =
{
typeof(Map),
typeof(Thing),
typeof(ThingComp),
typeof(WorldObject),
typeof(WorldObjectComp)
};

internal enum ISelectableImpl : byte
{
None, Thing, Zone, WorldObject
}

internal enum VerbOwnerType : byte
{
None, Pawn, Ability, CompEquippable, CompReloadable
}

public static void Init()
{
storageParents = AllImplementationsOrdered(typeof(IStoreSettingsParent));
plantToGrowSettables = AllImplementationsOrdered(typeof(IPlantToGrowSettable));

thingCompTypes = AllSubclassesNonAbstractOrdered(typeof(ThingComp));
abilityCompTypes = AllSubclassesNonAbstractOrdered(typeof(AbilityComp));
designatorTypes = AllSubclassesNonAbstractOrdered(typeof(Designator));
worldObjectCompTypes = AllSubclassesNonAbstractOrdered(typeof(WorldObjectComp));

gameCompTypes = AllSubclassesNonAbstractOrdered(typeof(GameComponent));
worldCompTypes = AllSubclassesNonAbstractOrdered(typeof(WorldComponent));
mapCompTypes = AllSubclassesNonAbstractOrdered(typeof(MapComponent));
}

internal static T ReadWithImpl<T>(ByteReader data, IList<Type> impls) where T : class
{
ushort impl = data.ReadUShort();
if (impl == ushort.MaxValue) return null;
return (T)SyncSerialization.ReadSyncObject(data, impls[impl]);
}

internal static void WriteWithImpl<T>(ByteWriter data, object obj, IList<Type> impls) where T : class
{
if (obj == null)
{
data.WriteUShort(ushort.MaxValue);
return;
}

GetImpl(obj, impls, out Type implType, out int impl);

if (implType == null)
throw new SerializationException($"Unknown {typeof(T)} implementation type {obj.GetType()}");

data.WriteUShort((ushort)impl);
SyncSerialization.WriteSyncObject(data, obj, implType);
}

internal static void GetImpl(object obj, IList<Type> impls, out Type type, out int index)
{
type = null;
index = -1;

if (obj == null) return;

for (int i = 0; i < impls.Count; i++)
{
if (impls[i].IsAssignableFrom(obj.GetType()))
{
type = impls[i];
index = i;
break;
}
}
}

public static Type[] AllImplementationsOrdered(Type type)
{
return type.AllImplementing()
.OrderBy(t => t.IsInterface)
.ThenBy(t => t.Name)
.ToArray();
}

public static Type[] AllSubclassesNonAbstractOrdered(Type type) {
return type
.AllSubclassesNonAbstract()
.OrderBy(t => t.Name)
.ToArray();
}
}
}
3 changes: 1 addition & 2 deletions Source/Client/Syncing/SyncFieldUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ public static bool CheckShouldRemove(SyncField field, BufferTarget target, Buffe
public static object SnapshotValueIfNeeded(SyncField field, object value)
{
if (field.fieldType.expose)
return SyncSerialization.ReadExposable(field.fieldType.type)
.Invoke(null, new[] { ScribeUtil.WriteExposable((IExposable)value), null });
return ExposableSerialization.ReadExposable(field.fieldType.type, ScribeUtil.WriteExposable((IExposable)value));

return value;
}
Expand Down
Loading

0 comments on commit 13a4dea

Please sign in to comment.