Skip to content

Commit

Permalink
Update MpCompatPatchLoader and sync field attribute (#472)
Browse files Browse the repository at this point in the history
Updates to `MpCompatSyncFieldAttribute`:
- Removed `context`, as it's unused (both here and in MP)
  - This includes removing the constructor accepting that argument
  - The existing constructors no longer accept SyncContext argument
- Made `fieldName` field public
- Made `instancePath` field
- Added constructors accepting `instancePath` as an argument
  - This means constructors accepting `type` as both `string` and `Type`
- The `Type` getter will return `null` if `typeName` and `type` fields are null/empty and `instancePath` isn't null/empty
  - Sync fields support `instancePath` starting with a static member, in which case the `Type` is expected to be null

Updated `MpCompatPatchLoader`:
- Added a field (`FastInvokeHandler`) to call ISyncField registration method that accepts instance path
  - This includes code to setup that field in static constructor
  - Perhaps we could make the field public at some point, if that would be needed/helpful?
- Modified code setting up sync fields to support instance path, as well as handling the unexpected situation where the method to register them wasn't found/failed

A side note - we should open the ability to use `ISyncField`s with instance path using the MP API.

Another side note - if I remember correctly, it may be possible to simplify Cash Register compat (and potentially the 3 Corruption mods as well, but they weren't updated since 1.2, so... not worth it for them). I'll look into it in the future.
  • Loading branch information
SokyranTheDragon authored Sep 27, 2024
1 parent 215c945 commit 12a5e24
Showing 1 changed file with 38 additions and 8 deletions.
46 changes: 38 additions & 8 deletions Source/MpCompatAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ public class MpCompatRequireModAttribute : Attribute
public static class MpCompatPatchLoader
{
private static readonly MethodInfo RegisterSyncWorker;
private static readonly FastInvokeHandler RegisterSyncFieldWithInstancePath;

static MpCompatPatchLoader()
{
// Register sync worker method
RegisterSyncWorker =
typeof(MP)
.GetMethods(AccessTools.allDeclared)
Expand All @@ -58,7 +60,21 @@ static MpCompatPatchLoader()
});

if (RegisterSyncWorker == null)
Log.Error($"Failed retrieving {nameof(MP.RegisterSyncWorker)}");
Log.Error($"Retrieved null method: {nameof(MP)}.{nameof(MP.RegisterSyncWorker)}");

// Register sync field with instance path method
var registerSyncFieldMethod = AccessTools.DeclaredMethod("Multiplayer.Client.Sync:Field",
[typeof(Type), typeof(string), typeof(string)]);

const string syncFieldMethodName = "Multiplayer.Client.Sync:Field(Type, string, string)";
if (registerSyncFieldMethod == null)
Log.Error($"Retrieved null method: {syncFieldMethodName}");
else if (!registerSyncFieldMethod.IsStatic)
Log.Error($"Retrieved non-static method: {syncFieldMethodName}");
else if (!typeof(ISyncField).IsAssignableFrom(registerSyncFieldMethod.ReturnType))
Log.Error($"Retrieved method has incorrect return argument (expected subtype of {nameof(ISyncField)}, received {registerSyncFieldMethod.ReturnType}): {syncFieldMethodName}");
else
RegisterSyncFieldWithInstancePath = MethodInvoker.GetHandler(registerSyncFieldMethod);
}

public static void LoadPatch(object instance) => LoadPatch(instance?.GetType());
Expand Down Expand Up @@ -154,7 +170,14 @@ public static void LoadPatch(Type type)
if (!typeof(ISyncField).IsAssignableFrom(field.FieldType))
throw new Exception($"{syncFieldExceptionText} cannot assign object of type {nameof(ISyncField)} to the field (field type: {field.FieldType})");

var sync = MP.RegisterSyncField(attribute.Field);
ISyncField sync;
if (string.IsNullOrWhiteSpace(attribute.instancePath))
sync = MP.RegisterSyncField(attribute.Field);
else if (RegisterSyncFieldWithInstancePath == null)
continue;
else
sync = (ISyncField)RegisterSyncFieldWithInstancePath(null,
attribute.Type, attribute.instancePath, attribute.fieldName);

// It seems Context is unused in MP
if (attribute.cancelIfValueNull)
Expand Down Expand Up @@ -400,7 +423,6 @@ public class MpCompatSyncMethodAttribute : Attribute
[MeansImplicitUse(ImplicitUseKindFlags.Assign)]
public class MpCompatSyncFieldAttribute : Attribute
{
public SyncContext context;
public bool cancelIfValueNull;
public bool inGameLoop;
public bool bufferChanges = true;
Expand All @@ -410,7 +432,8 @@ public class MpCompatSyncFieldAttribute : Attribute

private Type type;
private string typeName;
private string fieldName;
public string fieldName;
public string instancePath;

private FieldInfo field;

Expand All @@ -420,6 +443,9 @@ public Type Type
{
if (type != null)
return type;
// Null type is supported with instance paths
if (!string.IsNullOrWhiteSpace(instancePath) && string.IsNullOrWhiteSpace(typeName))
return null;

type = AccessTools.TypeByName(typeName);
if (type == null)
Expand All @@ -444,19 +470,23 @@ public FieldInfo Field
}
}

protected MpCompatSyncFieldAttribute(SyncContext context = SyncContext.None) => this.context = context;

public MpCompatSyncFieldAttribute(string typeName, string fieldName, SyncContext context = SyncContext.None) : this(context)
public MpCompatSyncFieldAttribute(string typeName, string fieldName)
{
this.typeName = typeName;
this.fieldName = fieldName;
}

public MpCompatSyncFieldAttribute(Type type, string fieldName, SyncContext context = SyncContext.None) : this(context)
public MpCompatSyncFieldAttribute(Type type, string fieldName)
{
this.type = type;
this.fieldName = fieldName;
}

public MpCompatSyncFieldAttribute(string typeName, string instancePath, string fieldName) : this(typeName, fieldName)
=> this.instancePath = instancePath;

public MpCompatSyncFieldAttribute(Type type, string instancePath, string fieldName) : this(type, fieldName)
=> this.instancePath = instancePath;
}

[AttributeUsage(AttributeTargets.Method)]
Expand Down

0 comments on commit 12a5e24

Please sign in to comment.