简体   繁体   中英

C# RPC expose API methods

Ok, so far, I have a way to expose some methods using this:

AddCommand ("UpdateStar", ((args) => {
   // do something
}));

It adds the command into a SortedDictionary<string, SimpleDelegate> . Here's my SimpleDelegate definition:

public delegate void SimpleDelegate(JSONNode args);

It's pretty straight forward, from Javascript I send a list of argument and in C# I receive a JSONNode which is a JSONArray . It's working but it's quite annoying to do things such as:

string name = args[0].Value;
int x = args[1].AsInt;
...

In the end, what I'd like to do is to instead expose actual methods in C# instead of exposing lambdas.

In other words, I'd like to do something like this:

[expose-to-rpc]
public void AddSystem(string name, int x, int y) {

}

It could find the name of the method, the number of argument and the type of arguments using reflection. I'm pretty sure something like that is possible but I'm kind of lost here. Not sure how to get started.

Ah I got it. It was easier than I thought...

First, this class is required:

[AttributeUsage(AttributeTargets.Method)]
public class ExposeAttribute: Attribute {
}

Then we need to add this other class:

public static class RPCApi
{

    public static object nodeToType(JSONNode node, Type type) {
        if (typeof(int) == type) {
            return node.AsInt;
        }
        if (typeof(long) == type) {
            return node.AsLong;
        }
        if (typeof(bool) == type) {
            return node.AsBool;
        }
        if (typeof(string) == type) {
            return node.Value;
        }
        if (typeof(float) == type) {
            return node.AsFloat;
        }
        if (typeof(double) == type) {
            return node.AsDouble;
        }
        if (typeof(JSONArray) == type) {
            return node.AsArray;
        }
        if (typeof(JSONClass) == type) {
            return node.AsObject;
        }

        return null;
    }

    public static void Resolve(MonoBehaviour behaviour) {
        NetworkManager manager = behaviour.GetComponent<NetworkManager> ();
        Type t = behaviour.GetType ();
        MethodInfo[] methods = t.GetMethods ();

        for (int i = 0; i < methods.Length; i++) {
            MethodInfo meth = methods [i];
            ExposeAttribute[] atts = (ExposeAttribute[])meth.GetCustomAttributes (typeof(ExposeAttribute), true);

            if (atts.Length == 0) {
                continue;
            }

            ParameterInfo[] paramss = meth.GetParameters ();
            manager.AddCommand (meth.Name, ((args) => {
                object[] argss = new object[paramss.Length];
                for(int l=0; l<argss.Length; l++) {
                    argss[l] = nodeToType(
                      args[l],
                      paramss[l].ParameterType
                    );
                }
                meth.Invoke(behaviour, argss);
            }));
        }
    }
}

First we get all the methods of the object passed to Resolve. Check for any method with the attribute Expose.

If it's there, lookup the parameters type and create a new command as I was doing before... Thought before calling the actual method, we convert the arguments to the type that the method is expecting. Since we receive JSON, We cannot receive complex types so we can convert most parameters easily... That said, there might be a better way to that.

Finally, how to use it:

public class NetworkManager : MonoBehaviour {

    public void Start () {
        RPCApi.Resolve (this);
    }

    ...

    [Expose]
    public void addStar(string system) {
        Planet fun = JsonUtility.FromJson<Planet> (system);
        GameObject prefabs = GameObject.Find ("PrefabContainer");
        PrefabContainer obj = prefabs.GetComponent<PrefabContainer> ();
        GameObject newobj = Instantiate (obj.star, new Vector3 (fun.x, fun.y, 0), Quaternion.Euler (0, 0, 0));
        newobj.name = fun.id;
        newobj.GetComponent<PlanetBehaviour> ().planetConfig = fun;
    }

    [Expose]
    public void UpdateStar (string uuid, float x, float y) {
        GameObject system = GameObject.Find (uuid);
        MoveToBehaviour move = system.GetComponent<MoveToBehaviour>();
        move.MoveTo(new Vector3(x, y, 0));
    }
}

Adding the return type shouldn't be much harder either.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM