Exemplo n.º 1
0
    /// <summary>
    /// (re)Run the scripts collected thus far
    /// </summary>
    public static Assembly CompileScripts()
    {
        // compile
        MonoScript.Unload(oldScripts);
        var scriptass = Script.Evaluator.StaticCompile(GetSources(), "s_");

        oldScripts = scriptass;
        Dictionary <string, List <MethodInfo> > broadcast = new Dictionary <string, List <MethodInfo> >();

        // populate the base dispatch list
        foreach (var t in typeof(ScriptEvents).GetMethods(BindingFlags.Public | BindingFlags.Instance))
        {
            if (!broadcast.ContainsKey(t.Name))
            {
                broadcast[t.Name] = new List <MethodInfo>();
            }
        }

        var dispatcher = new StringBuilder();

        dispatcher.AppendLine("using System.Collections.Generic;");
        dispatcher.AppendLine("public class ScriptDispatcher : ScriptEvents {");

        // collect the methods we'll broadcast to
        foreach (var t in scriptass.GetTypesSafe())
        {
            if (t.IsAbstract || t.IsInterface || (!t.IsPublic))
            {
                Debug.Log($"[SCRIPT] Skipping {t.Name} because it is abstract {t.IsAbstract} {t.IsInterface} {t.IsPublic} {t.BaseType?.Name ?? "no base"} {t?.BaseType?.BaseType?.Name ?? "no 2base"}");
                continue;
            }
            for (var bt = t.BaseType; bt != null && bt != typeof(Object); bt = bt.BaseType)
            {
                if (bt.Name == "ScriptEvents")
                {
                    goto good;
                }
            }
            continue;
good:
            foreach (var m in t.GetMethods(BindingFlags.Public | BindingFlags.Instance))
            {
                if (!broadcast.ContainsKey(m.Name))
                {
                    continue;
                }
                if (m.DeclaringType != t)
                {
                    continue;
                }
                broadcast[m.Name].Add(m);
            }
            // populate the instances while at it.
            dispatcher.AppendLine($"public {t.Name} {t.Name}_instance = new {t.Name}();");
        }

        // sort em according to broadcast priority
        foreach (var kv in broadcast)
        {
            kv.Value.Sort((a, b) =>
            {
                var attra = new Prio(0);
                var attrb = new Prio(0);
                a.GetAttr(ref attra);
                b.GetAttr(ref attrb);
                return(attrb.prio - attra.prio);
            });
        }

        // Now construct the dispatcher
        foreach (var kv in broadcast)
        {
            if (kv.Value.Count == 0)
            {
                continue;
            }
            var m = typeof(ScriptEvents).GetMethod(kv.Key);
            dispatcher.AppendLine("override " + m.GetSignature().Replace("&", "").Replace("+", "."));
            dispatcher.AppendLine("{");
            // early exit?
            // TODO: support enumerators
            var isexit = m.ReturnType == typeof(bool);
            if (isexit)
            {
                dispatcher.Append("return ");
            }
            // construct the call
            foreach (var fun in kv.Value)
            {
                dispatcher.Append(fun.DeclaringType.Name + "_instance." + fun.GetSignature(true).Replace("&", ""));
                if (isexit && kv.Value.Last() != fun)
                {
                    dispatcher.Append("||");
                }
                else
                {
                    dispatcher.AppendLine(";");
                }
            }
            dispatcher.AppendLine("}");
        }
        dispatcher.AppendLine("}");
        dispatcher.AppendLine("//" + scriptass.FullName);
        var dstr = dispatcher.ToString();

        Debug.Log("Compiled dispatch: " + dstr);
        // compile this mess and instantiate the dispatcher
        var dispAss = Script.Evaluator.StaticCompile(new object[] { /*scriptass, */ dstr.ToBytes() }, "d_");

        Script.On?.OnDestroy();
        Script.On = Activator.CreateInstance(dispAss.GetTypesSafe().First(x => x.Name == "ScriptDispatcher")) as ScriptEvents;
        // if there is a GO already in place, we'll have to simulate start and awake
        if (MBProxy.go != null)
        {
            Script.On.Awake();
            Script.On.Start();
        }
        var newlist = new Dictionary <MonoBehaviour, ScriptEvents>();

        foreach (var kv in SingletonList.singletons)
        {
            if (kv.Value != Script.On)
            {
                Script.On.OnSingleton(kv.Key, false);
            }
            newlist[kv.Key] = Script.On;
        }
        SingletonList.singletons = newlist;
        MonoScript.Unload(dispAss);
        return(scriptass);
    }