[SyncVar] GameObjects are now 100% based on their underlying netId field. Fixes #149 (#292)

* fix

* Test file

* Syntax

* NetworkBehaviour.Set/GetSyncVarNetworkIdentity functions added

* add definitions to weaver

* Use them

* add NetworkIdentity cases everywhere else too

* ProcessInstructionSetter/GetterField replaces for OnDeserialize too, so that [SyncVar]GameObjects/NetworkIdentities work with custom On(De)Serialize as well

* Custom on(De)serialize test

* Weaver.ProcessSiteMethod doesn't ignore OnDeserialize anymore either

* [SyncVar] GameObject/NetworkIdentity hooks work again

* testscene

* public gameobject field test

* pass gameobject field to getsyncvargameobject etc.

* GetSyncVarGameObject/NetworkIdentity always returns the field if server, looks up netId otherwise; functions aren't static anymore.

* Use original OnSerialize generation again, so it simply calls writer.Write(go) on server, which sends the netId internally anyway.

* fixed hooks not using 'this.' because getter function isn't static anymore.

* add scene referencing test

* remove tests
This commit is contained in:
vis2k 2019-01-14 23:39:29 +01:00 committed by GitHub
parent 3c0ff33c94
commit 43baeff444
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 231 additions and 103 deletions

View File

@ -276,39 +276,99 @@ internal bool InvokeHandlerDelegate(int cmdHash, UNetInvokeType invokeType, Netw
// ----------------------------- Helpers -------------------------------- // ----------------------------- Helpers --------------------------------
// helper function for [SyncVar] GameObjects.
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, ulong dirtyBit, ref uint netIdField) protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, ulong dirtyBit, ref uint netIdField)
{ {
if (m_SyncVarGuard) if (m_SyncVarGuard)
return; return;
uint newGameObjectNetId = 0; uint newNetId = 0;
if (newGameObject != null) if (newGameObject != null)
{ {
NetworkIdentity identity = newGameObject.GetComponent<NetworkIdentity>(); NetworkIdentity identity = newGameObject.GetComponent<NetworkIdentity>();
if (identity != null) if (identity != null)
{ {
newGameObjectNetId = identity.netId; newNetId = identity.netId;
if (newGameObjectNetId == 0) if (newNetId == 0)
{ {
Debug.LogWarning("SetSyncVarGameObject GameObject " + newGameObject + " has a zero netId. Maybe it is not spawned yet?"); Debug.LogWarning("SetSyncVarGameObject GameObject " + newGameObject + " has a zero netId. Maybe it is not spawned yet?");
} }
} }
} }
uint oldGameObjectNetId = 0; // netId changed?
if (gameObjectField != null) if (newNetId != netIdField)
{ {
oldGameObjectNetId = gameObjectField.GetComponent<NetworkIdentity>().netId; if (LogFilter.Debug) { Debug.Log("SetSyncVar GameObject " + GetType().Name + " bit [" + dirtyBit + "] netfieldId:" + netIdField + "->" + newNetId); }
SetDirtyBit(dirtyBit);
gameObjectField = newGameObject; // assign new one on the server, and in case we ever need it on client too
netIdField = newNetId;
}
}
// helper function for [SyncVar] GameObjects.
// -> ref GameObject as second argument makes OnDeserialize processing easier
[EditorBrowsable(EditorBrowsableState.Never)]
protected GameObject GetSyncVarGameObject(uint netId, ref GameObject gameObjectField)
{
// server always uses the field
if (isServer)
{
return gameObjectField;
} }
if (newGameObjectNetId != oldGameObjectNetId) // client always looks up based on netId because objects might get in and out of range
// over and over again, which shouldn't null them forever
NetworkIdentity identity;
if (NetworkIdentity.spawned.TryGetValue(netId, out identity) && identity != null)
return identity.gameObject;
return null;
}
// helper function for [SyncVar] NetworkIdentities.
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SetSyncVarNetworkIdentity(NetworkIdentity newIdentity, ref NetworkIdentity identityField, ulong dirtyBit, ref uint netIdField)
{
if (m_SyncVarGuard)
return;
uint newNetId = 0;
if (newIdentity != null)
{ {
if (LogFilter.Debug) { Debug.Log("SetSyncVar GameObject " + GetType().Name + " bit [" + dirtyBit + "] netfieldId:" + oldGameObjectNetId + "->" + newGameObjectNetId); } newNetId = newIdentity.netId;
SetDirtyBit(dirtyBit); if (newNetId == 0)
gameObjectField = newGameObject; {
netIdField = newGameObjectNetId; Debug.LogWarning("SetSyncVarNetworkIdentity NetworkIdentity " + newIdentity + " has a zero netId. Maybe it is not spawned yet?");
}
} }
// netId changed?
if (newNetId != netIdField)
{
if (LogFilter.Debug) { Debug.Log("SetSyncVarNetworkIdentity NetworkIdentity " + GetType().Name + " bit [" + dirtyBit + "] netIdField:" + netIdField + "->" + newNetId); }
SetDirtyBit(dirtyBit);
netIdField = newNetId;
identityField = newIdentity; // assign new one on the server, and in case we ever need it on client too
}
}
// helper function for [SyncVar] NetworkIdentities.
// -> ref GameObject as second argument makes OnDeserialize processing easier
[EditorBrowsable(EditorBrowsableState.Never)]
protected NetworkIdentity GetSyncVarNetworkIdentity(uint netId, ref NetworkIdentity identityField)
{
// server always uses the field
if (isServer)
{
return identityField;
}
// client always looks up based on netId because objects might get in and out of range
// over and over again, which shouldn't null them forever
NetworkIdentity identity;
NetworkIdentity.spawned.TryGetValue(netId, out identity);
return identity;
} }
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
@ -440,7 +500,6 @@ private void DeSerializeObjectsDelta(NetworkReader reader)
} }
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public virtual void PreStartClient() {}
public virtual void OnNetworkDestroy() {} public virtual void OnNetworkDestroy() {}
public virtual void OnStartServer() {} public virtual void OnStartServer() {}
public virtual void OnStartClient() {} public virtual void OnStartClient() {}

View File

@ -363,7 +363,6 @@ internal void OnStartClient()
{ {
try try
{ {
comp.PreStartClient(); // generated startup to resolve object references
comp.OnStartClient(); // user implemented startup comp.OnStartClient(); // user implemented startup
} }
catch (Exception e) catch (Exception e)

View File

@ -63,7 +63,6 @@ public void Process()
} }
GenerateDeSerialization(); GenerateDeSerialization();
GeneratePreStartClient();
Weaver.DLog(m_td, "Process Done"); Weaver.DLog(m_td, "Process Done");
} }
@ -447,66 +446,9 @@ public static int GetChannelId(CustomAttribute ca)
return 0; return 0;
} }
void GeneratePreStartClient()
{
int netIdFieldCounter = 0;
MethodDefinition preStartMethod = null;
ILProcessor serWorker = null;
foreach (var m in m_td.Methods)
{
if (m.Name == "PreStartClient")
return;
}
foreach (FieldDefinition syncVar in m_SyncVars)
{
if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName)
{
if (preStartMethod == null)
{
preStartMethod = new MethodDefinition("PreStartClient", MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.HideBySig,
Weaver.voidType);
serWorker = preStartMethod.Body.GetILProcessor();
}
FieldDefinition netIdField = m_SyncVarNetIds[netIdFieldCounter];
netIdFieldCounter += 1;
// Generates: if (!_crateNetId.IsEmpty()) { crate = ClientScene.FindLocalObject(_crateNetId); }
Instruction nullLabel = serWorker.Create(OpCodes.Nop);
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldfld, netIdField));
serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_0));
serWorker.Append(serWorker.Create(OpCodes.Ceq));
serWorker.Append(serWorker.Create(OpCodes.Brtrue, nullLabel));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldfld, netIdField));
serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.FindLocalObjectReference));
// return value of FindLocalObjectReference is on stack, assign it to the syncvar
serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
// Generates: end crateNetId != 0
serWorker.Append(nullLabel);
}
}
if (preStartMethod != null)
{
serWorker.Append(serWorker.Create(OpCodes.Ret));
m_td.Methods.Add(preStartMethod);
}
}
void GenerateDeSerialization() void GenerateDeSerialization()
{ {
Weaver.DLog(m_td, " GenerateDeSerialization"); Weaver.DLog(m_td, " GenerateDeSerialization");
int netIdFieldCounter = 0;
foreach (var m in m_td.Methods) foreach (var m in m_td.Methods)
{ {
@ -544,15 +486,21 @@ void GenerateDeSerialization()
serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2));
serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel));
int netIdFieldCounter = 0;
foreach (var syncVar in m_SyncVars) foreach (var syncVar in m_SyncVars)
{ {
// assign value // assign value
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName) if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
{ {
// GameObject SyncVar - assign to generated netId var // GameObject/NetworkIdentity SyncVar:
// OnSerialize sends writer.Write(go);
// OnDeserialize reads to __netId manually so we can use
// lookups in the getter (so it still works if objects
// move in and out of range repeatedly)
FieldDefinition netIdField = m_SyncVarNetIds[netIdFieldCounter]; FieldDefinition netIdField = m_SyncVarNetIds[netIdFieldCounter];
netIdFieldCounter += 1; netIdFieldCounter += 1;
@ -592,6 +540,7 @@ void GenerateDeSerialization()
serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); serWorker.Append(serWorker.Create(OpCodes.Stloc_0));
// conditionally read each syncvar // conditionally read each syncvar
netIdFieldCounter = 0; // reset
int dirtyBit = Weaver.GetSyncVarStart(m_td.BaseType.FullName); // start at number of syncvars in parent int dirtyBit = Weaver.GetSyncVarStart(m_td.BaseType.FullName); // start at number of syncvars in parent
foreach (FieldDefinition syncVar in m_SyncVars) foreach (FieldDefinition syncVar in m_SyncVars)
{ {
@ -603,14 +552,6 @@ void GenerateDeSerialization()
serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.And));
serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel));
MethodReference readFunc = Weaver.GetReadFunc(syncVar.FieldType);
if (readFunc == null)
{
Log.Error("GenerateDeSerialization for " + m_td.Name + " unknown type [" + syncVar.FieldType + "]. UNet [SyncVar] member variables must be basic types.");
Weaver.fail = true;
return;
}
// check for Hook function // check for Hook function
MethodDefinition foundMethod; MethodDefinition foundMethod;
if (!SyncVarProcessor.CheckForHookFunction(m_td, syncVar, out foundMethod)) if (!SyncVarProcessor.CheckForHookFunction(m_td, syncVar, out foundMethod))
@ -618,21 +559,67 @@ void GenerateDeSerialization()
return; return;
} }
if (foundMethod == null) if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName ||
syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
{ {
// just assign value // GameObject/NetworkIdentity SyncVar:
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // OnSerialize sends writer.Write(go);
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // OnDeserialize reads to __netId manually so we can use
serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); // lookups in the getter (so it still works if objects
serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); // move in and out of range repeatedly)
FieldDefinition netIdField = m_SyncVarNetIds[netIdFieldCounter];
netIdFieldCounter += 1;
if (foundMethod == null)
{
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField));
}
else
{
// call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32()))
// because we send/receive the netID, not the GameObject/NetworkIdentity
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this.
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.NetworkReaderReadPacked32));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar));
if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName)
serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarGameObjectReference));
else if (syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
serWorker.Append(serWorker.Create(OpCodes.Callvirt, Weaver.getSyncVarNetworkIdentityReference));
serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
}
} }
else else
{ {
// call hook instead MethodReference readFunc = Weaver.GetReadFunc(syncVar.FieldType);
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); if (readFunc == null)
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); {
serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); Log.Error("GenerateDeSerialization for " + m_td.Name + " unknown type [" + syncVar.FieldType + "]. UNet [SyncVar] member variables must be basic types.");
serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod)); Weaver.fail = true;
return;
}
if (foundMethod == null)
{
// just assign value
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar));
}
else
{
// call hook instead
serWorker.Append(serWorker.Create(OpCodes.Ldarg_0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Call, readFunc));
serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod));
}
} }
serWorker.Append(varLabel); serWorker.Append(varLabel);
dirtyBit += 1; dirtyBit += 1;

