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

Updates and additions to API #381

Merged
merged 10 commits into from
Dec 27, 2023
33 changes: 33 additions & 0 deletions Source/Client/MultiplayerAPIBridge.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Reflection;
using HarmonyLib;
using Multiplayer.API;
using Multiplayer.Client;
using Multiplayer.Client.Patches;

// ReSharper disable once CheckNamespace
namespace Multiplayer.Common
Expand All @@ -21,6 +23,10 @@

public bool IsExecutingSyncCommandIssuedBySelf => TickPatch.currentExecutingCmdIssuedBySelf;

public bool CanUseDevMode => Client.Multiplayer.GameComp.LocalPlayerDataOrNull?.canUseDevMode ?? false;

public bool InInterface => Client.Multiplayer.InInterface;

public void WatchBegin()
{
SyncFieldUtil.FieldWatchPrefix();
Expand Down Expand Up @@ -90,6 +96,16 @@
return Sync.RegisterSyncMethod(method, argTypes);
}

public ISyncMethod RegisterSyncMethodLambda(Type parentType, string parentMethod, int lambdaOrdinal, Type[] parentArgs = null, ParentMethodType parentMethodType = ParentMethodType.Normal)

Check failure on line 99 in Source/Client/MultiplayerAPIBridge.cs

View workflow job for this annotation

GitHub Actions / Builds

The type or namespace name 'ParentMethodType' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 99 in Source/Client/MultiplayerAPIBridge.cs

View workflow job for this annotation

GitHub Actions / Builds

The name 'ParentMethodType' does not exist in the current context
{
return SyncMethod.Lambda(parentType, parentMethod, lambdaOrdinal, parentArgs, (MethodType)parentMethodType);
}

public ISyncMethod RegisterSyncMethodLambdaInGetter(Type parentType, string parentMethod, int lambdaOrdinal)
{
return SyncMethod.LambdaInGetter(parentType, parentMethod, lambdaOrdinal);
}

public ISyncDelegate RegisterSyncDelegate(Type inType, string nestedType, string methodName, string[] fields, Type[] args = null)
{
return Sync.RegisterSyncDelegate(inType, nestedType, methodName, fields, args);
Expand All @@ -100,6 +116,21 @@
return Sync.RegisterSyncDelegate(type, nestedType, method);
}

public ISyncDelegate RegisterSyncDelegateLambda(Type parentType, string parentMethod, int lambdaOrdinal, Type[] parentArgs = null, ParentMethodType parentMethodType = ParentMethodType.Normal)

Check failure on line 119 in Source/Client/MultiplayerAPIBridge.cs

View workflow job for this annotation

GitHub Actions / Builds

The type or namespace name 'ParentMethodType' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 119 in Source/Client/MultiplayerAPIBridge.cs

View workflow job for this annotation

GitHub Actions / Builds

The name 'ParentMethodType' does not exist in the current context
{
return SyncDelegate.Lambda(parentType, parentMethod, lambdaOrdinal, parentArgs, (MethodType)parentMethodType);
}

public ISyncDelegate RegisterSyncDelegateLambdaInGetter(Type parentType, string parentMethod, int lambdaOrdinal)
{
return SyncDelegate.LambdaInGetter(parentType, parentMethod, lambdaOrdinal);
}

public ISyncDelegate RegisterSyncDelegateLocalFunc(Type parentType, string parentMethod, string localFuncName, Type[] parentArgs = null)
{
return SyncDelegate.LocalFunc(parentType, parentMethod, localFuncName, parentArgs);
}

public void RegisterSyncWorker<T>(SyncWorkerDelegate<T> syncWorkerDelegate, Type targetType = null, bool isImplicit = false, bool shouldConstruct = false)
{
Sync.RegisterSyncWorker(syncWorkerDelegate, targetType, isImplicit: isImplicit, shouldConstruct: shouldConstruct);
Expand All @@ -119,5 +150,7 @@
{
AsyncTimeComp.pauseLocks.Add(pauseLock);
}

public void RegisterDefaultLetterChoice(MethodInfo method, Type letterType = null) => CloseDialogsForExpiredLetters.RegisterDefaultLetterChoice(method, letterType);
}
}
67 changes: 61 additions & 6 deletions Source/Client/Syncing/Handler/SyncDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
private string[] allowedNull;
private string[] cancelIfNull;
private string[] removeNullsFromLists;
private string[] exposeFields;

public SyncDelegate(Type delegateType, MethodInfo method, string[] inPaths) :
base(delegateType, null, method, null)
Expand Down Expand Up @@ -64,13 +65,18 @@
for (int i = 0; i < fieldPaths.Length; i++)
{
var val = target.GetPropertyOrField(fieldPaths[i]);
var type = fieldTypes[i];
var path = fieldPaths[i];

if (fieldTransformers[i] is SyncTransformer tr)
writer(tr.Writer.DynamicInvoke(val, target, args), tr.NetworkType, path);
else if (!fieldTypes[i].IsCompilerGenerated())
{
var type = (SyncType)fieldTypes[i];
if (exposeFields != null && exposeFields.Contains(path))
type.expose = true;

writer(val, type, path);
}
}
}

Expand Down Expand Up @@ -129,14 +135,14 @@
return this;
}

public ISyncMethod TransformField<Live, Networked>(string field, Serializer<Live, Networked> serializer)
public ISyncDelegate TransformField<Live, Networked>(string field, Serializer<Live, Networked> serializer, bool skipTypeCheck = false)

Check failure on line 138 in Source/Client/Syncing/Handler/SyncDelegate.cs

View workflow job for this annotation

GitHub Actions / Builds

The type or namespace name 'Serializer<,>' could not be found (are you missing a using directive or an assembly reference?)
{
CheckFieldsExist(field);

var index = fieldPathsNoTypes.FindIndex(field);

if (fieldTypes[index] != typeof(Live))
throw new Exception($"Arg transformer param mismatch for {this}: {fieldTypes[index]} != {typeof(Live)}");
if (!skipTypeCheck && fieldTypes[index] != typeof(Live))
throw new Exception($"Field transformer type mismatch for {this}: {fieldTypes[index]} != {typeof(Live)}");

fieldTransformers[index] = new(typeof(Live), typeof(Networked), serializer.Writer, serializer.Reader);
return this;
Expand All @@ -149,15 +155,15 @@
throw new Exception($"Field with path {f} not found");
}

public static SyncDelegate Lambda(Type parentType, string parentMethod, int lambdaOrdinal, Type[] parentArgs = null, MethodType parentMethodType = MethodType.Normal)
public new static SyncDelegate Lambda(Type parentType, string parentMethod, int lambdaOrdinal, Type[] parentArgs = null, MethodType parentMethodType = MethodType.Normal)
{
return Sync.RegisterSyncDelegate(
MpMethodUtil.GetLambda(parentType, parentMethod, parentMethodType, parentArgs, lambdaOrdinal),
null
);
}

public static SyncDelegate LambdaInGetter(Type parentType, string parentMethod, int lambdaOrdinal)
public new static SyncDelegate LambdaInGetter(Type parentType, string parentMethod, int lambdaOrdinal)
{
return Sync.RegisterSyncDelegate(
MpMethodUtil.GetLambda(parentType, parentMethod, MethodType.Getter, null, lambdaOrdinal),
Expand Down Expand Up @@ -221,6 +227,13 @@
return this;
}

public ISyncDelegate ExposeFields(params string[] fields)
{
CheckFieldsExist(fields);
exposeFields = fields;
return this;
}

ISyncDelegate ISyncDelegate.SetContext(SyncContext context)
{
SetContext(context);
Expand All @@ -232,6 +245,48 @@
SetDebugOnly();
return this;
}

ISyncDelegate ISyncDelegate.SetHostOnly()

Check failure on line 249 in Source/Client/Syncing/Handler/SyncDelegate.cs

View workflow job for this annotation

GitHub Actions / Builds

'SyncDelegate.SetHostOnly()' in explicit interface declaration is not found among members of the interface that can be implemented
{
SetHostOnly();
return this;
}

ISyncDelegate ISyncDelegate.SetPreInvoke(Action<object, object[]> action)

Check failure on line 255 in Source/Client/Syncing/Handler/SyncDelegate.cs

View workflow job for this annotation

GitHub Actions / Builds

'SyncDelegate.SetPreInvoke(Action<object, object[]>)' in explicit interface declaration is not found among members of the interface that can be implemented
{
SetPreInvoke(action);
return this;
}

ISyncDelegate ISyncDelegate.SetPostInvoke(Action<object, object[]> action)

Check failure on line 261 in Source/Client/Syncing/Handler/SyncDelegate.cs

View workflow job for this annotation

GitHub Actions / Builds

'SyncDelegate.SetPostInvoke(Action<object, object[]>)' in explicit interface declaration is not found among members of the interface that can be implemented
{
SetPostInvoke(action);
return this;
}

ISyncDelegate ISyncDelegate.TransformArgument<Live, Networked>(int index, Serializer<Live, Networked> serializer, bool skipTypeCheck)

Check failure on line 267 in Source/Client/Syncing/Handler/SyncDelegate.cs

View workflow job for this annotation

GitHub Actions / Builds

The type or namespace name 'Serializer<,>' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 267 in Source/Client/Syncing/Handler/SyncDelegate.cs

View workflow job for this annotation

GitHub Actions / Builds

'SyncDelegate.TransformArgument<Live, Networked>(int, Serializer<Live, Networked>, bool)' in explicit interface declaration is not found among members of the interface that can be implemented
{
TransformArgument(index, serializer, skipTypeCheck);
return this;
}

ISyncDelegate ISyncDelegate.TransformTarget<Live, Networked>(Serializer<Live, Networked> serializer, bool skipTypeCheck)
{
TransformTarget(serializer, skipTypeCheck);
return this;
}

ISyncDelegate ISyncDelegate.CancelIfNoSelectedMapObjects()
{
CancelIfNoSelectedMapObjects();
return this;
}

ISyncDelegate ISyncDelegate.CancelIfNoSelectedWorldObjects()
{
CancelIfNoSelectedWorldObjects();
return this;
}
}

}
47 changes: 12 additions & 35 deletions Source/Client/Syncing/Handler/SyncMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,6 @@

namespace Multiplayer.Client
{
public record Serializer<Live, Networked>(
Func<Live, object, object[], Networked> Writer, // (live, target, args) => networked
Func<Networked, Live> Reader // (networked) => live
);

public static class Serializer
{
public static Serializer<Live, Networked> New<Live, Networked>(Func<Live, object, object[], Networked> writer, Func<Networked, Live> reader)
{
return new(writer, reader);
}

public static Serializer<Live, Networked> New<Live, Networked>(Func<Live, Networked> writer, Func<Networked, Live> reader)
{
return new((live, _, _) => writer(live), reader);
}

public static Serializer<Live, object> SimpleReader<Live>(Func<Live> reader)
{
return new((_, _, _) => null, _ => reader());
}
}

public record SyncTransformer(Type LiveType, Type NetworkType, Delegate Writer, Delegate Reader);

public delegate void SyncMethodWriter(object obj, SyncType type, string debugInfo);
Expand Down Expand Up @@ -234,6 +211,12 @@ public ISyncMethod SetDebugOnly()
return this;
}

public ISyncMethod SetHostOnly()
{
hostOnly = true;
return this;
}

public ISyncMethod SetPreInvoke(Action<object, object[]> action)
{
beforeCall = action;
Expand Down Expand Up @@ -270,39 +253,33 @@ public ISyncMethod ExposeParameter(int index)
return this;
}

public SyncMethod TransformArgument<Live, Networked>(int index, Serializer<Live, Networked> serializer)
public ISyncMethod TransformArgument<Live, Networked>(int index, Serializer<Live, Networked> serializer, bool skipTypeCheck = false)
{
if (argTypes[index].type != typeof(Live))
if (!skipTypeCheck && argTypes[index].type != typeof(Live))
throw new Exception($"Arg transformer type mismatch for {this}: {argTypes[index].type} != {typeof(Live)}");

argTransformers[index] = new(typeof(Live), typeof(Networked), serializer.Writer, serializer.Reader);
return this;
}

public SyncMethod TransformTarget<Live, Networked>(Serializer<Live, Networked> serializer)
public ISyncMethod TransformTarget<Live, Networked>(Serializer<Live, Networked> serializer, bool skipTypeCheck = false)
{
if (targetType != typeof(Live))
if (!skipTypeCheck && targetType != typeof(Live))
throw new Exception($"Target transformer type mismatch for {this}: {targetType} != {typeof(Live)}");

targetTransformer = new(typeof(Live), typeof(Networked), serializer.Writer, serializer.Reader);
return this;
}

public SyncMethod SetHostOnly()
{
hostOnly = true;
return this;
}

public static SyncMethod Register(Type type, string methodOrPropertyName, SyncType[] argTypes = null)
{
return Sync.RegisterSyncMethod(type, methodOrPropertyName, argTypes);
}

public static SyncMethod Lambda(Type parentType, string parentMethod, int lambdaOrdinal, Type[] parentArgs = null)
public static SyncMethod Lambda(Type parentType, string parentMethod, int lambdaOrdinal, Type[] parentArgs = null, MethodType parentMethodType = MethodType.Normal)
{
return Sync.RegisterSyncMethod(
MpMethodUtil.GetLambda(parentType, parentMethod, MethodType.Normal, parentArgs, lambdaOrdinal),
MpMethodUtil.GetLambda(parentType, parentMethod, parentMethodType, parentArgs, lambdaOrdinal),
null
);
}
Expand Down
5 changes: 5 additions & 0 deletions Source/Client/Util/MpMethodUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ public static MethodBase GetMethod(Type type, string methodName, MethodType meth
return AccessTools
.GetDeclaredConstructors(type)
.FirstOrDefault(c => c.IsStatic);

case MethodType.Enumerator:
if (methodName == null)
return null;
return AccessTools.EnumeratorMoveNext(AccessTools.DeclaredMethod(type, methodName, args));
}

return null;
Expand Down
Loading