├── .gitignore ├── LICENSE ├── README.md └── Sample ├── Assets ├── Main.unity ├── Main.unity.meta ├── Resources.meta ├── Resources │ ├── Textures.meta │ └── Textures │ │ ├── Sprite.png │ │ └── Sprite.png.meta ├── Scripts.meta └── Scripts │ ├── Game.cs │ ├── Game.cs.meta │ ├── JSONObject.meta │ ├── JSONObject │ ├── Editor.meta │ ├── Editor │ │ ├── JSONChecker.cs │ │ └── JSONChecker.cs.meta │ ├── JSONObject.cs │ ├── JSONObject.cs.meta │ ├── JSONTemplates.cs │ ├── JSONTemplates.cs.meta │ ├── VectorTemplates.cs │ ├── VectorTemplates.cs.meta │ ├── readme.txt │ └── readme.txt.meta │ ├── Modding.meta │ ├── Modding │ ├── Mod.cs │ ├── Mod.cs.meta │ ├── ModManager.cs │ ├── ModManager.cs.meta │ ├── Moddable.cs │ └── Moddable.cs.meta │ ├── Mods.meta │ └── Mods │ ├── Character.meta │ └── Character │ ├── Character.mod │ ├── Character.mod.meta │ ├── Scripts.meta │ └── Scripts │ ├── Character.cs │ └── Character.cs.meta ├── Mods └── Character │ ├── Character.mod │ ├── Scripts │ └── Character.cs │ └── Textures │ └── Sprite.png └── ProjectSettings ├── AudioManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── ProjectVersion.txt ├── QualitySettings.asset ├── TagManager.asset ├── TimeManager.asset ├── UnityAdsSettings.asset └── UnityAnalyticsManager.asset /.gitignore: -------------------------------------------------------------------------------- 1 | Sample/Library/ 2 | Sample/Temp/ 3 | *.csproj 4 | *.sln 5 | Sample/Sample_Data/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nuno Silva 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-CSharp-Mod 2 | Native C# Modding support for Unity 3 | 4 | This is a sample project for creating a fully moddable environment in Unity in runtime using the Mono compiler, inspired by the Cities: Skylines game's modding method. 5 | 6 | There's a Mod Manager that searches all directories in the Mods directory for .mod files containing info about a mod bundle. It runs the Mono compiler on the mod bundle's scripts folder to produce an assembly .dll that it loads into Unity. 7 | 8 | The .mod format is a simple JSON file with a name, description, and any dependencies needed separated by |'s. 9 | 10 | Every Moddable object must derive from the Moddable class, which contains a TheMod variable (to get the mod it came from and details about where it's located). You can spawn each moddable object by its class name using the Spawn method in the ModManager class. 11 | 12 | You may choose to enable compilation with the MODMANAGER_COMPILES_MODS define on your player. Otherwise, it'll only try to load the mod .dll. You need a Mono folder containing the Mono compiler (usually from your Unity installation) in the same folder as the project folder in order to compile. 13 | 14 | You may choose to disable mod loading at runtime and instead load all mods from memory by compiling all mods into your project and adding the MODMANAGER_LOADS_FROM_MEMORY define to make the mod manager search for all Moddable Types in your Unity assemblies. 15 | This is useful in Mobile builds where you can't run the compiler. 16 | 17 | # Credits 18 | Art by Stephen Challener (Redshrike), hosted by OpenGameArt.org 19 | Code by Nuno Silva (LittleCodingFox) 20 | -------------------------------------------------------------------------------- /Sample/Assets/Main.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/Assets/Main.unity -------------------------------------------------------------------------------- /Sample/Assets/Main.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 28f93c43fb6fe7b4d81e1703c740b9f0 3 | timeCreated: 1443817231 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Sample/Assets/Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6738b9d0a047a8d4fa61d246c4f242c1 3 | folderAsset: yes 4 | timeCreated: 1443818198 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Resources/Textures.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aaaa99105e7070545b94bc3e544303d9 3 | folderAsset: yes 4 | timeCreated: 1443818198 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Resources/Textures/Sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/Assets/Resources/Textures/Sprite.png -------------------------------------------------------------------------------- /Sample/Assets/Resources/Textures/Sprite.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 42ecd517e8162bb498e2f274d5c0c94b 3 | timeCreated: 1443818201 4 | licenseType: Free 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 1 11 | linearTexture: 0 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: .25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 8 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -1 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: -1 33 | aniso: -1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 1 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: .5, y: .5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 8 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | spritePackingTag: 54 | userData: 55 | assetBundleName: 56 | assetBundleVariant: 57 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f70de8ebedd388941ab800d259d39fe2 3 | folderAsset: yes 4 | timeCreated: 1443816459 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Game.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class Game : MonoBehaviour 4 | { 5 | public GameObject Player; 6 | 7 | void Start () 8 | { 9 | Player = ModManager.Instance.Spawn("Character", Vector3.zero); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Game.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0b9b77b2c36caca40925f0db8abdc7a7 3 | timeCreated: 1443816604 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b26501fc3322c274d876329a63eb5e51 3 | folderAsset: yes 4 | timeCreated: 1443816511 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 1 2 | guid: 6b700d54458b2824c934e039a95c468c 3 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/Editor/JSONChecker.cs: -------------------------------------------------------------------------------- 1 | //#define PERFTEST //For testing performance of parse/stringify. Turn on editor profiling to see how we're doing 2 | 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | public class JSONChecker : EditorWindow { 7 | string JSON = @"{ 8 | ""TestObject"": { 9 | ""SomeText"": ""Blah"", 10 | ""SomeObject"": { 11 | ""SomeNumber"": 42, 12 | ""SomeFloat"": 13.37, 13 | ""SomeBool"": true, 14 | ""SomeNull"": null 15 | }, 16 | 17 | ""SomeEmptyObject"": { }, 18 | ""SomeEmptyArray"": [ ], 19 | ""EmbeddedObject"": ""{\""field\"":\""Value with \\\""escaped quotes\\\""\""}"" 20 | } 21 | }"; //dat string literal... 22 | string URL = ""; 23 | JSONObject j; 24 | [MenuItem("Window/JSONChecker")] 25 | static void Init() { 26 | GetWindow(typeof(JSONChecker)); 27 | } 28 | void OnGUI() { 29 | JSON = EditorGUILayout.TextArea(JSON); 30 | GUI.enabled = !string.IsNullOrEmpty(JSON); 31 | if(GUILayout.Button("Check JSON")) { 32 | #if PERFTEST 33 | Profiler.BeginSample("JSONParse"); 34 | j = JSONObject.Create(JSON); 35 | Profiler.EndSample(); 36 | Profiler.BeginSample("JSONStringify"); 37 | j.ToString(true); 38 | Profiler.EndSample(); 39 | #else 40 | j = JSONObject.Create(JSON); 41 | #endif 42 | Debug.Log(j.ToString(true)); 43 | } 44 | EditorGUILayout.Separator(); 45 | URL = EditorGUILayout.TextField("URL", URL); 46 | if (GUILayout.Button("Get JSON")) { 47 | Debug.Log(URL); 48 | WWW test = new WWW(URL); 49 | while (!test.isDone) ; 50 | if (!string.IsNullOrEmpty(test.error)) { 51 | Debug.Log(test.error); 52 | } else { 53 | Debug.Log(test.text); 54 | j = new JSONObject(test.text); 55 | Debug.Log(j.ToString(true)); 56 | } 57 | } 58 | if(j) { 59 | //Debug.Log(System.GC.GetTotalMemory(false) + ""); 60 | if(j.type == JSONObject.Type.NULL) 61 | GUILayout.Label("JSON fail:\n" + j.ToString(true)); 62 | else 63 | GUILayout.Label("JSON success:\n" + j.ToString(true)); 64 | 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/Editor/JSONChecker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 1 2 | guid: 6c5a625d29393ed4da8d9150a629fb35 3 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/JSONObject.cs: -------------------------------------------------------------------------------- 1 | #define PRETTY //Comment out when you no longer need to read JSON to disable pretty Print system-wide 2 | //Using doubles will cause errors in VectorTemplates.cs; Unity speaks floats 3 | #define USEFLOAT //Use floats for numbers instead of doubles (enable if you're getting too many significant digits in string output) 4 | //#define POOLING //Currently using a build setting for this one (also it's experimental) 5 | 6 | using System.Diagnostics; 7 | using UnityEngine; 8 | using System.Collections; 9 | using System.Collections.Generic; 10 | using System.Text; 11 | using Debug = UnityEngine.Debug; 12 | 13 | /* 14 | * http://www.opensource.org/licenses/lgpl-2.1.php 15 | * JSONObject class v.1.4.1 16 | * for use with Unity 17 | * Copyright Matt Schoen 2010 - 2013 18 | */ 19 | 20 | public class JSONObject { 21 | #if POOLING 22 | const int MAX_POOL_SIZE = 10000; 23 | public static Queue releaseQueue = new Queue(); 24 | #endif 25 | 26 | const int MAX_DEPTH = 100; 27 | const string INFINITY = "\"INFINITY\""; 28 | const string NEGINFINITY = "\"NEGINFINITY\""; 29 | const string NaN = "\"NaN\""; 30 | public static readonly char[] WHITESPACE = { ' ', '\r', '\n', '\t', '\uFEFF', '\u0009' }; 31 | public enum Type { NULL, STRING, NUMBER, OBJECT, ARRAY, BOOL, BAKED } 32 | public bool isContainer { get { return (type == Type.ARRAY || type == Type.OBJECT); } } 33 | public Type type = Type.NULL; 34 | public int Count { 35 | get { 36 | if(list == null) 37 | return -1; 38 | return list.Count; 39 | } 40 | } 41 | public List list; 42 | public List keys; 43 | public string str; 44 | #if USEFLOAT 45 | public float n; 46 | public float f { 47 | get { 48 | return n; 49 | } 50 | } 51 | #else 52 | public double n; 53 | public float f { 54 | get { 55 | return (float)n; 56 | } 57 | } 58 | #endif 59 | public bool b; 60 | public delegate void AddJSONContents(JSONObject self); 61 | 62 | public static JSONObject nullJO { get { return Create(Type.NULL); } } //an empty, null object 63 | public static JSONObject obj { get { return Create(Type.OBJECT); } } //an empty object 64 | public static JSONObject arr { get { return Create(Type.ARRAY); } } //an empty array 65 | 66 | public JSONObject(Type t) { 67 | type = t; 68 | switch(t) { 69 | case Type.ARRAY: 70 | list = new List(); 71 | break; 72 | case Type.OBJECT: 73 | list = new List(); 74 | keys = new List(); 75 | break; 76 | } 77 | } 78 | public JSONObject(bool b) { 79 | type = Type.BOOL; 80 | this.b = b; 81 | } 82 | #if USEFLOAT 83 | public JSONObject(float f) { 84 | type = Type.NUMBER; 85 | n = f; 86 | } 87 | #else 88 | public JSONObject(double d) { 89 | type = Type.NUMBER; 90 | n = d; 91 | } 92 | #endif 93 | public JSONObject(Dictionary dic) { 94 | type = Type.OBJECT; 95 | keys = new List(); 96 | list = new List(); 97 | //Not sure if it's worth removing the foreach here 98 | foreach(KeyValuePair kvp in dic) { 99 | keys.Add(kvp.Key); 100 | list.Add(CreateStringObject(kvp.Value)); 101 | } 102 | } 103 | public JSONObject(Dictionary dic) { 104 | type = Type.OBJECT; 105 | keys = new List(); 106 | list = new List(); 107 | //Not sure if it's worth removing the foreach here 108 | foreach(KeyValuePair kvp in dic) { 109 | keys.Add(kvp.Key); 110 | list.Add(kvp.Value); 111 | } 112 | } 113 | public JSONObject(AddJSONContents content) { 114 | content.Invoke(this); 115 | } 116 | public JSONObject(JSONObject[] objs) { 117 | type = Type.ARRAY; 118 | list = new List(objs); 119 | } 120 | //Convenience function for creating a JSONObject containing a string. This is not part of the constructor so that malformed JSON data doesn't just turn into a string object 121 | public static JSONObject StringObject(string val) { return CreateStringObject(val); } 122 | public void Absorb(JSONObject obj) { 123 | list.AddRange(obj.list); 124 | keys.AddRange(obj.keys); 125 | str = obj.str; 126 | n = obj.n; 127 | b = obj.b; 128 | type = obj.type; 129 | } 130 | public static JSONObject Create() { 131 | #if POOLING 132 | JSONObject result = null; 133 | while(result == null && releaseQueue.Count > 0) { 134 | result = releaseQueue.Dequeue(); 135 | #if DEV 136 | //The following cases should NEVER HAPPEN (but they do...) 137 | if(result == null) 138 | Debug.Log("wtf " + releaseQueue.Count); 139 | else if(result.list != null) 140 | Debug.Log("wtflist " + result.list.Count); 141 | #endif 142 | } 143 | if(result != null) 144 | return result; 145 | #endif 146 | return new JSONObject(); 147 | } 148 | public static JSONObject Create(Type t) { 149 | JSONObject obj = Create(); 150 | obj.type = t; 151 | switch(t) { 152 | case Type.ARRAY: 153 | obj.list = new List(); 154 | break; 155 | case Type.OBJECT: 156 | obj.list = new List(); 157 | obj.keys = new List(); 158 | break; 159 | } 160 | return obj; 161 | } 162 | public static JSONObject Create(bool val) { 163 | JSONObject obj = Create(); 164 | obj.type = Type.BOOL; 165 | obj.b = val; 166 | return obj; 167 | } 168 | public static JSONObject Create(float val) { 169 | JSONObject obj = Create(); 170 | obj.type = Type.NUMBER; 171 | obj.n = val; 172 | return obj; 173 | } 174 | public static JSONObject Create(int val) { 175 | JSONObject obj = Create(); 176 | obj.type = Type.NUMBER; 177 | obj.n = val; 178 | return obj; 179 | } 180 | public static JSONObject CreateStringObject(string val) { 181 | JSONObject obj = Create(); 182 | obj.type = Type.STRING; 183 | obj.str = val; 184 | return obj; 185 | } 186 | public static JSONObject CreateBakedObject(string val) { 187 | JSONObject bakedObject = Create(); 188 | bakedObject.type = Type.BAKED; 189 | bakedObject.str = val; 190 | return bakedObject; 191 | } 192 | /// 193 | /// Create a JSONObject by parsing string data 194 | /// 195 | /// The string to be parsed 196 | /// The maximum depth for the parser to search. Set this to to 1 for the first level, 197 | /// 2 for the first 2 levels, etc. It defaults to -2 because -1 is the depth value that is parsed (see below) 198 | /// Whether to store levels beyond maxDepth in baked JSONObjects 199 | /// Whether to be strict in the parsing. For example, non-strict parsing will successfully 200 | /// parse "a string" into a string-type 201 | /// 202 | public static JSONObject Create(string val, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false) { 203 | JSONObject obj = Create(); 204 | obj.Parse(val, maxDepth, storeExcessLevels, strict); 205 | return obj; 206 | } 207 | public static JSONObject Create(AddJSONContents content) { 208 | JSONObject obj = Create(); 209 | content.Invoke(obj); 210 | return obj; 211 | } 212 | public static JSONObject Create(Dictionary dic) { 213 | JSONObject obj = Create(); 214 | obj.type = Type.OBJECT; 215 | obj.keys = new List(); 216 | obj.list = new List(); 217 | //Not sure if it's worth removing the foreach here 218 | foreach(KeyValuePair kvp in dic) { 219 | obj.keys.Add(kvp.Key); 220 | obj.list.Add(CreateStringObject(kvp.Value)); 221 | } 222 | return obj; 223 | } 224 | public JSONObject() { } 225 | #region PARSE 226 | public JSONObject(string str, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false) { //create a new JSONObject from a string (this will also create any children, and parse the whole string) 227 | Parse(str, maxDepth, storeExcessLevels, strict); 228 | } 229 | void Parse(string str, int maxDepth = -2, bool storeExcessLevels = false, bool strict = false) { 230 | if(!string.IsNullOrEmpty(str)) { 231 | str = str.Trim(WHITESPACE); 232 | if(strict) { 233 | if(str[0] != '[' && str[0] != '{') { 234 | type = Type.NULL; 235 | Debug.LogWarning("Improper (strict) JSON formatting. First character must be [ or {"); 236 | return; 237 | } 238 | } 239 | if(str.Length > 0) { 240 | #if UNITY_WP8 || UNITY_WSA 241 | if (str == "true") { 242 | type = Type.BOOL; 243 | b = true; 244 | } else if (str == "false") { 245 | type = Type.BOOL; 246 | b = false; 247 | } else if (str == "null") { 248 | type = Type.NULL; 249 | #else 250 | if(string.Compare(str, "true", true) == 0) { 251 | type = Type.BOOL; 252 | b = true; 253 | } else if(string.Compare(str, "false", true) == 0) { 254 | type = Type.BOOL; 255 | b = false; 256 | } else if(string.Compare(str, "null", true) == 0) { 257 | type = Type.NULL; 258 | #endif 259 | #if USEFLOAT 260 | } else if(str == INFINITY) { 261 | type = Type.NUMBER; 262 | n = float.PositiveInfinity; 263 | } else if(str == NEGINFINITY) { 264 | type = Type.NUMBER; 265 | n = float.NegativeInfinity; 266 | } else if(str == NaN) { 267 | type = Type.NUMBER; 268 | n = float.NaN; 269 | #else 270 | } else if(str == INFINITY) { 271 | type = Type.NUMBER; 272 | n = double.PositiveInfinity; 273 | } else if(str == NEGINFINITY) { 274 | type = Type.NUMBER; 275 | n = double.NegativeInfinity; 276 | } else if(str == NaN) { 277 | type = Type.NUMBER; 278 | n = double.NaN; 279 | #endif 280 | } else if(str[0] == '"') { 281 | type = Type.STRING; 282 | this.str = str.Substring(1, str.Length - 2); 283 | } else { 284 | int tokenTmp = 1; 285 | /* 286 | * Checking for the following formatting (www.json.org) 287 | * object - {"field1":value,"field2":value} 288 | * array - [value,value,value] 289 | * value - string - "string" 290 | * - number - 0.0 291 | * - bool - true -or- false 292 | * - null - null 293 | */ 294 | int offset = 0; 295 | switch(str[offset]) { 296 | case '{': 297 | type = Type.OBJECT; 298 | keys = new List(); 299 | list = new List(); 300 | break; 301 | case '[': 302 | type = Type.ARRAY; 303 | list = new List(); 304 | break; 305 | default: 306 | try { 307 | #if USEFLOAT 308 | n = System.Convert.ToSingle(str); 309 | #else 310 | n = System.Convert.ToDouble(str); 311 | #endif 312 | type = Type.NUMBER; 313 | } catch(System.FormatException) { 314 | type = Type.NULL; 315 | Debug.LogWarning("improper JSON formatting:" + str); 316 | } 317 | return; 318 | } 319 | string propName = ""; 320 | bool openQuote = false; 321 | bool inProp = false; 322 | int depth = 0; 323 | while(++offset < str.Length) { 324 | if(System.Array.IndexOf(WHITESPACE, str[offset]) > -1) 325 | continue; 326 | if(str[offset] == '\\') { 327 | offset += 1; 328 | continue; 329 | } 330 | if(str[offset] == '"') { 331 | if(openQuote) { 332 | if(!inProp && depth == 0 && type == Type.OBJECT) 333 | propName = str.Substring(tokenTmp + 1, offset - tokenTmp - 1); 334 | openQuote = false; 335 | } else { 336 | if(depth == 0 && type == Type.OBJECT) 337 | tokenTmp = offset; 338 | openQuote = true; 339 | } 340 | } 341 | if(openQuote) 342 | continue; 343 | if(type == Type.OBJECT && depth == 0) { 344 | if(str[offset] == ':') { 345 | tokenTmp = offset + 1; 346 | inProp = true; 347 | } 348 | } 349 | 350 | if(str[offset] == '[' || str[offset] == '{') { 351 | depth++; 352 | } else if(str[offset] == ']' || str[offset] == '}') { 353 | depth--; 354 | } 355 | //if (encounter a ',' at top level) || a closing ]/} 356 | if((str[offset] == ',' && depth == 0) || depth < 0) { 357 | inProp = false; 358 | string inner = str.Substring(tokenTmp, offset - tokenTmp).Trim(WHITESPACE); 359 | if(inner.Length > 0) { 360 | if(type == Type.OBJECT) 361 | keys.Add(propName); 362 | if(maxDepth != -1) //maxDepth of -1 is the end of the line 363 | list.Add(Create(inner, (maxDepth < -1) ? -2 : maxDepth - 1)); 364 | else if(storeExcessLevels) 365 | list.Add(CreateBakedObject(inner)); 366 | 367 | } 368 | tokenTmp = offset + 1; 369 | } 370 | } 371 | } 372 | } else type = Type.NULL; 373 | } else type = Type.NULL; //If the string is missing, this is a null 374 | //Profiler.EndSample(); 375 | } 376 | #endregion 377 | public bool IsNumber { get { return type == Type.NUMBER; } } 378 | public bool IsNull { get { return type == Type.NULL; } } 379 | public bool IsString { get { return type == Type.STRING; } } 380 | public bool IsBool { get { return type == Type.BOOL; } } 381 | public bool IsArray { get { return type == Type.ARRAY; } } 382 | public bool IsObject { get { return type == Type.OBJECT || type == Type.BAKED; } } 383 | public void Add(bool val) { 384 | Add(Create(val)); 385 | } 386 | public void Add(float val) { 387 | Add(Create(val)); 388 | } 389 | public void Add(int val) { 390 | Add(Create(val)); 391 | } 392 | public void Add(string str) { 393 | Add(CreateStringObject(str)); 394 | } 395 | public void Add(AddJSONContents content) { 396 | Add(Create(content)); 397 | } 398 | public void Add(JSONObject obj) { 399 | if(obj) { //Don't do anything if the object is null 400 | if(type != Type.ARRAY) { 401 | type = Type.ARRAY; //Congratulations, son, you're an ARRAY now 402 | if(list == null) 403 | list = new List(); 404 | } 405 | list.Add(obj); 406 | } 407 | } 408 | public void AddField(string name, bool val) { 409 | AddField(name, Create(val)); 410 | } 411 | public void AddField(string name, float val) { 412 | AddField(name, Create(val)); 413 | } 414 | public void AddField(string name, int val) { 415 | AddField(name, Create(val)); 416 | } 417 | public void AddField(string name, AddJSONContents content) { 418 | AddField(name, Create(content)); 419 | } 420 | public void AddField(string name, string val) { 421 | AddField(name, CreateStringObject(val)); 422 | } 423 | public void AddField(string name, JSONObject obj) { 424 | if(obj) { //Don't do anything if the object is null 425 | if(type != Type.OBJECT) { 426 | if(keys == null) 427 | keys = new List(); 428 | if(type == Type.ARRAY) { 429 | for(int i = 0; i < list.Count; i++) 430 | keys.Add(i + ""); 431 | } else 432 | if(list == null) 433 | list = new List(); 434 | type = Type.OBJECT; //Congratulations, son, you're an OBJECT now 435 | } 436 | keys.Add(name); 437 | list.Add(obj); 438 | } 439 | } 440 | public void SetField(string name, string val) { SetField(name, CreateStringObject(val)); } 441 | public void SetField(string name, bool val) { SetField(name, Create(val)); } 442 | public void SetField(string name, float val) { SetField(name, Create(val)); } 443 | public void SetField(string name, int val) { SetField(name, Create(val)); } 444 | public void SetField(string name, JSONObject obj) { 445 | if(HasField(name)) { 446 | list.Remove(this[name]); 447 | keys.Remove(name); 448 | } 449 | AddField(name, obj); 450 | } 451 | public void RemoveField(string name) { 452 | if(keys.IndexOf(name) > -1) { 453 | list.RemoveAt(keys.IndexOf(name)); 454 | keys.Remove(name); 455 | } 456 | } 457 | public delegate void FieldNotFound(string name); 458 | public delegate void GetFieldResponse(JSONObject obj); 459 | public bool GetField(ref bool field, string name, bool fallback) { 460 | if (GetField(ref field, name)) { return true; } 461 | field = fallback; 462 | return false; 463 | } 464 | public bool GetField(ref bool field, string name, FieldNotFound fail = null) { 465 | if(type == Type.OBJECT) { 466 | int index = keys.IndexOf(name); 467 | if(index >= 0) { 468 | field = list[index].b; 469 | return true; 470 | } 471 | } 472 | if(fail != null) fail.Invoke(name); 473 | return false; 474 | } 475 | #if USEFLOAT 476 | public bool GetField(ref float field, string name, float fallback) { 477 | #else 478 | public bool GetField(ref double field, string name, double fallback) { 479 | #endif 480 | if (GetField(ref field, name)) { return true; } 481 | field = fallback; 482 | return false; 483 | } 484 | #if USEFLOAT 485 | public bool GetField(ref float field, string name, FieldNotFound fail = null) { 486 | #else 487 | public bool GetField(ref double field, string name, FieldNotFound fail = null) { 488 | #endif 489 | if(type == Type.OBJECT) { 490 | int index = keys.IndexOf(name); 491 | if(index >= 0) { 492 | field = list[index].n; 493 | return true; 494 | } 495 | } 496 | if(fail != null) fail.Invoke(name); 497 | return false; 498 | } 499 | public bool GetField(ref int field, string name, int fallback) { 500 | if (GetField(ref field, name)) { return true; } 501 | field = fallback; 502 | return false; 503 | } 504 | public bool GetField(ref int field, string name, FieldNotFound fail = null) { 505 | if (IsObject) { 506 | int index = keys.IndexOf(name); 507 | if(index >= 0) { 508 | field = (int)list[index].n; 509 | return true; 510 | } 511 | } 512 | if(fail != null) fail.Invoke(name); 513 | return false; 514 | } 515 | public bool GetField(ref uint field, string name, uint fallback) { 516 | if (GetField(ref field, name)) { return true; } 517 | field = fallback; 518 | return false; 519 | } 520 | public bool GetField(ref uint field, string name, FieldNotFound fail = null) { 521 | if (IsObject) { 522 | int index = keys.IndexOf(name); 523 | if(index >= 0) { 524 | field = (uint)list[index].n; 525 | return true; 526 | } 527 | } 528 | if(fail != null) fail.Invoke(name); 529 | return false; 530 | } 531 | public bool GetField(ref string field, string name, string fallback) { 532 | if (GetField(ref field, name)) { return true; } 533 | field = fallback; 534 | return false; 535 | } 536 | public bool GetField(ref string field, string name, FieldNotFound fail = null) { 537 | if (IsObject) { 538 | int index = keys.IndexOf(name); 539 | if(index >= 0) { 540 | field = list[index].str; 541 | return true; 542 | } 543 | } 544 | if(fail != null) fail.Invoke(name); 545 | return false; 546 | } 547 | public void GetField(string name, GetFieldResponse response, FieldNotFound fail = null) { 548 | if(response != null && IsObject) { 549 | int index = keys.IndexOf(name); 550 | if(index >= 0) { 551 | response.Invoke(list[index]); 552 | return; 553 | } 554 | } 555 | if(fail != null) fail.Invoke(name); 556 | } 557 | public JSONObject GetField(string name) { 558 | if (IsObject) 559 | for(int i = 0; i < keys.Count; i++) 560 | if(keys[i] == name) 561 | return list[i]; 562 | return null; 563 | } 564 | public bool HasFields(string[] names) { 565 | if (!IsObject) 566 | return false; 567 | for(int i = 0; i < names.Length; i++) 568 | if(!keys.Contains(names[i])) 569 | return false; 570 | return true; 571 | } 572 | public bool HasField(string name) { 573 | if (!IsObject) 574 | return false; 575 | for (int i = 0; i < keys.Count; i++) 576 | if (keys[i] == name) 577 | return true; 578 | return false; 579 | } 580 | public void Clear() { 581 | type = Type.NULL; 582 | if(list != null) 583 | list.Clear(); 584 | if(keys != null) 585 | keys.Clear(); 586 | str = ""; 587 | n = 0; 588 | b = false; 589 | } 590 | /// 591 | /// Copy a JSONObject. This could probably work better 592 | /// 593 | /// 594 | public JSONObject Copy() { 595 | return Create(Print()); 596 | } 597 | /* 598 | * The Merge function is experimental. Use at your own risk. 599 | */ 600 | public void Merge(JSONObject obj) { 601 | MergeRecur(this, obj); 602 | } 603 | /// 604 | /// Merge object right into left recursively 605 | /// 606 | /// The left (base) object 607 | /// The right (new) object 608 | static void MergeRecur(JSONObject left, JSONObject right) { 609 | if(left.type == Type.NULL) 610 | left.Absorb(right); 611 | else if(left.type == Type.OBJECT && right.type == Type.OBJECT) { 612 | for(int i = 0; i < right.list.Count; i++) { 613 | string key = right.keys[i]; 614 | if(right[i].isContainer) { 615 | if(left.HasField(key)) 616 | MergeRecur(left[key], right[i]); 617 | else 618 | left.AddField(key, right[i]); 619 | } else { 620 | if(left.HasField(key)) 621 | left.SetField(key, right[i]); 622 | else 623 | left.AddField(key, right[i]); 624 | } 625 | } 626 | } else if(left.type == Type.ARRAY && right.type == Type.ARRAY) { 627 | if(right.Count > left.Count) { 628 | Debug.LogError("Cannot merge arrays when right object has more elements"); 629 | return; 630 | } 631 | for(int i = 0; i < right.list.Count; i++) { 632 | if(left[i].type == right[i].type) { //Only overwrite with the same type 633 | if(left[i].isContainer) 634 | MergeRecur(left[i], right[i]); 635 | else { 636 | left[i] = right[i]; 637 | } 638 | } 639 | } 640 | } 641 | } 642 | public void Bake() { 643 | if(type != Type.BAKED) { 644 | str = Print(); 645 | type = Type.BAKED; 646 | } 647 | } 648 | public IEnumerable BakeAsync() { 649 | if(type != Type.BAKED) { 650 | foreach(string s in PrintAsync()) { 651 | if(s == null) 652 | yield return s; 653 | else { 654 | str = s; 655 | } 656 | } 657 | type = Type.BAKED; 658 | } 659 | } 660 | #pragma warning disable 219 661 | public string Print(bool pretty = false) { 662 | StringBuilder builder = new StringBuilder(); 663 | Stringify(0, builder, pretty); 664 | return builder.ToString(); 665 | } 666 | public IEnumerable PrintAsync(bool pretty = false) { 667 | StringBuilder builder = new StringBuilder(); 668 | printWatch.Reset(); 669 | printWatch.Start(); 670 | foreach(IEnumerable e in StringifyAsync(0, builder, pretty)) { 671 | yield return null; 672 | } 673 | yield return builder.ToString(); 674 | } 675 | #pragma warning restore 219 676 | #region STRINGIFY 677 | const float maxFrameTime = 0.008f; 678 | static readonly Stopwatch printWatch = new Stopwatch(); 679 | IEnumerable StringifyAsync(int depth, StringBuilder builder, bool pretty = false) { //Convert the JSONObject into a string 680 | //Profiler.BeginSample("JSONprint"); 681 | if(depth++ > MAX_DEPTH) { 682 | Debug.Log("reached max depth!"); 683 | yield break; 684 | } 685 | if(printWatch.Elapsed.TotalSeconds > maxFrameTime) { 686 | printWatch.Reset(); 687 | yield return null; 688 | printWatch.Start(); 689 | } 690 | switch(type) { 691 | case Type.BAKED: 692 | builder.Append(str); 693 | break; 694 | case Type.STRING: 695 | builder.AppendFormat("\"{0}\"", str); 696 | break; 697 | case Type.NUMBER: 698 | #if USEFLOAT 699 | if(float.IsInfinity(n)) 700 | builder.Append(INFINITY); 701 | else if(float.IsNegativeInfinity(n)) 702 | builder.Append(NEGINFINITY); 703 | else if(float.IsNaN(n)) 704 | builder.Append(NaN); 705 | #else 706 | if(double.IsInfinity(n)) 707 | builder.Append(INFINITY); 708 | else if(double.IsNegativeInfinity(n)) 709 | builder.Append(NEGINFINITY); 710 | else if(double.IsNaN(n)) 711 | builder.Append(NaN); 712 | #endif 713 | else 714 | builder.Append(n.ToString()); 715 | break; 716 | case Type.OBJECT: 717 | builder.Append("{"); 718 | if(list.Count > 0) { 719 | #if(PRETTY) //for a bit more readability, comment the define above to disable system-wide 720 | if(pretty) 721 | builder.Append("\n"); 722 | #endif 723 | for(int i = 0; i < list.Count; i++) { 724 | string key = keys[i]; 725 | JSONObject obj = list[i]; 726 | if(obj) { 727 | #if(PRETTY) 728 | if(pretty) 729 | for(int j = 0; j < depth; j++) 730 | builder.Append("\t"); //for a bit more readability 731 | #endif 732 | builder.AppendFormat("\"{0}\":", key); 733 | foreach(IEnumerable e in obj.StringifyAsync(depth, builder, pretty)) 734 | yield return e; 735 | builder.Append(","); 736 | #if(PRETTY) 737 | if(pretty) 738 | builder.Append("\n"); 739 | #endif 740 | } 741 | } 742 | #if(PRETTY) 743 | if(pretty) 744 | builder.Length -= 2; 745 | else 746 | #endif 747 | builder.Length--; 748 | } 749 | #if(PRETTY) 750 | if(pretty && list.Count > 0) { 751 | builder.Append("\n"); 752 | for(int j = 0; j < depth - 1; j++) 753 | builder.Append("\t"); //for a bit more readability 754 | } 755 | #endif 756 | builder.Append("}"); 757 | break; 758 | case Type.ARRAY: 759 | builder.Append("["); 760 | if(list.Count > 0) { 761 | #if(PRETTY) 762 | if(pretty) 763 | builder.Append("\n"); //for a bit more readability 764 | #endif 765 | for(int i = 0; i < list.Count; i++) { 766 | if(list[i]) { 767 | #if(PRETTY) 768 | if(pretty) 769 | for(int j = 0; j < depth; j++) 770 | builder.Append("\t"); //for a bit more readability 771 | #endif 772 | foreach(IEnumerable e in list[i].StringifyAsync(depth, builder, pretty)) 773 | yield return e; 774 | builder.Append(","); 775 | #if(PRETTY) 776 | if(pretty) 777 | builder.Append("\n"); //for a bit more readability 778 | #endif 779 | } 780 | } 781 | #if(PRETTY) 782 | if(pretty) 783 | builder.Length -= 2; 784 | else 785 | #endif 786 | builder.Length--; 787 | } 788 | #if(PRETTY) 789 | if(pretty && list.Count > 0) { 790 | builder.Append("\n"); 791 | for(int j = 0; j < depth - 1; j++) 792 | builder.Append("\t"); //for a bit more readability 793 | } 794 | #endif 795 | builder.Append("]"); 796 | break; 797 | case Type.BOOL: 798 | if(b) 799 | builder.Append("true"); 800 | else 801 | builder.Append("false"); 802 | break; 803 | case Type.NULL: 804 | builder.Append("null"); 805 | break; 806 | } 807 | //Profiler.EndSample(); 808 | } 809 | //TODO: Refactor Stringify functions to share core logic 810 | /* 811 | * I know, I know, this is really bad form. It turns out that there is a 812 | * significant amount of garbage created when calling as a coroutine, so this 813 | * method is duplicated. Hopefully there won't be too many future changes, but 814 | * I would still like a more elegant way to optionaly yield 815 | */ 816 | void Stringify(int depth, StringBuilder builder, bool pretty = false) { //Convert the JSONObject into a string 817 | //Profiler.BeginSample("JSONprint"); 818 | if(depth++ > MAX_DEPTH) { 819 | Debug.Log("reached max depth!"); 820 | return; 821 | } 822 | switch(type) { 823 | case Type.BAKED: 824 | builder.Append(str); 825 | break; 826 | case Type.STRING: 827 | builder.AppendFormat("\"{0}\"", str); 828 | break; 829 | case Type.NUMBER: 830 | #if USEFLOAT 831 | if(float.IsInfinity(n)) 832 | builder.Append(INFINITY); 833 | else if(float.IsNegativeInfinity(n)) 834 | builder.Append(NEGINFINITY); 835 | else if(float.IsNaN(n)) 836 | builder.Append(NaN); 837 | #else 838 | if(double.IsInfinity(n)) 839 | builder.Append(INFINITY); 840 | else if(double.IsNegativeInfinity(n)) 841 | builder.Append(NEGINFINITY); 842 | else if(double.IsNaN(n)) 843 | builder.Append(NaN); 844 | #endif 845 | else 846 | builder.Append(n.ToString()); 847 | break; 848 | case Type.OBJECT: 849 | builder.Append("{"); 850 | if(list.Count > 0) { 851 | #if(PRETTY) //for a bit more readability, comment the define above to disable system-wide 852 | if(pretty) 853 | builder.Append("\n"); 854 | #endif 855 | for(int i = 0; i < list.Count; i++) { 856 | string key = keys[i]; 857 | JSONObject obj = list[i]; 858 | if(obj) { 859 | #if(PRETTY) 860 | if(pretty) 861 | for(int j = 0; j < depth; j++) 862 | builder.Append("\t"); //for a bit more readability 863 | #endif 864 | builder.AppendFormat("\"{0}\":", key); 865 | obj.Stringify(depth, builder, pretty); 866 | builder.Append(","); 867 | #if(PRETTY) 868 | if(pretty) 869 | builder.Append("\n"); 870 | #endif 871 | } 872 | } 873 | #if(PRETTY) 874 | if(pretty) 875 | builder.Length -= 2; 876 | else 877 | #endif 878 | builder.Length--; 879 | } 880 | #if(PRETTY) 881 | if(pretty && list.Count > 0) { 882 | builder.Append("\n"); 883 | for(int j = 0; j < depth - 1; j++) 884 | builder.Append("\t"); //for a bit more readability 885 | } 886 | #endif 887 | builder.Append("}"); 888 | break; 889 | case Type.ARRAY: 890 | builder.Append("["); 891 | if(list.Count > 0) { 892 | #if(PRETTY) 893 | if(pretty) 894 | builder.Append("\n"); //for a bit more readability 895 | #endif 896 | for(int i = 0; i < list.Count; i++) { 897 | if(list[i]) { 898 | #if(PRETTY) 899 | if(pretty) 900 | for(int j = 0; j < depth; j++) 901 | builder.Append("\t"); //for a bit more readability 902 | #endif 903 | list[i].Stringify(depth, builder, pretty); 904 | builder.Append(","); 905 | #if(PRETTY) 906 | if(pretty) 907 | builder.Append("\n"); //for a bit more readability 908 | #endif 909 | } 910 | } 911 | #if(PRETTY) 912 | if(pretty) 913 | builder.Length -= 2; 914 | else 915 | #endif 916 | builder.Length--; 917 | } 918 | #if(PRETTY) 919 | if(pretty && list.Count > 0) { 920 | builder.Append("\n"); 921 | for(int j = 0; j < depth - 1; j++) 922 | builder.Append("\t"); //for a bit more readability 923 | } 924 | #endif 925 | builder.Append("]"); 926 | break; 927 | case Type.BOOL: 928 | if(b) 929 | builder.Append("true"); 930 | else 931 | builder.Append("false"); 932 | break; 933 | case Type.NULL: 934 | builder.Append("null"); 935 | break; 936 | } 937 | //Profiler.EndSample(); 938 | } 939 | #endregion 940 | public static implicit operator WWWForm(JSONObject obj) { 941 | WWWForm form = new WWWForm(); 942 | for(int i = 0; i < obj.list.Count; i++) { 943 | string key = i + ""; 944 | if(obj.type == Type.OBJECT) 945 | key = obj.keys[i]; 946 | string val = obj.list[i].ToString(); 947 | if(obj.list[i].type == Type.STRING) 948 | val = val.Replace("\"", ""); 949 | form.AddField(key, val); 950 | } 951 | return form; 952 | } 953 | public JSONObject this[int index] { 954 | get { 955 | if(list.Count > index) return list[index]; 956 | return null; 957 | } 958 | set { 959 | if(list.Count > index) 960 | list[index] = value; 961 | } 962 | } 963 | public JSONObject this[string index] { 964 | get { 965 | return GetField(index); 966 | } 967 | set { 968 | SetField(index, value); 969 | } 970 | } 971 | public override string ToString() { 972 | return Print(); 973 | } 974 | public string ToString(bool pretty) { 975 | return Print(pretty); 976 | } 977 | public Dictionary ToDictionary() { 978 | if(type == Type.OBJECT) { 979 | Dictionary result = new Dictionary(); 980 | for(int i = 0; i < list.Count; i++) { 981 | JSONObject val = list[i]; 982 | switch(val.type) { 983 | case Type.STRING: result.Add(keys[i], val.str); break; 984 | case Type.NUMBER: result.Add(keys[i], val.n + ""); break; 985 | case Type.BOOL: result.Add(keys[i], val.b + ""); break; 986 | default: Debug.LogWarning("Omitting object: " + keys[i] + " in dictionary conversion"); break; 987 | } 988 | } 989 | return result; 990 | } 991 | Debug.LogWarning("Tried to turn non-Object JSONObject into a dictionary"); 992 | return null; 993 | } 994 | public static implicit operator bool(JSONObject o) { 995 | return o != null; 996 | } 997 | #if POOLING 998 | static bool pool = true; 999 | public static void ClearPool() { 1000 | pool = false; 1001 | releaseQueue.Clear(); 1002 | pool = true; 1003 | } 1004 | 1005 | ~JSONObject() { 1006 | if(pool && releaseQueue.Count < MAX_POOL_SIZE) { 1007 | type = Type.NULL; 1008 | list = null; 1009 | keys = null; 1010 | str = ""; 1011 | n = 0; 1012 | b = false; 1013 | releaseQueue.Enqueue(this); 1014 | } 1015 | } 1016 | #endif 1017 | } 1018 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/JSONObject.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 1 2 | guid: f71819923b555d14ab64ae2044718de9 3 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/JSONTemplates.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | /* 6 | * http://www.opensource.org/licenses/lgpl-2.1.php 7 | * JSONTemplates class 8 | * for use with Unity 9 | * Copyright Matt Schoen 2010 10 | */ 11 | 12 | public static partial class JSONTemplates { 13 | static readonly HashSet touched = new HashSet(); 14 | 15 | public static JSONObject TOJSON(object obj) { //For a generic guess 16 | if(touched.Add(obj)) { 17 | JSONObject result = JSONObject.obj; 18 | //Fields 19 | FieldInfo[] fieldinfo = obj.GetType().GetFields(); 20 | foreach(FieldInfo fi in fieldinfo) { 21 | JSONObject val = JSONObject.nullJO; 22 | if(!fi.GetValue(obj).Equals(null)) { 23 | MethodInfo info = typeof(JSONTemplates).GetMethod("From" + fi.FieldType.Name); 24 | if(info != null) { 25 | object[] parms = new object[1]; 26 | parms[0] = fi.GetValue(obj); 27 | val = (JSONObject)info.Invoke(null, parms); 28 | } else if(fi.FieldType == typeof(string)) 29 | val = JSONObject.CreateStringObject(fi.GetValue(obj).ToString()); 30 | else 31 | val = JSONObject.Create(fi.GetValue(obj).ToString()); 32 | } 33 | if(val) { 34 | if(val.type != JSONObject.Type.NULL) 35 | result.AddField(fi.Name, val); 36 | else Debug.LogWarning("Null for this non-null object, property " + fi.Name + " of class " + obj.GetType().Name + ". Object type is " + fi.FieldType.Name); 37 | } 38 | } 39 | //Properties 40 | PropertyInfo[] propertyInfo = obj.GetType().GetProperties(); 41 | foreach(PropertyInfo pi in propertyInfo) { 42 | //This section should mirror part of AssetFactory.AddScripts() 43 | JSONObject val = JSONObject.nullJO; 44 | if(!pi.GetValue(obj, null).Equals(null)) { 45 | MethodInfo info = typeof(JSONTemplates).GetMethod("From" + pi.PropertyType.Name); 46 | if(info != null) { 47 | object[] parms = new object[1]; 48 | parms[0] = pi.GetValue(obj, null); 49 | val = (JSONObject)info.Invoke(null, parms); 50 | } else if(pi.PropertyType == typeof(string)) 51 | val = JSONObject.CreateStringObject(pi.GetValue(obj, null).ToString()); 52 | else 53 | val = JSONObject.Create(pi.GetValue(obj, null).ToString()); 54 | } 55 | if(val) { 56 | if(val.type != JSONObject.Type.NULL) 57 | result.AddField(pi.Name, val); 58 | else Debug.LogWarning("Null for this non-null object, property " + pi.Name + " of class " + obj.GetType().Name + ". Object type is " + pi.PropertyType.Name); 59 | } 60 | } 61 | return result; 62 | } 63 | Debug.LogWarning("trying to save the same data twice"); 64 | return JSONObject.nullJO; 65 | } 66 | } 67 | 68 | /* 69 | * Some helpful code templates for the JSON class 70 | * 71 | * LOOP THROUGH OBJECT 72 | for(int i = 0; i < obj.Count; i++){ 73 | if(obj.keys[i] != null){ 74 | switch((string)obj.keys[i]){ 75 | case "key1": 76 | do stuff with (JSONObject)obj.list[i]; 77 | break; 78 | case "key2": 79 | do stuff with (JSONObject)obj.list[i]; 80 | break; 81 | } 82 | } 83 | } 84 | * 85 | * LOOP THROUGH ARRAY 86 | foreach(JSONObject ob in obj.list) 87 | do stuff with ob; 88 | */ 89 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/JSONTemplates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 1 2 | guid: 3e45009f4fac2044ea997acbf31422ec 3 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/VectorTemplates.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public static partial class JSONTemplates { 4 | 5 | /* 6 | * Vector2 7 | */ 8 | public static Vector2 ToVector2(JSONObject obj) { 9 | float x = obj["x"] ? obj["x"].f : 0; 10 | float y = obj["y"] ? obj["y"].f : 0; 11 | return new Vector2(x, y); 12 | } 13 | public static JSONObject FromVector2(Vector2 v) { 14 | JSONObject vdata = JSONObject.obj; 15 | if(v.x != 0) vdata.AddField("x", v.x); 16 | if(v.y != 0) vdata.AddField("y", v.y); 17 | return vdata; 18 | } 19 | /* 20 | * Vector3 21 | */ 22 | public static JSONObject FromVector3(Vector3 v) { 23 | JSONObject vdata = JSONObject.obj; 24 | if(v.x != 0) vdata.AddField("x", v.x); 25 | if(v.y != 0) vdata.AddField("y", v.y); 26 | if(v.z != 0) vdata.AddField("z", v.z); 27 | return vdata; 28 | } 29 | public static Vector3 ToVector3(JSONObject obj) { 30 | float x = obj["x"] ? obj["x"].f : 0; 31 | float y = obj["y"] ? obj["y"].f : 0; 32 | float z = obj["z"] ? obj["z"].f : 0; 33 | return new Vector3(x, y, z); 34 | } 35 | /* 36 | * Vector4 37 | */ 38 | public static JSONObject FromVector4(Vector4 v) { 39 | JSONObject vdata = JSONObject.obj; 40 | if(v.x != 0) vdata.AddField("x", v.x); 41 | if(v.y != 0) vdata.AddField("y", v.y); 42 | if(v.z != 0) vdata.AddField("z", v.z); 43 | if(v.w != 0) vdata.AddField("w", v.w); 44 | return vdata; 45 | } 46 | public static Vector4 ToVector4(JSONObject obj) { 47 | float x = obj["x"] ? obj["x"].f : 0; 48 | float y = obj["y"] ? obj["y"].f : 0; 49 | float z = obj["z"] ? obj["z"].f : 0; 50 | float w = obj["w"] ? obj["w"].f : 0; 51 | return new Vector4(x, y, z, w); 52 | } 53 | /* 54 | * Matrix4x4 55 | */ 56 | public static JSONObject FromMatrix4x4(Matrix4x4 m) { 57 | JSONObject mdata = JSONObject.obj; 58 | if(m.m00 != 0) mdata.AddField("m00", m.m00); 59 | if(m.m01 != 0) mdata.AddField("m01", m.m01); 60 | if(m.m02 != 0) mdata.AddField("m02", m.m02); 61 | if(m.m03 != 0) mdata.AddField("m03", m.m03); 62 | if(m.m10 != 0) mdata.AddField("m10", m.m10); 63 | if(m.m11 != 0) mdata.AddField("m11", m.m11); 64 | if(m.m12 != 0) mdata.AddField("m12", m.m12); 65 | if(m.m13 != 0) mdata.AddField("m13", m.m13); 66 | if(m.m20 != 0) mdata.AddField("m20", m.m20); 67 | if(m.m21 != 0) mdata.AddField("m21", m.m21); 68 | if(m.m22 != 0) mdata.AddField("m22", m.m22); 69 | if(m.m23 != 0) mdata.AddField("m23", m.m23); 70 | if(m.m30 != 0) mdata.AddField("m30", m.m30); 71 | if(m.m31 != 0) mdata.AddField("m31", m.m31); 72 | if(m.m32 != 0) mdata.AddField("m32", m.m32); 73 | if(m.m33 != 0) mdata.AddField("m33", m.m33); 74 | return mdata; 75 | } 76 | public static Matrix4x4 ToMatrix4x4(JSONObject obj) { 77 | Matrix4x4 result = new Matrix4x4(); 78 | if(obj["m00"]) result.m00 = obj["m00"].f; 79 | if(obj["m01"]) result.m01 = obj["m01"].f; 80 | if(obj["m02"]) result.m02 = obj["m02"].f; 81 | if(obj["m03"]) result.m03 = obj["m03"].f; 82 | if(obj["m10"]) result.m10 = obj["m10"].f; 83 | if(obj["m11"]) result.m11 = obj["m11"].f; 84 | if(obj["m12"]) result.m12 = obj["m12"].f; 85 | if(obj["m13"]) result.m13 = obj["m13"].f; 86 | if(obj["m20"]) result.m20 = obj["m20"].f; 87 | if(obj["m21"]) result.m21 = obj["m21"].f; 88 | if(obj["m22"]) result.m22 = obj["m22"].f; 89 | if(obj["m23"]) result.m23 = obj["m23"].f; 90 | if(obj["m30"]) result.m30 = obj["m30"].f; 91 | if(obj["m31"]) result.m31 = obj["m31"].f; 92 | if(obj["m32"]) result.m32 = obj["m32"].f; 93 | if(obj["m33"]) result.m33 = obj["m33"].f; 94 | return result; 95 | } 96 | /* 97 | * Quaternion 98 | */ 99 | public static JSONObject FromQuaternion(Quaternion q) { 100 | JSONObject qdata = JSONObject.obj; 101 | if(q.w != 0) qdata.AddField("w", q.w); 102 | if(q.x != 0) qdata.AddField("x", q.x); 103 | if(q.y != 0) qdata.AddField("y", q.y); 104 | if(q.z != 0) qdata.AddField("z", q.z); 105 | return qdata; 106 | } 107 | public static Quaternion ToQuaternion(JSONObject obj) { 108 | float x = obj["x"] ? obj["x"].f : 0; 109 | float y = obj["y"] ? obj["y"].f : 0; 110 | float z = obj["z"] ? obj["z"].f : 0; 111 | float w = obj["w"] ? obj["w"].f : 0; 112 | return new Quaternion(x, y, z, w); 113 | } 114 | /* 115 | * Color 116 | */ 117 | public static JSONObject FromColor(Color c) { 118 | JSONObject cdata = JSONObject.obj; 119 | if(c.r != 0) cdata.AddField("r", c.r); 120 | if(c.g != 0) cdata.AddField("g", c.g); 121 | if(c.b != 0) cdata.AddField("b", c.b); 122 | if(c.a != 0) cdata.AddField("a", c.a); 123 | return cdata; 124 | } 125 | public static Color ToColor(JSONObject obj) { 126 | Color c = new Color(); 127 | for(int i = 0; i < obj.Count; i++) { 128 | switch(obj.keys[i]) { 129 | case "r": c.r = obj[i].f; break; 130 | case "g": c.g = obj[i].f; break; 131 | case "b": c.b = obj[i].f; break; 132 | case "a": c.a = obj[i].f; break; 133 | } 134 | } 135 | return c; 136 | } 137 | /* 138 | * Layer Mask 139 | */ 140 | public static JSONObject FromLayerMask(LayerMask l) { 141 | JSONObject result = JSONObject.obj; 142 | result.AddField("value", l.value); 143 | return result; 144 | } 145 | public static LayerMask ToLayerMask(JSONObject obj) { 146 | LayerMask l = new LayerMask {value = (int)obj["value"].n}; 147 | return l; 148 | } 149 | public static JSONObject FromRect(Rect r) { 150 | JSONObject result = JSONObject.obj; 151 | if(r.x != 0) result.AddField("x", r.x); 152 | if(r.y != 0) result.AddField("y", r.y); 153 | if(r.height != 0) result.AddField("height", r.height); 154 | if(r.width != 0) result.AddField("width", r.width); 155 | return result; 156 | } 157 | public static Rect ToRect(JSONObject obj) { 158 | Rect r = new Rect(); 159 | for(int i = 0; i < obj.Count; i++) { 160 | switch(obj.keys[i]) { 161 | case "x": r.x = obj[i].f; break; 162 | case "y": r.y = obj[i].f; break; 163 | case "height": r.height = obj[i].f; break; 164 | case "width": r.width = obj[i].f; break; 165 | } 166 | } 167 | return r; 168 | } 169 | public static JSONObject FromRectOffset(RectOffset r) { 170 | JSONObject result = JSONObject.obj; 171 | if(r.bottom != 0) result.AddField("bottom", r.bottom); 172 | if(r.left != 0) result.AddField("left", r.left); 173 | if(r.right != 0) result.AddField("right", r.right); 174 | if(r.top != 0) result.AddField("top", r.top); 175 | return result; 176 | } 177 | public static RectOffset ToRectOffset(JSONObject obj) { 178 | RectOffset r = new RectOffset(); 179 | for(int i = 0; i < obj.Count; i++) { 180 | switch(obj.keys[i]) { 181 | case "bottom": r.bottom = (int)obj[i].n; break; 182 | case "left": r.left = (int)obj[i].n; break; 183 | case "right": r.right = (int)obj[i].n; break; 184 | case "top": r.top = (int)obj[i].n; break; 185 | } 186 | } 187 | return r; 188 | } 189 | 190 | public static AnimationCurve ToAnimationCurve(JSONObject obj){ 191 | AnimationCurve a = new AnimationCurve(); 192 | if(obj.HasField("keys")){ 193 | JSONObject keys = obj.GetField("keys"); 194 | for(int i =0; i < keys.list.Count;i++){ 195 | a.AddKey(ToKeyframe(keys[i])); 196 | } 197 | } 198 | if(obj.HasField("preWrapMode")) 199 | a.preWrapMode = (WrapMode)((int)obj.GetField("preWrapMode").n); 200 | if(obj.HasField("postWrapMode")) 201 | a.postWrapMode = (WrapMode)((int)obj.GetField("postWrapMode").n); 202 | return a; 203 | } 204 | 205 | public static JSONObject FromAnimationCurve(AnimationCurve a){ 206 | JSONObject result = JSONObject.obj; 207 | result.AddField("preWrapMode", a.preWrapMode.ToString()); 208 | result.AddField("postWrapMode", a.postWrapMode.ToString()); 209 | if(a.keys.Length > 0){ 210 | JSONObject keysJSON = JSONObject.Create(); 211 | for(int i =0; i < a.keys.Length;i++){ 212 | keysJSON.Add(FromKeyframe(a.keys[i])); 213 | } 214 | result.AddField("keys", keysJSON); 215 | } 216 | return result; 217 | } 218 | 219 | public static Keyframe ToKeyframe(JSONObject obj){ 220 | Keyframe k = new Keyframe(obj.HasField("time")? obj.GetField("time").n : 0, obj.HasField("value")? obj.GetField("value").n : 0); 221 | if(obj.HasField("inTangent")) k.inTangent = obj.GetField("inTangent").n; 222 | if(obj.HasField("outTangent")) k.outTangent = obj.GetField("outTangent").n; 223 | if(obj.HasField("tangentMode")) k.tangentMode = (int)obj.GetField("tangentMode").n; 224 | 225 | return k; 226 | } 227 | public static JSONObject FromKeyframe(Keyframe k){ 228 | JSONObject result = JSONObject.obj; 229 | if(k.inTangent != 0) result.AddField("inTangent", k.inTangent); 230 | if(k.outTangent != 0) result.AddField("outTangent", k.outTangent); 231 | if(k.tangentMode != 0) result.AddField("tangentMode", k.tangentMode); 232 | if(k.time != 0) result.AddField("time", k.time); 233 | if(k.value != 0) result.AddField("value", k.value); 234 | return result; 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/VectorTemplates.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 1 2 | guid: 886fb4d7a67d4ce4bb7f51bcc38e20c1 3 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/readme.txt: -------------------------------------------------------------------------------- 1 | ==Author== 2 | [mailto:schoen@defectivestudios.com Matt Schoen] of [http://www.defectivestudios.com Defective Studios] 3 | 4 | ==Download== 5 | [[Media:JSONObject.zip|Download JSONObject.zip]] 6 | 7 | = Intro = 8 | I came across the need to send structured data to and from a server on one of my projects, and figured it would be worth my while to use JSON. When I looked into the issue, I tried a few of the C# implementations listed on [http://json.org json.org], but found them to be too complicated to work with and expand upon. So, I've written a very simple JSONObject class, which can be generically used to encode/decode data into a simple container. This page assumes that you know what JSON is, and how it works. It's rather simple, just go to json.org for a visual description of the encoding format. 9 | 10 | As an aside, this class is pretty central to the [[AssetCloud]] content management system, from Defective Studios. 11 | 12 | Update: The code has been updated to version 1.4 to incorporate user-submitted patches and bug reports. This fixes issues dealing with whitespace in the format, as well as empty arrays and objects, and escaped quotes within strings. 13 | 14 | = Usage = 15 | Users should not have to modify the JSONObject class themselves, and must follow the very simple proceedures outlined below: 16 | 17 | Sample data (in JSON format): 18 | 19 | { 20 | "TestObject": { 21 | "SomeText": "Blah", 22 | "SomeObject": { 23 | "SomeNumber": 42, 24 | "SomeBool": true, 25 | "SomeNull": null 26 | }, 27 | 28 | "SomeEmptyObject": { }, 29 | "SomeEmptyArray": [ ], 30 | "EmbeddedObject": "{\"field\":\"Value with \\\"escaped quotes\\\"\"}" 31 | } 32 | } 33 | 34 | = Features = 35 | 36 | *Decode JSON-formatted strings into a usable data structure 37 | *Encode structured data into a JSON-formatted string 38 | *Interoperable with Dictionary and WWWForm 39 | *Optimized parse/stringify functions -- minimal (unavoidable) garbage creation 40 | *Asynchronous stringify function for serializing lots of data without frame drops 41 | *MaxDepth parsing will skip over nested data that you don't need 42 | *Special (non-compliant) "Baked" object type can store stringified data within parsed objects 43 | *Copy to new JSONObject 44 | *Merge with another JSONObject (experimental) 45 | *Random access (with [int] or [string]) 46 | *ToString() returns JSON data with optional "pretty" flag to include newlines and tabs 47 | *Switch between double and float for numeric storage depending on level of precision needed (and to ensure that numbers are parsed/stringified correctly) 48 | *Supports Infinity and NaN values 49 | *JSONTemplates static class provides serialization functions for common classes like Vector3, Matrix4x4 50 | *Object pool implementation (experimental) 51 | *Handy JSONChecker window to test parsing on sample data 52 | 53 | It should be pretty obvious what this parser can and cannot do. If anyone reading this is a JSON buff (is there such a thing?) please feel free to expand and modify the parser to be more compliant. Currently I am using the .NET System.Convert namespace functions for parsing the data itself. It parses strings and numbers, which was all that I needed of it, but unless the formatting is supported by System.Convert, it may not incorporate all proper JSON strings. Also, having never written a JSON parser before, I don't doubt that I could improve the efficiency or correctness of the parser. It serves my purpose, and hopefully will help you with your project! Let me know if you make any improvements :) 54 | 55 | Also, you JSON buffs (really, who would admit to being a JSON buff...) might also notice from my feature list that this thing isn't exactly to specifications. Here is where it differs: 56 | *"a string" is considered valid JSON. There is an optional "strict" parameter to the parser which will bomb out on such input, in case that matters to you. 57 | *The "Baked" mode is totally made up. 58 | *The "MaxDepth" parsing is totally made up. 59 | *NaN and Infinity aren't officially supported by JSON ([http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript read more] about this issue... I lol'd @ the first comment on the first answer) 60 | *I have no idea about edge cases in my parsing strategy. I have been using this code for about 3 years now and have only had to modify the parser because other people's use cases (still valid JSON) didn't parse correctly. In my experience, anything that this code generates is parsed just fine. 61 | 62 | == Encoding == 63 | 64 | Encoding is something of a hard-coded process. This is because I have no idea what your data is! It would be great if this were some sort of interface for taking an entire class and encoding it's number/string fields, but it's not. I've come up with a few clever ways of using loops and/or recursive methods to cut down of the amount of code I have to write when I use this tool, but they're pretty project-specific. 65 | 66 | Note: This section used to be WRONG! And now it's OLD! Will update later... this will all still work, but there are now a couple of ways to skin this cat. 67 | 68 | 69 | //Note: your data can only be numbers and strings. This is not a solution for object serialization or anything like that. 70 | JSONObject j = new JSONObject(JSONObject.Type.OBJECT); 71 | //number 72 | j.AddField("field1", 0.5); 73 | //string 74 | j.AddField("field2", "sampletext"); 75 | //array 76 | JSONObject arr = new JSONObject(JSONObject.Type.ARRAY); 77 | j.AddField("field3", arr); 78 | 79 | arr.Add(1); 80 | arr.Add(2); 81 | arr.Add(3); 82 | 83 | string encodedString = j.print(); 84 | 85 | 86 | NEW! The constructor, Add, and AddField functions now support a nested delegate structure. This is useful if you need to create a nested JSONObject in a single line. For example: 87 | 88 | DoRequest(URL, new JSONObject(delegate(JSONObject request) { 89 | request.AddField("sort", delegate(JSONObject sort) { 90 | sort.AddField("_timestamp", "desc"); 91 | }); 92 | request.AddField("query", new JSONObject(delegate(JSONObject query) { 93 | query.AddField("match_all", JSONObject.obj); 94 | })); 95 | request.AddField("fields", delegate(JSONObject fields) { 96 | fields.Add("_timestamp"); 97 | }); 98 | }).ToString()); 99 | 100 | 101 | == Decoding == 102 | Decoding is much simpler on the input end, and again, what you do with the JSONObject will vary on a per-project basis. One of the more complicated way to extract the data is with a recursive function, as drafted below. Calling the constructor with a properly formatted JSON string will return the root object (or array) containing all of its children, in one neat reference! The data is in a public ArrayList called list, with a matching key list (called keys!) if the root is an Object. If that's confusing, take a glance over the following code and the print() method in the JSONOBject class. If there is an error in the JSON formatting (or if there's an error with my code!) the debug console will read "improper JSON formatting". 103 | 104 | 105 | 106 | string encodedString = "{\"field1\": 0.5,\"field2\": \"sampletext\",\"field3\": [1,2,3]}"; 107 | JSONObject j = new JSONObject(encodedString); 108 | accessData(j); 109 | //access data (and print it) 110 | void accessData(JSONObject obj){ 111 | switch(obj.type){ 112 | case JSONObject.Type.OBJECT: 113 | for(int i = 0; i < obj.list.Count; i++){ 114 | string key = (string)obj.keys[i]; 115 | JSONObject j = (JSONObject)obj.list[i]; 116 | Debug.Log(key); 117 | accessData(j); 118 | } 119 | break; 120 | case JSONObject.Type.ARRAY: 121 | foreach(JSONObject j in obj.list){ 122 | accessData(j); 123 | } 124 | break; 125 | case JSONObject.Type.STRING: 126 | Debug.Log(obj.str); 127 | break; 128 | case JSONObject.Type.NUMBER: 129 | Debug.Log(obj.n); 130 | break; 131 | case JSONObject.Type.BOOL: 132 | Debug.Log(obj.b); 133 | break; 134 | case JSONObject.Type.NULL: 135 | Debug.Log("NULL"); 136 | break; 137 | 138 | } 139 | } 140 | 141 | 142 | NEW! Decoding now also supports a delegate format which will automatically check if a field exists before processing the data, providing an optional parameter for an OnFieldNotFound response. For example: 143 | 144 | new JSONObject(data); 145 | list.GetField("hits", delegate(JSONObject hits) { 146 | hits.GetField("hits", delegate(JSONObject hits2) { 147 | foreach (JSONObject gameSession in hits2.list) { 148 | Debug.Log(gameSession); 149 | } 150 | }); 151 | }, delegate(string name) { //"name" will be equal to the name of the missing field. In this case, "hits" 152 | Debug.LogWarning("no game sessions"); 153 | }); 154 | 155 | 156 | ===Not So New! (O(n)) Random access!=== 157 | I've added a string and int [] index to the class, so you can now retrieve data as such (from above): 158 | 159 | JSONObject arr = obj["field3"]; 160 | Debug.log(arr[2].n); //Should ouptut "3" 161 | 162 | 163 | ---- 164 | 165 | ---Code omitted from readme--- 166 | 167 | =Change Log= 168 | ==v1.4== 169 | Big update! 170 | *Better GC performance. Enough of that garbage! 171 | **Remaining culprits are internal garbage from StringBuilder.Append/AppendFormat, String.Substring, List.Add/GrowIfNeeded, Single.ToString 172 | *Added asynchronous Stringify function for serializing large amounts of data at runtime without frame drops 173 | *Added Baked type 174 | *Added MaxDepth to parsing function 175 | *Various cleanup refactors recommended by ReSharper 176 | 177 | ==v1.3.2== 178 | *Added support for NaN 179 | *Added strict mode to fail on purpose for improper formatting. Right now this just means that if the parse string doesn't start with [ or {, it will print a warning and return a null JO. 180 | *Changed infinity and NaN implementation to use float and double instead of Mathf 181 | *Handles empty objects/arrays better 182 | *Added a flag to print and ToString to turn on/off pretty print. The define on top is now an override to system-wide disable 183 | ==Earlier Versions== 184 | I'll fill these in later... 185 | [[Category:C Sharp]] 186 | [[Category:Scripts]] 187 | [[Category:Utility]] 188 | [[Category:JSON]] 189 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/JSONObject/readme.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 1 2 | guid: 80b692916bec1664098283aa4425e4d8 3 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f9f796aac617a334aa00205d498f39dd 3 | folderAsset: yes 4 | timeCreated: 1443816459 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding/Mod.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using System.IO; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | public class Mod 10 | { 11 | public string JSONSource; 12 | public string Name; 13 | public string Description; 14 | public string Type; 15 | public string Path; 16 | 17 | public Dictionary Moddables = new Dictionary(); 18 | public Assembly ScriptAssembly; 19 | public List Dependencies = new List(); 20 | 21 | private bool Inited = false; 22 | 23 | public bool Init() 24 | { 25 | if (Inited) 26 | return true; 27 | 28 | if (JSONSource == null) 29 | { 30 | Debug.Log("JSONSource is invalid, returning"); 31 | 32 | return false; 33 | } 34 | 35 | JSONObject Object = JSONObject.Create(JSONSource); 36 | 37 | JSONObject Value = Object.GetField("Name"); 38 | 39 | if(Value != null && Value.type == JSONObject.Type.STRING) 40 | { 41 | Name = Value.str; 42 | } 43 | 44 | Value = Object.GetField("Description"); 45 | 46 | if (Value != null && Value.type == JSONObject.Type.STRING) 47 | { 48 | Description = Value.str; 49 | } 50 | 51 | Value = Object.GetField("Dependencies"); 52 | 53 | if(Value != null && Value.type == JSONObject.Type.STRING) 54 | { 55 | Dependencies.AddRange(Value.str.Split("|".ToArray())); 56 | } 57 | 58 | foreach(string Dependency in Dependencies) 59 | { 60 | if(!ModManager.Instance.LoadMod(Dependency)) 61 | { 62 | Debug.Log("Unable to load Mod Bundle '" + Name + "': Unable to load dependency '" + Dependency + "'"); 63 | 64 | return false; 65 | } 66 | } 67 | 68 | try 69 | { 70 | string AssemblyPath = Path + "/" + Name + ".dll"; 71 | 72 | #if MODMANAGER_COMPILES_MODS 73 | DirectoryInfo SourceDirectory = new DirectoryInfo(Path + "/Scripts/"); 74 | 75 | if (SourceDirectory != null && SourceDirectory.Exists) 76 | { 77 | List SourceFiles = new List(); 78 | 79 | Array.ForEach(SourceDirectory.GetFiles("*.cs", SearchOption.AllDirectories), (FileInfo File) => 80 | { 81 | SourceFiles.Add(File.FullName); 82 | }); 83 | 84 | string DataPath = Application.dataPath; 85 | 86 | #if UNITY_EDITOR 87 | DataPath = Application.dataPath + "/../" + Application.productName + "_Data/"; 88 | #endif 89 | 90 | StringBuilder Parameters = new StringBuilder(); 91 | 92 | Parameters.Append("-target:library -out:\"" + AssemblyPath + "\""); 93 | 94 | Parameters.Append(" -r:\"" + Application.dataPath + "/../Library/ScriptAssemblies/Assembly-CSharp.dll" + "\""); 95 | Parameters.Append(" -r:\"" + DataPath + "/Managed/UnityEngine.dll" + "\""); 96 | Parameters.Append(" -r:\"" + DataPath + "/Managed/UnityEngine.UI.dll" + "\""); 97 | 98 | foreach (string Dependency in Dependencies) 99 | { 100 | Parameters.Append(" -r:\"" + Dependency + "\""); 101 | } 102 | 103 | foreach(string SourceFile in SourceFiles) 104 | { 105 | Parameters.Append(" \"" + SourceFile + "\""); 106 | } 107 | 108 | string GMCSFileName = Application.dataPath + "/../Mono/bin/gmcs"; 109 | 110 | if(Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer) 111 | { 112 | GMCSFileName += ".bat"; 113 | } 114 | 115 | System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo() 116 | { 117 | WorkingDirectory = Environment.CurrentDirectory, 118 | FileName = GMCSFileName, 119 | Arguments = Parameters.ToString(), 120 | UseShellExecute = false, 121 | RedirectStandardError = true, 122 | RedirectStandardOutput = true, 123 | CreateNoWindow = true 124 | }; 125 | 126 | try 127 | { 128 | System.Diagnostics.Process process = System.Diagnostics.Process.Start(startInfo); 129 | process.WaitForExit(); 130 | 131 | if (!process.StandardError.EndOfStream) 132 | { 133 | Debug.Log("Unable to compile Mod Bundle '" + Name + "': " + process.StandardError.ReadToEnd()); 134 | 135 | return false; 136 | } 137 | 138 | if (!process.StandardOutput.EndOfStream) 139 | { 140 | Debug.Log(process.StandardOutput.ReadToEnd()); 141 | } 142 | } 143 | catch (Exception e) 144 | { 145 | Debug.Log("Unable to compile Mod Bundle '" + Name + "'" + e); 146 | 147 | return false; 148 | } 149 | } 150 | #endif 151 | 152 | FileInfo AssemblyFileInfo = new FileInfo(AssemblyPath); 153 | 154 | if(AssemblyFileInfo.Exists) 155 | { 156 | FileStream AssemblyStream = AssemblyFileInfo.OpenRead(); 157 | 158 | BinaryReader Reader = new BinaryReader(AssemblyStream); 159 | 160 | byte[] Content = Reader.ReadBytes((int)Reader.BaseStream.Length); 161 | 162 | ScriptAssembly = Assembly.Load(Content); 163 | 164 | if (ScriptAssembly == null) 165 | { 166 | Debug.Log("Unable to load assembly '" + AssemblyPath + "'"); 167 | 168 | return false; 169 | } 170 | 171 | Type[] Types = ScriptAssembly.GetTypes(); 172 | 173 | Array.ForEach(Types, (Type) => 174 | { 175 | if (Type.IsSubclassOf(typeof(Moddable))) 176 | { 177 | Moddables.Add(Type.Name, Type); 178 | } 179 | }); 180 | } 181 | else 182 | { 183 | Debug.Log("Unable to find assembly for Mod Bundle '" + Name + "'"); 184 | 185 | return false; 186 | } 187 | } 188 | catch (Exception e) 189 | { 190 | Debug.Log("Unable to load script code for Mod Bundle '" + Name + "'"); 191 | 192 | return false; 193 | } 194 | 195 | Inited = true; 196 | 197 | return true; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding/Mod.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a82066a6c33a7414497d8bc0d3d69e5b 3 | timeCreated: 1443552601 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding/ModManager.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Reflection; 6 | using System; 7 | 8 | public class ModManager 9 | { 10 | public Dictionary Mods = new Dictionary(); 11 | 12 | private static ModManager PrivateInstance; 13 | public static ModManager Instance 14 | { 15 | get 16 | { 17 | if(PrivateInstance == null) 18 | { 19 | PrivateInstance = new ModManager(); 20 | PrivateInstance.Initialize(); 21 | } 22 | 23 | return PrivateInstance; 24 | } 25 | } 26 | 27 | public bool LoadMod(string Name) 28 | { 29 | if (Mods.ContainsKey(Name)) 30 | return true; 31 | 32 | string ModDirectory = Environment.CurrentDirectory.Replace('\\', '/') + "/Mods/"; 33 | 34 | try 35 | { 36 | foreach (DirectoryInfo Directory in new DirectoryInfo(ModDirectory).GetDirectories()) 37 | { 38 | FileInfo[] Path = Directory.GetFiles("*.mod"); 39 | 40 | if (Path.Length == 0) 41 | { 42 | Debug.Log("Found no mods bundles at '" + Directory.FullName + "'"); 43 | } 44 | else if(Path[0].Name == Name + ".mod") 45 | { 46 | FileInfo ModPath = Path[0]; 47 | 48 | try 49 | { 50 | FileStream TheFile = ModPath.OpenRead(); 51 | StreamReader TheReader = new StreamReader(TheFile); 52 | 53 | string Content = TheReader.ReadToEnd(); 54 | 55 | TheReader.Close(); 56 | TheFile.Close(); 57 | 58 | Mod TheMod = new Mod(); 59 | TheMod.JSONSource = Content; 60 | TheMod.Path = Directory.FullName.Replace('\\', '/'); 61 | 62 | if (!TheMod.Init()) 63 | { 64 | Debug.Log("Failed to load mod bundle '" + ModPath.FullName + "': Load failure"); 65 | 66 | return false; 67 | } 68 | 69 | Mods.Add(Name, TheMod); 70 | 71 | Debug.Log("Loaded mod '" + ModPath.FullName + "'"); 72 | 73 | return true; 74 | } 75 | catch (Exception e) 76 | { 77 | Debug.Log("Failed to load mod bundle '" + ModPath.FullName + "': " + e.ToString()); 78 | 79 | return false; 80 | } 81 | } 82 | } 83 | } 84 | catch (Exception e) 85 | { 86 | return false; 87 | } 88 | 89 | return false; 90 | } 91 | 92 | void Initialize() 93 | { 94 | #if MODMANAGER_LOADS_FROM_MEMORY 95 | Mod MemoryMod = new Mod(); 96 | 97 | Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), (Assembly) => 98 | { 99 | Array.ForEach(Assembly.GetTypes(), (Type) => 100 | { 101 | if(Type.IsSubclassOf(typeof(Moddable))) 102 | { 103 | MemoryMod.Moddables.Add(Type.Name, Type); 104 | } 105 | }); 106 | }); 107 | 108 | Mods.Add("__MEMORY__", MemoryMod); 109 | #else 110 | string ModDirectory = Environment.CurrentDirectory.Replace('\\', '/') + "/Mods/"; 111 | 112 | Debug.Log("Loading all mods bundles at '" + ModDirectory + "'"); 113 | 114 | int Counter = 0, LoadedCounter = 0; 115 | 116 | try 117 | { 118 | foreach (DirectoryInfo Directory in new DirectoryInfo(ModDirectory).GetDirectories()) 119 | { 120 | FileInfo[] Path = Directory.GetFiles("*.mod"); 121 | 122 | if (Path.Length == 0) 123 | { 124 | Debug.Log("Found no mods bundles at '" + Directory.FullName + "'"); 125 | } 126 | else 127 | { 128 | FileInfo ModPath = Path[0]; 129 | 130 | Counter++; 131 | 132 | if(LoadMod(ModPath.Name.Replace(".mod", ""))) 133 | { 134 | LoadedCounter++; 135 | } 136 | } 137 | } 138 | } 139 | catch(DirectoryNotFoundException) 140 | { 141 | Debug.Log("Found no mods bundles at '" + ModDirectory + "'"); 142 | } 143 | catch (Exception) 144 | { 145 | } 146 | 147 | Debug.Log("Loaded " + LoadedCounter + " mods bundles out of " + Counter); 148 | #endif 149 | } 150 | 151 | public GameObject Spawn(string Name, Vector3 Position) 152 | { 153 | GameObject ClonedObject = new GameObject(); 154 | ClonedObject.transform.position = Position; 155 | 156 | Moddable TheModdable = null; 157 | 158 | try 159 | { 160 | Type TheType = null; 161 | Mod TheMod = null; 162 | 163 | foreach (KeyValuePair Entry in Mods) 164 | { 165 | if (Entry.Value.Moddables.TryGetValue(Name, out TheType)) 166 | { 167 | TheMod = Entry.Value; 168 | 169 | break; 170 | } 171 | } 172 | 173 | if(TheType != null) 174 | { 175 | TheModdable = (Moddable)ClonedObject.AddComponent(TheType); 176 | TheModdable.TheMod = TheMod; 177 | } 178 | } 179 | catch(Exception e) 180 | { 181 | Debug.Log("Unable to start mod '" + Name + "': " + e.Message); 182 | 183 | return null; 184 | } 185 | 186 | return ClonedObject; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding/ModManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 07188f7575d2cba4ba8910486d5e2055 3 | timeCreated: 1443552680 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding/Moddable.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class Moddable : MonoBehaviour 4 | { 5 | public Mod TheMod; 6 | } 7 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Modding/Moddable.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 345d70a19606ffb44b97166fee9da9ba 3 | timeCreated: 1443552680 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bc4a128dd82cd924795b28203e1ad2c6 3 | folderAsset: yes 4 | timeCreated: 1443817503 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods/Character.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b87aad5713e2af340a68d10b41739842 3 | folderAsset: yes 4 | timeCreated: 1443817503 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods/Character/Character.mod: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Character", 3 | "Description": "Character Base" 4 | } 5 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods/Character/Character.mod.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: edb48712f272e1f42bfe080d37b2e4ec 3 | timeCreated: 1443817504 4 | licenseType: Free 5 | AudioImporter: 6 | serializedVersion: 6 7 | defaultSettings: 8 | loadType: 1 9 | sampleRateSetting: 0 10 | sampleRateOverride: 44100 11 | compressionFormat: 1 12 | quality: 1 13 | conversionMode: 0 14 | platformSettingOverrides: {} 15 | forceToMono: 0 16 | normalize: 1 17 | preloadAudioData: 1 18 | loadInBackground: 0 19 | 3D: 1 20 | userData: 21 | assetBundleName: 22 | assetBundleVariant: 23 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods/Character/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 442320ac540e1d147ac58732acc9908d 3 | folderAsset: yes 4 | timeCreated: 1443817503 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods/Character/Scripts/Character.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class Character : Moddable 5 | { 6 | public SpriteRenderer TheSpriteRenderer; 7 | public Sprite TheSprite; 8 | public const float Speed = 10; 9 | 10 | void Start() 11 | { 12 | TheSpriteRenderer = gameObject.AddComponent(); 13 | 14 | #if MODMANAGER_LOADS_FROM_MEMORY 15 | TheSprite = Resources.Load("Textures/Sprite"); 16 | 17 | TheSpriteRenderer.sprite = TheSprite; 18 | #else 19 | StartCoroutine(LoadSprite()); 20 | #endif 21 | } 22 | 23 | void Update() 24 | { 25 | if(Input.GetKey(KeyCode.LeftArrow)) 26 | { 27 | transform.position = new Vector3(transform.position.x - Speed * Time.deltaTime, transform.position.y, transform.position.z); 28 | } 29 | 30 | if (Input.GetKey(KeyCode.RightArrow)) 31 | { 32 | transform.position = new Vector3(transform.position.x + Speed * Time.deltaTime, transform.position.y, transform.position.z); 33 | } 34 | 35 | if (Input.GetKey(KeyCode.UpArrow)) 36 | { 37 | transform.position = new Vector3(transform.position.x, transform.position.y + Speed * Time.deltaTime, transform.position.z); 38 | } 39 | 40 | if (Input.GetKey(KeyCode.DownArrow)) 41 | { 42 | transform.position = new Vector3(transform.position.x, transform.position.y - Speed * Time.deltaTime, transform.position.z); 43 | } 44 | } 45 | 46 | IEnumerator LoadSprite() 47 | { 48 | WWW TheSpriteWWW = new WWW("file://" + TheMod.Path.Replace('\\', '/') + "/Textures/Sprite.png"); 49 | 50 | yield return TheSpriteWWW; 51 | 52 | if(TheSpriteWWW.error != null) 53 | { 54 | Debug.Log("Failed to load sprite '" + TheMod.Path + "/Textures/Sprite.png" + "': " + TheSpriteWWW.error); 55 | } 56 | else 57 | { 58 | TheSprite = Sprite.Create(TheSpriteWWW.texture, new Rect(Vector2.zero, new Vector2(TheSpriteWWW.texture.width, TheSpriteWWW.texture.height)), new Vector2(0.5f, 0.5f)); 59 | 60 | if(TheSprite != null) 61 | { 62 | TheSpriteRenderer.sprite = TheSprite; 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Sample/Assets/Scripts/Mods/Character/Scripts/Character.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c743abd9431ae4342876d4442f54a236 3 | timeCreated: 1443817504 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Sample/Mods/Character/Character.mod: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Character", 3 | "Description": "Character Base" 4 | } 5 | -------------------------------------------------------------------------------- /Sample/Mods/Character/Scripts/Character.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class Character : Moddable 5 | { 6 | public SpriteRenderer TheSpriteRenderer; 7 | public Sprite TheSprite; 8 | public const float Speed = 10; 9 | 10 | void Start() 11 | { 12 | TheSpriteRenderer = gameObject.AddComponent(); 13 | 14 | #if MODMANAGER_LOADS_FROM_MEMORY 15 | TheSprite = Resources.Load("Textures/Sprite"); 16 | 17 | TheSpriteRenderer.sprite = TheSprite; 18 | #else 19 | StartCoroutine(LoadSprite()); 20 | #endif 21 | } 22 | 23 | void Update() 24 | { 25 | if(Input.GetKey(KeyCode.LeftArrow)) 26 | { 27 | transform.position = new Vector3(transform.position.x - Speed * Time.deltaTime, transform.position.y, transform.position.z); 28 | } 29 | 30 | if (Input.GetKey(KeyCode.RightArrow)) 31 | { 32 | transform.position = new Vector3(transform.position.x + Speed * Time.deltaTime, transform.position.y, transform.position.z); 33 | } 34 | 35 | if (Input.GetKey(KeyCode.UpArrow)) 36 | { 37 | transform.position = new Vector3(transform.position.x, transform.position.y + Speed * Time.deltaTime, transform.position.z); 38 | } 39 | 40 | if (Input.GetKey(KeyCode.DownArrow)) 41 | { 42 | transform.position = new Vector3(transform.position.x, transform.position.y - Speed * Time.deltaTime, transform.position.z); 43 | } 44 | } 45 | 46 | IEnumerator LoadSprite() 47 | { 48 | WWW TheSpriteWWW = new WWW("file://" + TheMod.Path.Replace('\\', '/') + "/Textures/Sprite.png"); 49 | 50 | yield return TheSpriteWWW; 51 | 52 | if(TheSpriteWWW.error != null) 53 | { 54 | Debug.Log("Failed to load sprite '" + TheMod.Path + "/Textures/Sprite.png" + "': " + TheSpriteWWW.error); 55 | } 56 | else 57 | { 58 | TheSprite = Sprite.Create(TheSpriteWWW.texture, new Rect(Vector2.zero, new Vector2(TheSpriteWWW.texture.width, TheSpriteWWW.texture.height)), new Vector2(0.5f, 0.5f)); 59 | 60 | if(TheSprite != null) 61 | { 62 | TheSpriteRenderer.sprite = TheSprite; 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Sample/Mods/Character/Textures/Sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/Mods/Character/Textures/Sprite.png -------------------------------------------------------------------------------- /Sample/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.2.0f3 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /Sample/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /Sample/ProjectSettings/UnityAnalyticsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LittleCodingFox/Unity-CSharp-Mod/7bb8ae360380503cabb10d490a01bf5b191b4079/Sample/ProjectSettings/UnityAnalyticsManager.asset --------------------------------------------------------------------------------