/// <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); }