View File

@ -53,7 +53,7 @@ public static bool CheckForHookFunction(TypeDefinition td, FieldDefinition syncV
return true; return true;
} }
public static MethodDefinition ProcessSyncVarGet(FieldDefinition fd, string originalName) public static MethodDefinition ProcessSyncVarGet(FieldDefinition fd, string originalName, FieldDefinition netFieldId)
{ {
//Create the get method //Create the get method
MethodDefinition get = new MethodDefinition( MethodDefinition get = new MethodDefinition(
@ -64,9 +64,37 @@ public static MethodDefinition ProcessSyncVarGet(FieldDefinition fd, string orig
ILProcessor getWorker = get.Body.GetILProcessor(); ILProcessor getWorker = get.Body.GetILProcessor();
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); // [SyncVar] GameObject?
getWorker.Append(getWorker.Create(OpCodes.Ldfld, fd)); if (fd.FieldType.FullName == Weaver.gameObjectType.FullName)
getWorker.Append(getWorker.Create(OpCodes.Ret)); {
// return this.GetSyncVarGameObject(ref field, uint netId);
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); // this.
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
getWorker.Append(getWorker.Create(OpCodes.Ldfld, netFieldId));
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
getWorker.Append(getWorker.Create(OpCodes.Ldflda, fd));
getWorker.Append(getWorker.Create(OpCodes.Call, Weaver.getSyncVarGameObjectReference));
getWorker.Append(getWorker.Create(OpCodes.Ret));
}
// [SyncVar] NetworkIdentity?
else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
{
// return this.GetSyncVarNetworkIdentity(ref field, uint netId);
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); // this.
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
getWorker.Append(getWorker.Create(OpCodes.Ldfld, netFieldId));
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
getWorker.Append(getWorker.Create(OpCodes.Ldflda, fd));
getWorker.Append(getWorker.Create(OpCodes.Call, Weaver.getSyncVarNetworkIdentityReference));
getWorker.Append(getWorker.Create(OpCodes.Ret));
}
// [SyncVar] int, string, etc.
else
{
getWorker.Append(getWorker.Create(OpCodes.Ldarg_0));
getWorker.Append(getWorker.Create(OpCodes.Ldfld, fd));
getWorker.Append(getWorker.Create(OpCodes.Ret));
}
get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); get.Body.Variables.Add(new VariableDefinition(fd.FieldType));
get.Body.InitLocals = true; get.Body.InitLocals = true;
@ -137,6 +165,14 @@ public static MethodDefinition ProcessSyncVarSet(TypeDefinition td, FieldDefinit
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarGameObjectReference)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarGameObjectReference));
} }
else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
{
// reference to netId Field to set
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId));
setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarNetworkIdentityReference));
}
else else
{ {
// make generic version of SetSyncVar with field type // make generic version of SetSyncVar with field type
@ -158,12 +194,12 @@ public static MethodDefinition ProcessSyncVarSet(TypeDefinition td, FieldDefinit
public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, List<FieldDefinition> syncVarNetIds, long dirtyBit) public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, List<FieldDefinition> syncVarNetIds, long dirtyBit)
{ {
string originalName = fd.Name; string originalName = fd.Name;
Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType + " " + Weaver.gameObjectType); Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType + " " + Weaver.gameObjectType);
// GameObject SyncVars have a new field for netId // GameObject/NetworkIdentity SyncVars have a new field for netId
FieldDefinition netFieldId = null; FieldDefinition netFieldId = null;
if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) if (fd.FieldType.FullName == Weaver.gameObjectType.FullName ||
fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
{ {
netFieldId = new FieldDefinition("___" + fd.Name + "NetId", netFieldId = new FieldDefinition("___" + fd.Name + "NetId",
FieldAttributes.Private, FieldAttributes.Private,
@ -173,7 +209,7 @@ public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, List<Fi
Weaver.lists.netIdFields.Add(netFieldId); Weaver.lists.netIdFields.Add(netFieldId);
} }
var get = ProcessSyncVarGet(fd, originalName); var get = ProcessSyncVarGet(fd, originalName, netFieldId);
var set = ProcessSyncVarSet(td, fd, originalName, dirtyBit, netFieldId); var set = ProcessSyncVarSet(td, fd, originalName, dirtyBit, netFieldId);
//NOTE: is property even needed? Could just use a setter function? //NOTE: is property even needed? Could just use a setter function?
@ -188,6 +224,16 @@ public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, List<Fi
td.Methods.Add(set); td.Methods.Add(set);
td.Properties.Add(propertyDefinition); td.Properties.Add(propertyDefinition);
Weaver.lists.replacementSetterProperties[fd] = set; Weaver.lists.replacementSetterProperties[fd] = set;
// replace getter field if GameObject/NetworkIdentity so it uses
// netId instead
// -> only for GameObjects, otherwise an int syncvar's getter would
// end up in recursion.
if (fd.FieldType.FullName == Weaver.gameObjectType.FullName ||
fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName)
{
Weaver.lists.replacementGetterProperties[fd] = get;
}
} }
public static void ProcessSyncVars(TypeDefinition td, List<FieldDefinition> syncVars, List<FieldDefinition> syncObjects, List<FieldDefinition> syncVarNetIds) public static void ProcessSyncVars(TypeDefinition td, List<FieldDefinition> syncVars, List<FieldDefinition> syncObjects, List<FieldDefinition> syncVarNetIds)

View File

@ -15,6 +15,8 @@ class WeaverLists
{ {
// setter functions that replace [SyncVar] member variable references. dict<field, replacement> // setter functions that replace [SyncVar] member variable references. dict<field, replacement>
public Dictionary<FieldDefinition, MethodDefinition> replacementSetterProperties = new Dictionary<FieldDefinition, MethodDefinition>(); public Dictionary<FieldDefinition, MethodDefinition> replacementSetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
// getter functions that replace [SyncVar] member variable references. dict<field, replacement>
public Dictionary<FieldDefinition, MethodDefinition> replacementGetterProperties = new Dictionary<FieldDefinition, MethodDefinition>();
// GameObject SyncVar generated netId fields // GameObject SyncVar generated netId fields
public List<FieldDefinition> netIdFields = new List<FieldDefinition>(); public List<FieldDefinition> netIdFields = new List<FieldDefinition>();
@ -146,6 +148,9 @@ class Weaver
public static MethodReference setSyncVarHookGuard; public static MethodReference setSyncVarHookGuard;
public static MethodReference getSyncVarHookGuard; public static MethodReference getSyncVarHookGuard;
public static MethodReference setSyncVarGameObjectReference; public static MethodReference setSyncVarGameObjectReference;
public static MethodReference getSyncVarGameObjectReference;
public static MethodReference setSyncVarNetworkIdentityReference;
public static MethodReference getSyncVarNetworkIdentityReference;
public static MethodReference registerCommandDelegateReference; public static MethodReference registerCommandDelegateReference;
public static MethodReference registerRpcDelegateReference; public static MethodReference registerRpcDelegateReference;
public static MethodReference registerEventDelegateReference; public static MethodReference registerEventDelegateReference;
@ -742,10 +747,11 @@ static void ConfirmGeneratedCodeClass(ModuleDefinition moduleDef)
} }
} }
// replaces syncvar write access with the NetworkXYZ.get property calls
static void ProcessInstructionSetterField(TypeDefinition td, MethodDefinition md, Instruction i, FieldDefinition opField) static void ProcessInstructionSetterField(TypeDefinition td, MethodDefinition md, Instruction i, FieldDefinition opField)
{ {
// dont replace property call sites in constructors or deserialize // dont replace property call sites in constructors
if (md.Name == ".ctor" || md.Name == "OnDeserialize") if (md.Name == ".ctor")
return; return;
// does it set a field that we replaced? // does it set a field that we replaced?
@ -760,6 +766,25 @@ static void ProcessInstructionSetterField(TypeDefinition td, MethodDefinition md
} }
} }
// replaces syncvar read access with the NetworkXYZ.get property calls
static void ProcessInstructionGetterField(TypeDefinition td, MethodDefinition md, Instruction i, FieldDefinition opField)
{
// dont replace property call sites in constructors
if (md.Name == ".ctor")
return;
// does it set a field that we replaced?
MethodDefinition replacement;
if (lists.replacementGetterProperties.TryGetValue(opField, out replacement))
{
//replace with property
//DLog(td, " replacing " + md.Name + ":" + i);
i.OpCode = OpCodes.Call;
i.Operand = replacement;
//DLog(td, " replaced " + md.Name + ":" + i);
}
}
static void ProcessInstruction(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, Instruction i, int iCount) static void ProcessInstruction(ModuleDefinition moduleDef, TypeDefinition td, MethodDefinition md, Instruction i, int iCount)
{ {
if (i.OpCode == OpCodes.Call || i.OpCode == OpCodes.Callvirt) if (i.OpCode == OpCodes.Call || i.OpCode == OpCodes.Callvirt)
@ -780,6 +805,16 @@ static void ProcessInstruction(ModuleDefinition moduleDef, TypeDefinition td, Me
ProcessInstructionSetterField(td, md, i, opField); ProcessInstructionSetterField(td, md, i, opField);
} }
} }
if (i.OpCode == OpCodes.Ldfld)
{
// this instruction gets the value of a field. cache the field reference.
FieldDefinition opField = i.Operand as FieldDefinition;
if (opField != null)
{
ProcessInstructionGetterField(td, md, i, opField);
}
}
} }
// this is required to early-out from a function with "ref" or "out" parameters // this is required to early-out from a function with "ref" or "out" parameters
@ -885,7 +920,6 @@ static void ProcessSiteMethod(ModuleDefinition moduleDef, TypeDefinition td, Met
//Weaver.DLog(td, " ProcessSiteMethod " + md); //Weaver.DLog(td, " ProcessSiteMethod " + md);
if (md.Name == ".cctor" || if (md.Name == ".cctor" ||
md.Name == "OnDeserialize" ||
md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || md.Name == NetworkBehaviourProcessor.ProcessedFunctionName ||
md.Name.StartsWith("CallCmd") || md.Name.StartsWith("CallCmd") ||
md.Name.StartsWith("InvokeCmd") || md.Name.StartsWith("InvokeCmd") ||
@ -1129,6 +1163,9 @@ static void SetupTargetTypes()
getSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "get_syncVarHookGuard"); getSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "get_syncVarHookGuard");
setSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "SetSyncVarGameObject"); setSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "SetSyncVarGameObject");
getSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "GetSyncVarGameObject");
setSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "SetSyncVarNetworkIdentity");
getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "GetSyncVarNetworkIdentity");
registerCommandDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "RegisterCommandDelegate"); registerCommandDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "RegisterCommandDelegate");
registerRpcDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "RegisterRpcDelegate"); registerRpcDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "RegisterRpcDelegate");
registerEventDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "RegisterEventDelegate"); registerEventDelegateReference = Resolvers.ResolveMethod(NetworkBehaviourType, scriptDef, "RegisterEventDelegate");