/// <summary> /// Merge object right into left recursively /// </summary> /// <param name="left">The left (base) object</param> /// <param name="right">The right (new) object</param> static void MergeRecur(JSONObject left, JSONObject right) { if(left.type == Type.NULL) left.Absorb(right); else if(left.type == Type.OBJECT && right.type == Type.OBJECT) { for(int i = 0; i < right.list.Count; i++) { string key = right.keys[i]; if(right[i].isContainer) { if(left.HasField(key)) MergeRecur(left[key], right[i]); else left.AddField(key, right[i]); } else { if(left.HasField(key)) left.SetField(key, right[i]); else left.AddField(key, right[i]); } } } else if(left.type == Type.ARRAY && right.type == Type.ARRAY) { if(right.Count > left.Count) { #if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 Debug.LogError #else Debug.WriteLine #endif ("Cannot merge arrays when right object has more elements"); return; } for(int i = 0; i < right.list.Count; i++) { if(left[i].type == right[i].type) { //Only overwrite with the same type if(left[i].isContainer) MergeRecur(left[i], right[i]); else { left[i] = right[i]; } } } } }
public void SetField(string name, JSONObject obj) { if(HasField(name)) { list.Remove(this[name]); keys.Remove(name); } AddField(name, obj); }
/* * The Merge function is experimental. Use at your own risk. */ public void Merge(JSONObject obj) { MergeRecur(this, obj); }
public void Add(JSONObject obj) { if(obj) { //Don't do anything if the object is null if(type != Type.ARRAY) { type = Type.ARRAY; //Congratulations, son, you're an ARRAY now if(list == null) list = new List<JSONObject>(); } list.Add(obj); } }
public void AddField(string name, JSONObject obj) { if(obj) { //Don't do anything if the object is null if(type != Type.OBJECT) { if(keys == null) keys = new List<string>(); if(type == Type.ARRAY) { for(int i = 0; i < list.Count; i++) keys.Add(i + ""); } else if(list == null) list = new List<JSONObject>(); type = Type.OBJECT; //Congratulations, son, you're an OBJECT now } keys.Add(name); list.Add(obj); } }
public JSONObject(JSONObject[] objs) { type = Type.ARRAY; list = new List<JSONObject>(objs); }
public void Absorb(JSONObject obj) { list.AddRange(obj.list); keys.AddRange(obj.keys); str = obj.str; n = obj.n; useInt = obj.useInt; i = obj.i; b = obj.b; type = obj.type; }
/// <summary> /// Register some commonly used Unity classes and objects for Lua interop. /// To register more class objects externally to this class, register them in the Awake method of any /// monobehavior in your scene. /// </summary> protected virtual void InitFungusModule() { if (fungusModule == FungusModuleOptions.NoFungusModule) { return; } MoonSharp.Interpreter.Script interpreter = luaEnvironment.Interpreter; // Require the Fungus module and assign it to the global 'fungus' Table fungusTable = null; MoonSharp.Interpreter.DynValue value = interpreter.RequireModule("fungus"); if (value != null && value.Type == DataType.Function) { fungusTable = value.Function.Call().Table; } if (fungusTable == null) { UnityEngine.Debug.LogError("Failed to create Fungus table"); return; } interpreter.Globals["fungus"] = fungusTable; // Static classes fungusTable["time"] = UserData.CreateStatic(typeof(Time)); fungusTable["playerprefs"] = UserData.CreateStatic(typeof(PlayerPrefs)); fungusTable["prefs"] = UserData.CreateStatic(typeof(FungusPrefs)); fungusTable["factory"] = UserData.CreateStatic(typeof(PODTypeFactory)); // Lua Environment and Lua Utils components fungusTable["luaenvironment"] = luaEnvironment; fungusTable["luautils"] = this; // Provide access to the Unity Test Tools (if available). Type testType = Type.GetType("IntegrationTest"); if (testType != null) { UserData.RegisterType(testType); fungusTable["test"] = UserData.CreateStatic(testType); } // Populate the string table by parsing the string table JSON files stringTable = new Table(interpreter); fungusTable["stringtable"] = stringTable; foreach (TextAsset stringFile in stringTables) { if (stringFile.text == "") { continue; } JSONObject stringsJSON = new JSONObject(stringFile.text); if (stringsJSON == null || stringsJSON.type != JSONObject.Type.OBJECT) { UnityEngine.Debug.LogError("String table JSON format is not correct " + stringFile.name); continue; } foreach (string stringKey in stringsJSON.keys) { if (stringKey == "") { UnityEngine.Debug.LogError("String table JSON format is not correct " + stringFile.name); continue; } Table entriesTable = new Table(interpreter); stringTable[stringKey] = entriesTable; JSONObject entries = stringsJSON.GetField(stringKey); if (entries.type != JSONObject.Type.OBJECT) { UnityEngine.Debug.LogError("String table JSON format is not correct " + stringFile.name); continue; } foreach (string language in entries.keys) { string translation = entries.GetField(language).str; entriesTable[language] = translation; } } } stringSubstituter = new StringSubstituter(); stringSubstituter.CacheSubstitutionHandlers(); #if !FUNGUSLUA_STANDALONE conversationManager = new ConversationManager(); conversationManager.PopulateCharacterCache(); #endif if (fungusModule == FungusModuleOptions.UseGlobalVariables) { // Copy all items from the Fungus table to global variables foreach (TablePair p in fungusTable.Pairs) { if (interpreter.Globals.Keys.Contains(p.Key)) { UnityEngine.Debug.LogError("Lua globals already contains a variable " + p.Key); } else { interpreter.Globals[p.Key] = p.Value; } } interpreter.Globals["fungus"] = DynValue.Nil; // Note: We can't remove the fungus table itself because of dependencies between functions } }
/// <summary> /// Registers all listed c# types for interop with Lua. /// You can also register types directly in the Awake method of any /// monobehavior in your scene using UserData.RegisterType(). /// </summary> protected virtual void InitTypes() { bool isFungusInstalled = (Type.GetType("Fungus.Flowchart") != null); // Always register these FungusLua utilities LuaEnvironment.RegisterType("Fungus.PODTypeFactory"); LuaEnvironment.RegisterType("Fungus.FungusPrefs"); foreach (TextAsset textFile in registerTypes) { if (textFile == null) { continue; } // Parse JSON file JSONObject jsonObject = new JSONObject(textFile.text); if (jsonObject == null || jsonObject.type != JSONObject.Type.OBJECT) { UnityEngine.Debug.LogError("Error parsing JSON file " + textFile.name); continue; } // Register types with MoonSharp JSONObject registerTypesArray = jsonObject.GetField("registerTypes"); if (registerTypesArray != null && registerTypesArray.type == JSONObject.Type.ARRAY) { foreach (JSONObject entry in registerTypesArray.list) { if (entry != null && entry.type == JSONObject.Type.STRING) { string typeName = entry.str.Trim(); // Don't register fungus types if the Fungus library is not present if (!isFungusInstalled && typeName.StartsWith("Fungus.")) { continue; } LuaEnvironment.RegisterType(typeName); } } } // Register extension types with MoonSharp JSONObject extensionTypesArray = jsonObject.GetField("extensionTypes"); if (extensionTypesArray != null && extensionTypesArray.type == JSONObject.Type.ARRAY) { foreach (JSONObject entry in extensionTypesArray.list) { if (entry != null && entry.type == JSONObject.Type.STRING) { string typeName = entry.str.Trim(); // Don't register fungus types if the Fungus library is not present if (!isFungusInstalled && typeName.StartsWith("Fungus.")) { continue; } LuaEnvironment.RegisterType(typeName, true); } } } } }
/// <summary> /// Register some commonly used Unity classes and objects for Lua interop. /// To register more class objects externally to this class, register them in the Awake method of any /// monobehavior in your scene. /// </summary> protected virtual void InitFungusModule() { if (fungusModule == FungusModuleOptions.NoFungusModule) { return; } MoonSharp.Interpreter.Script interpreter = luaEnvironment.Interpreter; // Require the Fungus module and assign it to the global 'fungus' Table fungusTable = null; MoonSharp.Interpreter.DynValue value = interpreter.RequireModule("fungus"); if (value != null && value.Type == DataType.Function) { fungusTable = value.Function.Call().Table; } if (fungusTable == null) { UnityEngine.Debug.LogError("Failed to create Fungus table"); return; } interpreter.Globals["fungus"] = fungusTable; // Static classes fungusTable["time"] = UserData.CreateStatic(typeof(Time)); fungusTable["playerprefs"] = UserData.CreateStatic(typeof(PlayerPrefs)); fungusTable["prefs"] = UserData.CreateStatic(typeof(FungusPrefs)); fungusTable["factory"] = UserData.CreateStatic(typeof(PODTypeFactory)); // Lua Environment and Lua Utils components fungusTable["luaenvironment"] = luaEnvironment; fungusTable["luautils"] = this; // Provide access to the Unity Test Tools (if available). Type testType = Type.GetType("IntegrationTest"); if (testType != null) { UserData.RegisterType(testType); fungusTable["test"] = UserData.CreateStatic(testType); } // Populate the string table by parsing the string table JSON files stringTable = new Table(interpreter); fungusTable["stringtable"] = stringTable; foreach (TextAsset stringFile in stringTables) { if (stringFile.text == "") { continue; } JSONObject stringsJSON = new JSONObject(stringFile.text); if (stringsJSON == null || stringsJSON.type != JSONObject.Type.OBJECT) { UnityEngine.Debug.LogError("String table JSON format is not correct " + stringFile.name); continue; } foreach (string stringKey in stringsJSON.keys) { if (stringKey == "") { UnityEngine.Debug.LogError("String table JSON format is not correct " + stringFile.name); continue; } Table entriesTable = new Table(interpreter); stringTable[stringKey] = entriesTable; JSONObject entries = stringsJSON.GetField(stringKey); if (entries.type != JSONObject.Type.OBJECT) { UnityEngine.Debug.LogError("String table JSON format is not correct " + stringFile.name); continue; } foreach (string language in entries.keys) { string translation = entries.GetField(language).str; entriesTable[language] = translation; } } } stringSubstituter = new StringSubstituter(); stringSubstituter.CacheSubstitutionHandlers(); conversationManager = new ConversationManager(); conversationManager.PopulateCharacterCache(); if (fungusModule == FungusModuleOptions.UseGlobalVariables) { // Copy all items from the Fungus table to global variables foreach (TablePair p in fungusTable.Pairs) { if (interpreter.Globals.Keys.Contains(p.Key)) { UnityEngine.Debug.LogError("Lua globals already contains a variable " + p.Key); } else { interpreter.Globals[p.Key] = p.Value; } } interpreter.Globals["fungus"] = DynValue.Nil; // Note: We can't remove the fungus table itself because of dependencies between functions } }
/// <summary> /// Merge object right into left recursively /// </summary> /// <param name="left">The left (base) object</param> /// <param name="right">The right (new) object</param> static void MergeRecur(JSONObject left, JSONObject right) { if (left.type == Type.NULL) { left.Absorb(right); } else if (left.type == Type.OBJECT && right.type == Type.OBJECT) { for (int i = 0; i < right.list.Count; i++) { string key = right.keys[i]; if (right[i].isContainer) { if (left.HasField(key)) { MergeRecur(left[key], right[i]); } else { left.AddField(key, right[i]); } } else { if (left.HasField(key)) { left.SetField(key, right[i]); } else { left.AddField(key, right[i]); } } } } else if (left.type == Type.ARRAY && right.type == Type.ARRAY) { if (right.Count > left.Count) { #if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 Debug.LogError #else Debug.WriteLine #endif ("Cannot merge arrays when right object has more elements"); return; } for (int i = 0; i < right.list.Count; i++) { if (left[i].type == right[i].type) //Only overwrite with the same type { if (left[i].isContainer) { MergeRecur(left[i], right[i]); } else { left[i] = right[i]; } } } } }
//TODO: Refactor Stringify functions to share core logic /* * I know, I know, this is really bad form. It turns out that there is a * significant amount of garbage created when calling as a coroutine, so this * method is duplicated. Hopefully there won't be too many future changes, but * I would still like a more elegant way to optionaly yield */ void Stringify(int depth, StringBuilder builder, bool pretty = false) //Convert the JSONObject into a string //Profiler.BeginSample("JSONprint"); { if (depth++ > MAX_DEPTH) { #if UNITY_2 || UNITY_3 || UNITY_4 || UNITY_5 Debug.Log #else Debug.WriteLine #endif ("reached max depth!"); return; } switch (type) { case Type.BAKED: builder.Append(str); break; case Type.STRING: builder.AppendFormat("\"{0}\"", str); break; case Type.NUMBER: if (useInt) { builder.Append(i.ToString()); } else { #if USEFLOAT if (float.IsInfinity(n)) { builder.Append(INFINITY); } else if (float.IsNegativeInfinity(n)) { builder.Append(NEGINFINITY); } else if (float.IsNaN(n)) { builder.Append(NaN); } #else if (double.IsInfinity(n)) { builder.Append(INFINITY); } else if (double.IsNegativeInfinity(n)) { builder.Append(NEGINFINITY); } else if (double.IsNaN(n)) { builder.Append(NaN); } #endif else { builder.Append(n.ToString()); } } break; case Type.OBJECT: builder.Append("{"); if (list.Count > 0) { #if (PRETTY) //for a bit more readability, comment the define above to disable system-wide if (pretty) { builder.Append("\n"); } #endif for (int i = 0; i < list.Count; i++) { string key = keys[i]; JSONObject obj = list[i]; if (obj) { #if (PRETTY) if (pretty) { for (int j = 0; j < depth; j++) { builder.Append("\t"); //for a bit more readability } } #endif builder.AppendFormat("\"{0}\":", key); obj.Stringify(depth, builder, pretty); builder.Append(","); #if (PRETTY) if (pretty) { builder.Append("\n"); } #endif } } #if (PRETTY) if (pretty) { builder.Length -= 2; } else #endif builder.Length--; } #if (PRETTY) if (pretty && list.Count > 0) { builder.Append("\n"); for (int j = 0; j < depth - 1; j++) { builder.Append("\t"); //for a bit more readability } } #endif builder.Append("}"); break; case Type.ARRAY: builder.Append("["); if (list.Count > 0) { #if (PRETTY) if (pretty) { builder.Append("\n"); //for a bit more readability } #endif for (int i = 0; i < list.Count; i++) { if (list[i]) { #if (PRETTY) if (pretty) { for (int j = 0; j < depth; j++) { builder.Append("\t"); //for a bit more readability } } #endif list[i].Stringify(depth, builder, pretty); builder.Append(","); #if (PRETTY) if (pretty) { builder.Append("\n"); //for a bit more readability } #endif } } #if (PRETTY) if (pretty) { builder.Length -= 2; } else #endif builder.Length--; } #if (PRETTY) if (pretty && list.Count > 0) { builder.Append("\n"); for (int j = 0; j < depth - 1; j++) { builder.Append("\t"); //for a bit more readability } } #endif builder.Append("]"); break; case Type.BOOL: if (b) { builder.Append("true"); } else { builder.Append("false"); } break; case Type.NULL: builder.Append("null"); break; } //Profiler.EndSample(); }