Skip to content

Registering sync field thhrough API doesn't allow for specifying the exact type of the target #880

@SokyranTheDragon

Description

@SokyranTheDragon

Assuming a situation like this:

public class Base
{
    public int test;
}

public class Derived : Base
{ }

If we try to register a sync field through the API:

MP.RegisterSyncField(typeof(Derived), nameof(Derived.test));

we'll end up registering the sync field for Base class, rather than Derived. Which means that if we try watching changes:

public class SomeClass
{
    public Derived derived = new Derived();
    public ISyncField derivedSync = MP.RegisterSyncField(typeof(Derived), nameof(Derived.test));

    public void DoStuff()
    {
        // Start watching
        MP.WatchBehin();
        derivedSync.Watch();

        // Change watched value
        derived.test++;

        // Stop watching
        MP.WatchEnd();
    }
}

this will try syncing the changes as Base, rather than Derived. This happens due to the way registering happens in MP.

First, RegisterSyncField calls Sync.RegisterSyncField:

public ISyncField RegisterSyncField(Type targetType, string memberPath)
{
return Sync.RegisterSyncField(targetType, memberPath);
}

which ends up calling its overload taking FieldInfo:

public static ISyncField RegisterSyncField(Type targetType, string fieldName)
{
return RegisterSyncField(AccessTools.Field(targetType, fieldName));
}

and the overload is grabbing the type using FieldInfo.ReflectedType property:

public static ISyncField RegisterSyncField(FieldInfo field)
{
string memberPath = field.ReflectedType + "/" + field.Name;
SyncField sf;
if (field.IsStatic) {
sf = Field(null, null, memberPath);
} else {
sf = Field(field.ReflectedType, null, field.Name);
}
registeredSyncFields.Add(memberPath, sf);
return sf;
}

The issue here is that, as far as I can tell from my testing, FieldInfo.ReflectedType will always return the type that this field is declared in, not the type that we used to get the FieldInfo from. Which means that, in the situations above, registering the sync field for Derived.test field will read FieldInfo.ReflectedType as Base, not Derived.

And, because of this, the targetType will be Base, this when syncing the field MP will attempt to sync the Derived class as Base class:

if (targetType != null)
{
SyncSerialization.WriteSyncObject(writer, target, targetType);
if (context.map != null)
mapId = context.map.uniqueID;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    1.6Fixes or bugs relating to 1.6 (Not Odyssey).bugSomething isn't working.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions