├── Documentation.meta
├── LuaVM.cs.meta
├── LuaAPIBase.cs.meta
├── LuaAttributes.cs.meta
├── Documentation
├── LuaDocGenerator.cs.meta
└── LuaDocGenerator.cs
├── .gitignore
├── LICENSE
├── LuaAttributes.cs
├── LuaAPIBase.cs
├── README.md
└── LuaVM.cs
/Documentation.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9f1d5d03c2d6347718f28717b3e0bc68
3 | folderAsset: yes
4 | timeCreated: 1500644749
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/LuaVM.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0568b3618328543a18ba0783a6562e8c
3 | timeCreated: 1500644733
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/LuaAPIBase.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ed27cd522b96747978643987e0c57770
3 | timeCreated: 1500644733
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/LuaAttributes.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7ceb294d14a73461ea862712195cfcb8
3 | timeCreated: 1500644733
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Documentation/LuaDocGenerator.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1bae86bac4ada49b5961c48ccd81d635
3 | timeCreated: 1500644753
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /[Ll]ibrary/
2 | /[Tt]emp/
3 | /[Oo]bj/
4 | /[Bb]uild/
5 | /[Bb]uilds/
6 | /Assets/AssetStoreTools*
7 |
8 | # Visual Studio 2015 cache directory
9 | /.vs/
10 |
11 | # Autogenerated VS/MD/Consulo solution and project files
12 | ExportedObj/
13 | .consulo/
14 | *.csproj
15 | *.unityproj
16 | *.sln
17 | *.suo
18 | *.tmp
19 | *.user
20 | *.userprefs
21 | *.pidb
22 | *.booproj
23 | *.svd
24 | *.pdb
25 |
26 | # Unity3D generated meta files
27 | *.pidb.meta
28 |
29 | # Unity3D Generated File On Crash Reports
30 | sysinfo.txt
31 |
32 | # Builds
33 | *.apk
34 | *.unitypackage
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2017, Harry
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/LuaAttributes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | ///
4 | /// Used to document lua apis
5 | ///
6 | [AttributeUsage(AttributeTargets.Class, Inherited = false)]
7 | public class LuaApi : Attribute
8 | {
9 | public string luaName = string.Empty;
10 | public string description = string.Empty;
11 | public string notes = string.Empty;
12 | }
13 |
14 | ///
15 | /// Used to document lua functions
16 | ///
17 | [AttributeUsage(AttributeTargets.Method, Inherited = false)]
18 | public class LuaApiFunction : Attribute
19 | {
20 | public string name = string.Empty;
21 | public string description = string.Empty;
22 | public string returns = string.Empty;
23 | public string notes = string.Empty;
24 | public string warning = string.Empty;
25 | public string success = string.Empty;
26 | public string codeExample = string.Empty;
27 | }
28 |
29 | ///
30 | /// Used to document lua variables
31 | ///
32 | [AttributeUsage(AttributeTargets.Field)]
33 | public class LuaApiVariable : Attribute
34 | {
35 | public string name = string.Empty;
36 | public string description = string.Empty;
37 | }
38 |
39 | ///
40 | /// Used for generating lua counterparts to C# enums as well as document them
41 | ///
42 | [AttributeUsage(AttributeTargets.Enum)]
43 | public class LuaApiEnum : Attribute
44 | {
45 | public string name = string.Empty;
46 | public string description = string.Empty;
47 | }
48 |
49 | ///
50 | /// Used to document enum values
51 | ///
52 | [AttributeUsage(AttributeTargets.Field)]
53 | public class LuaApiEnumValue : Attribute
54 | {
55 | public string description = string.Empty;
56 | public bool hidden = false;
57 | }
58 |
--------------------------------------------------------------------------------
/LuaAPIBase.cs:
--------------------------------------------------------------------------------
1 | using MoonSharp.Interpreter;
2 |
3 | ///
4 | /// Base class for all lua api systems
5 | ///
6 | public abstract class LuaAPIBase
7 | {
8 | ///
9 | /// The table containing the API functions and variables
10 | ///
11 | protected Table m_ApiTable;
12 |
13 | ///
14 | /// The luaVM the api is attached to
15 | ///
16 | protected LuaVM m_ParentVM;
17 |
18 | ///
19 | /// The name of the Lua API, this will be used for the api name in Lua
20 | /// E.G
21 | ///
22 | /// C#:
23 | /// m_APIName = "ExampleAPI";
24 | ///
25 | /// Lua:
26 | /// ExampleAPI.ExampleFunction()
27 | ///
28 | private readonly string m_APIName;
29 |
30 | ///
31 | /// Derived types must provide their name
32 | ///
33 | /// API name.
34 | protected LuaAPIBase(string APIName)
35 | {
36 | m_APIName = APIName;
37 | }
38 |
39 | ///
40 | /// Derived types must create a function that fills in the api table
41 | ///
42 | protected abstract void InitialiseAPITable();
43 |
44 | ///
45 | /// Adds the API to lua instance.
46 | ///
47 | /// Lua instance.
48 | public void AddAPIToLuaInstance(LuaVM luaInstance)
49 | {
50 | // Set our parent
51 | m_ParentVM = luaInstance;
52 |
53 | // Make a new table
54 | Table apiTable = m_ParentVM.AddGlobalTable (m_APIName);
55 | if (apiTable != null)
56 | {
57 | // Set the api table
58 | m_ApiTable = apiTable;
59 |
60 | // Hand over to the API derived type to fill in the table
61 | InitialiseAPITable();
62 | }
63 | else
64 | {
65 | Logger.Log (Channel.Lua, "Failed to Initilise API {0}", m_APIName);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unity-Lua
2 | A wrapper around MoonSharp that allows easy development of moddable Unity games
3 |
4 | # Installation
5 | * Create "Assets/Plugins/Lua" in your Unity project
6 | * Download and place the contents of this repository to the newly created folder
7 |
8 | # Dependancies
9 | [MoonSharp](http://www.moonsharp.org/) & [Unity-Logger](https://github.com/Semaeopus/Unity-Logger)
10 |
11 | # Code Breakdown
12 |
13 | This codebase allows Unity developers to easily create game specific modding apis for their Unity games!
14 | It was created during the development of [Off Grid](http://www.offgridthegame.com)
15 |
16 | ## Executing Lua Code
17 | LuaVM is the main Lua interface, it's the class you use to run Lua scripts or individual Lua command strings.
18 |
19 | ### Executing a string
20 |
21 | ```C#
22 | const string luaCode = @"
23 | -- Lua Code
24 | num = 1 + 1
25 | print(num)
26 | ";
27 |
28 | LuaVM vm = new LuaVM();
29 | vm.ExecuteString(luaCode); // Prints 2
30 | ```
31 |
32 | ### Executing a script
33 |
34 | Most of the time you'll want to load and execute Lua code from a script file written to disk, here's an example of how that's achieved.
35 |
36 | Contents of fruits.lua
37 | ```lua
38 | fruits = {
39 | "apple",
40 | "banana",
41 | }
42 |
43 | GetRandomFruit = function()
44 | return fruits[math.random(1, #fruits)]
45 | end
46 | ```
47 |
48 | Here's some code that loads the fruits table from the script, as well as getting a reference to the GetRandomFruit and calling it.
49 | ```C#
50 | LuaVM vm = new LuaVM();
51 | vm.ExecuteScript("/path/to/fruits.lua");
52 |
53 | // Get table to iterate
54 | Table fruitTable = vm.GetGlobalTable("fruits");
55 | foreach (DynValue fruit in fruitTable.Values)
56 | {
57 | Debug.Log(fruit.String); // Prints "apple" then "banana"
58 | }
59 |
60 | // Or get a lua function and call it
61 | DynValue fruitFunction = vm.GetGlobal("GetRandomFruit");
62 | Debug.Log(vm.Call(fruitFunction).String); // Prints return of GetRandomFruit
63 | ```
64 |
65 | ## Creating an API
66 | Creating an api with this framework is incredibly simple, let's say we're making a game where the player can interact with supermarkets.
67 | We of course want this game to be moddable, so let's write up information about the super market in Lua, things like stock and name for a start.
68 |
69 | Here's an example of a very simple Lua api to let the players get random items that can go in their shops stocklist.
70 |
71 | ```C#
72 | [LuaApi(
73 | luaName = "SuperMarket",
74 | description = "This is a test lua api")]
75 | public class SuperMarketAPI : LuaAPIBase
76 | {
77 | private readonly List m_Veggies = new List
78 | {
79 | "Aubergine",
80 | "Broccoli",
81 | "Cauliflower",
82 | "Carrot",
83 | "Kale",
84 | };
85 |
86 | private readonly List m_Fruits = new List
87 | {
88 | "Strawberry",
89 | "Grape",
90 | "Lychee",
91 | "Melon",
92 | "Apple",
93 | };
94 |
95 | public SuperMarketAPI()
96 | : base("SuperMarket")
97 | {
98 | }
99 |
100 | protected override void InitialiseAPITable()
101 | {
102 | m_ApiTable["GetRandomVeg"] = (System.Func) (Lua_GetRandomVeggies);
103 | m_ApiTable["GetRandomFruit"] = (System.Func) (Lua_GetRandomFruits);
104 | m_ApiTable["MaxStock"] = MaxStock;
105 | }
106 |
107 | [LuaApiEnumValue(description = "The max stock any shop should contain")]
108 | private const int MaxStock = 10;
109 |
110 | [LuaApiFunction(
111 | name = "GetRandomVeg",
112 | description = "Returns a random vegetable that can be stocked by an in-game shop"
113 | )]
114 | private string Lua_GetRandomVeggies()
115 | {
116 | int randomIndex = Random.Range(0, m_Veggies.Count - 1);
117 | return m_Veggies[randomIndex];
118 | }
119 |
120 | [LuaApiFunction(
121 | name = "GetRandomFruit",
122 | description = "Returns a random fruit that can be stocked by an in-game shop"
123 | )]
124 | private string Lua_GetRandomFruits()
125 | {
126 | int randomIndex = Random.Range(0, m_Fruits.Count - 1);
127 | return m_Fruits[randomIndex];
128 | }
129 | }
130 |
131 | ```
132 | Lua apis become available to LuaVM instances by default, the first time a LuaVM is created reflection is used to cache all types that derive from LuaAPIBase.
133 | Here's a Lua script that uses the brand new SuperMarket api:
134 |
135 | ```lua
136 | Shop = {
137 | Name = "Dumpling's Super Store",
138 | Stock = {},
139 | }
140 |
141 | -- Generate the stock items
142 | for i= 1, SuperMarket.MaxStock do
143 | table.insert(Shop.Stock, SuperMarket.GetRandomVeg())
144 | table.insert(Shop.Stock, SuperMarket.GetRandomFruit())
145 | end
146 | ```
147 |
148 | Corrisponding C# code
149 |
150 | ```C#
151 | LuaVM vm = new LuaVM();
152 | vm.ExecuteScript("/path/to/DumplingsStore.lua");
153 |
154 | // Get the shops name
155 | string shopName = vm.GetGlobal("Shop", "Name").String;
156 | Debug.Log(shopName); // Prints "Dumpling's Super Store"
157 |
158 | // Get Items in stock
159 | Table fruitTable = vm.GetGlobalTable("Shop", "Stock");
160 | foreach (DynValue item in fruitTable.Values)
161 | {
162 | Debug.Log(item.String);
163 | }
164 |
165 | ```
166 |
167 | So there's a really simple example of how you can add arbitrary apis to your game for use in Lua.
168 | LuaApiBase uses the string passed into its constructor as the true name of the Lua api, in this example that's "SuperMarket".
169 | It then allows the derived type to fill in m_ApiTable, note how above this is really a MoonSharp wrapper around a Lua table, meaning that it's not just functions.
170 | Above we've used a MaxStock int which whilst it's currently a const, could be set by calling into a gameplay system.
171 |
172 | ### Exposing Enums
173 | One thing that can be a pain when dealing with lua is having to use raw ints rather than enums, using this framework fixes this issue by allowing the automatic generation of Lua versions of your enums.
174 | Let's say in our game the player can adopt pets, and we want modders to be able to create new pets with different personalities and abilities.
175 | Here's how you could go about exposing an enum type to your modders in order to know how to render the correct model/sprite.
176 |
177 | ```C#
178 | [LuaApiEnum(
179 | name = "PetType",
180 | description = "Defines what type a pet is")]
181 | public enum PetType
182 | {
183 | [LuaApiEnumValue(
184 | description = "Aloof and occasionally affectionate")]
185 | Cat,
186 |
187 | [LuaApiEnumValue(
188 | description = "A loyal best friend")]
189 | Dog,
190 |
191 | [LuaApiEnumValue(
192 | description = "Slow and a bit snappy")]
193 | Turtle,
194 |
195 | [LuaApiEnumValue(
196 | description = "Cute, small and loves grain")]
197 | Hamster,
198 |
199 | // Not ready for modders yet!
200 | [LuaApiEnumValue(hidden = true)]
201 | Dragon,
202 | }
203 | ```
204 |
205 | In a similar vein to how Lua apis are automatically detected by LuaVM, any enum with the LuaApiEnum is automatically exposed to all lua scripts run from LuaVM.
206 | So modders can now use it like so:
207 |
208 | ```Lua
209 | Pet = {
210 | Name = "Rex",
211 | Type = PetType.Dog,
212 | attack = function(target)
213 | -- Attack Logic
214 | end
215 | }
216 | ```
217 |
218 | ** Note: ** It's worth noting that enum values can be hidden from exposure by the LuaApiEnumValue attributes hidden value, just as we've done with the Dragon type above.
219 |
220 | ## Options
221 | The LuaVM constructor optionally takes in an instance of the VMSettings flag, this allows the user to attach only attach apis, enums, both or none at all.
222 | If you know you're not going to need any of the attachments, it's more performant to use ```new LuaVM(VMSettings.None)```
223 |
224 | # Documentation Creation
225 | One of this frameworks most handy features is its automatic documentation creation.
226 | Document creation is triggered by using the built in Unity menu items:
227 |
228 | 
229 |
230 | Currently the framework supports the creation of the following documentation:
231 | * MediaWiki pages per api
232 | * Visual Studio Code snippets for auto complete
233 | * Atom Snippets for auto complete
234 |
235 | These are all created by using the attributes attached to your apis, api function, api variables and enums.
236 | All the available attributes are used in the code snippets above, however here's a quick reference:
237 |
238 | | Attribute | Use |
239 | |:---------:|---|
240 | | LuaApi | Above LuaApiBase derived type |
241 | | LuaApiFunction | Above functions that are part of the Lua api table |
242 | | LuaApiVariable | Above variables that are part of the Lua api table |
243 | | LuaApiEnum | Above enums that you want to be attached to LuaVMs |
244 | | LuaApiEnumValue | Above enums values that you'd like to describe or hide |
245 |
246 | Adding more documentation formats is very easy, why not try adding another doucmentation type yourself in LuaDocGenerator.cs!
247 |
--------------------------------------------------------------------------------
/LuaVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using MoonSharp.Interpreter;
4 | using MoonSharp.VsCodeDebugger;
5 | using MoonSharp.Interpreter.Loaders;
6 | using System.Linq;
7 | using System.Reflection;
8 |
9 | ///
10 | /// Wrapper around the Moonsharp API.
11 | /// Implements heavy error checking and logging for end users
12 | ///
13 | public class LuaVM
14 | {
15 | ///
16 | /// The Moonsharp script object
17 | ///
18 | private readonly Script m_LuaScript = null;
19 | private static Type[] m_APITypeList = null;
20 | private LuaAPIBase[] m_APIList = null;
21 | private static DynValue m_EnumTables = null;
22 |
23 | ///
24 | /// Settings to control the behaviour of the VM
25 | ///
26 | [Flags]
27 | public enum VMSettings : uint
28 | {
29 | ///
30 | /// No custom code will be attached
31 | ///
32 | None = 0,
33 |
34 | ///
35 | /// We'll attach anything deriving from LuaAPIBase
36 | ///
37 | AttachAPIs = 1 << 0,
38 |
39 | ///
40 | /// We'll attach any enum with the LuaApiEnum attribute
41 | ///
42 | AttachEnums = 1 << 1,
43 |
44 | ///
45 | /// Attach everything we know about
46 | ///
47 | AttachAll = ~0u,
48 | }
49 |
50 | ///
51 | /// The Moonsharp remote debugger service
52 | ///
53 | private static MoonSharpVsCodeDebugServer s_RemoteDebugger = null;
54 |
55 | ///
56 | /// Default to attaching all apis and enums
57 | ///
58 | public LuaVM()
59 | : this(VMSettings.AttachAll)
60 | {
61 | }
62 |
63 | ///
64 | /// Default settings are a soft sandbox and setting up the file system script loader
65 | ///
66 | public LuaVM(VMSettings vmSettings)
67 | {
68 | m_LuaScript =
69 | new Script(CoreModules.Preset_SoftSandbox)
70 | {
71 | Options =
72 | {
73 | ScriptLoader = new FileSystemScriptLoader(),
74 | DebugPrint = log => Logger.Log (Channel.LuaNative, log),
75 | }
76 | };
77 |
78 | if ((vmSettings & VMSettings.AttachAPIs) == VMSettings.AttachAPIs)
79 | {
80 | AttachAPIS();
81 | }
82 |
83 | if ((vmSettings & VMSettings.AttachEnums) == VMSettings.AttachEnums)
84 | {
85 | AttachLuaEnums();
86 | }
87 | }
88 |
89 | ///
90 | /// Add a new global Lua table and if successful return a reference to it
91 | ///
92 | ///
93 | ///
94 | public Table AddGlobalTable(string tableName)
95 | {
96 | Table table = null;
97 |
98 | if(SetGlobal(tableName, DynValue.NewTable(m_LuaScript)))
99 | {
100 | table = GetGlobal(tableName).Table;
101 | }
102 | else
103 | {
104 | Logger.Log(Channel.Lua, Priority.FatalError, "Failed to add global Lua table {0}", tableName);
105 | }
106 |
107 | return table;
108 | }
109 |
110 | ///
111 | /// Attempts to set a global variable with key and value
112 | ///
113 | ///
114 | ///
115 | /// true if value was set successfully
116 | public bool SetGlobal(string key, object value)
117 | {
118 | bool didSet = false;
119 | try
120 | {
121 | m_LuaScript.Globals[key] = value;
122 | didSet = true;
123 | }
124 | catch (InterpreterException ex)
125 | {
126 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua SetGlobal error: {0}", ex.DecoratedMessage);
127 | }
128 | return didSet;
129 | }
130 |
131 | ///
132 | /// Attempts to retrive a value from the Lua globals
133 | ///
134 | ///
135 | /// null if failure occurs, else the requested value as a DynValue
136 | public DynValue GetGlobal(string key)
137 | {
138 | DynValue result = DynValue.Nil;
139 | try
140 | {
141 | result = m_LuaScript.Globals.Get(key);
142 | }
143 | catch
144 | {
145 | Logger.Log(Channel.Lua, Priority.FatalError, "Failed to get Lua global {0}", key);
146 | }
147 |
148 | return result;
149 | }
150 |
151 | ///
152 | /// Attempts to retrive a value from the Lua globals, allowing the user to pass parent and children names in
153 | ///
154 | /// The global.
155 | /// Keys.
156 | public DynValue GetGlobal(params object[] keys)
157 | {
158 | DynValue result = DynValue.Nil;
159 | try
160 | {
161 | result = m_LuaScript.Globals.Get(keys);
162 | }
163 | catch
164 | {
165 | Logger.Log(Channel.Lua, Priority.FatalError, "Failed to get Lua global at '{0}'",
166 | string.Join(", ", Array.ConvertAll(keys, input => input.ToString())));
167 | }
168 |
169 | return result;
170 | }
171 |
172 | ///
173 | /// Attempts to retrive a table from the Lua globals
174 | ///
175 | /// The global table.
176 | /// Key.
177 | public Table GetGlobalTable(string key)
178 | {
179 | Table result = null;
180 | DynValue tableDyn = GetGlobal (key);
181 | if (tableDyn != null)
182 | {
183 | if(tableDyn.Type == DataType.Table)
184 | {
185 | result = tableDyn.Table;
186 | }
187 | else
188 | {
189 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua global {0} is not type table, has type {1}", key, tableDyn.Type.ToString());
190 | }
191 | }
192 | return result;
193 | }
194 |
195 | ///
196 | /// Attempts to retrive a table from the Lua globals, allowing the user to pass parent and children names in
197 | ///
198 | /// The global table.
199 | /// Key.
200 | public Table GetGlobalTable(params object[] keys)
201 | {
202 | Table result = null;
203 | DynValue tableDyn = GetGlobal (keys);
204 | if (tableDyn != null)
205 | {
206 | if(tableDyn.Type == DataType.Table)
207 | {
208 | result = tableDyn.Table;
209 | }
210 | else
211 | {
212 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua global {0} is not type table, has type {1}", keys, tableDyn.Type.ToString());
213 | }
214 | }
215 | return result;
216 | }
217 |
218 | ///
219 | /// Return the global table for this vm
220 | ///
221 | ///
222 | public Table GetGlobalsTable()
223 | {
224 | return m_LuaScript.Globals;
225 | }
226 |
227 | ///
228 | /// Attempts to run the lua command passed in
229 | ///
230 | ///
231 | /// Null if an error occured otherwise will return the result of the executed lua code
232 | public DynValue ExecuteString(string command)
233 | {
234 | DynValue result = DynValue.Nil;
235 |
236 | try
237 | {
238 | result = m_LuaScript.DoString(command);
239 | }
240 | catch (InterpreterException ex)
241 | {
242 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua ExecuteString error: {0}", ex.DecoratedMessage);
243 | }
244 |
245 | return result;
246 | }
247 |
248 | ///
249 | /// Attempts to run the lua script passed in
250 | ///
251 | ///
252 | /// Null if an error occured otherwise will return the result of the executed lua code
253 | public DynValue ExecuteScript(string filePath)
254 | {
255 | DynValue result = DynValue.Nil;
256 |
257 | try
258 | {
259 | result = m_LuaScript.DoFile(filePath);
260 | }
261 | catch (InterpreterException ex)
262 | {
263 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua ExecuteScript error: {0}", ex.DecoratedMessage);
264 | }
265 | catch (Exception ex)
266 | {
267 | Logger.Log(Channel.Lua, Priority.FatalError, "System ExecuteScript error: {0}", ex.Message);
268 | }
269 |
270 | return result;
271 | }
272 |
273 | ///
274 | /// Attempts to load a lua script
275 | ///
276 | ///
277 | /// Null if an error occured otherwise will return the DynValue of the script. This can be passed to Call()
278 | public DynValue LoadScript(string filePath)
279 | {
280 | DynValue result = DynValue.Nil;
281 |
282 | try
283 | {
284 | result = m_LuaScript.LoadFile(filePath);
285 | }
286 | catch (InterpreterException ex)
287 | {
288 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua ExecuteString error: {0}", ex.DecoratedMessage);
289 | }
290 | catch (Exception ex)
291 | {
292 | Logger.Log(Channel.Lua, Priority.FatalError, "System ExecuteString error: {0}", ex.Message);
293 | }
294 |
295 | return result;
296 | }
297 |
298 | ///
299 | /// Attemps to load a string containing lua code
300 | ///
301 | ///
302 | /// Null if an error occured otherwise will return the DynValue of the script. This can be passed to Call()
303 | public DynValue LoadString(string luaString)
304 | {
305 | DynValue result = DynValue.Nil;
306 |
307 | try
308 | {
309 | result = m_LuaScript.LoadString(luaString);
310 | }
311 | catch (InterpreterException ex)
312 | {
313 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua ExecuteString error: {0}", ex.DecoratedMessage);
314 | }
315 | catch (Exception ex)
316 | {
317 | Logger.Log(Channel.Lua, Priority.FatalError, "System ExecuteString error: {0}", ex.Message);
318 | }
319 |
320 | return result;
321 | }
322 |
323 | ///
324 | /// Call a lua function via DynValue
325 | ///
326 | ///
327 | ///
328 | /// Null if call fails of function is invalid, else the result of the function
329 | public DynValue Call(DynValue luaFunc, params object[] args)
330 | {
331 | DynValue result = DynValue.Nil;
332 |
333 | if (luaFunc.IsNotNil() && luaFunc.Type == DataType.Function)
334 | {
335 | try
336 | {
337 | result = m_LuaScript.Call(luaFunc, args);
338 | }
339 | catch (ScriptRuntimeException ex)
340 | {
341 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua Call error: {0}", ex.DecoratedMessage);
342 | }
343 | }
344 | else
345 | {
346 | Logger.Log(Channel.Lua, Priority.FatalError, "Invalid lua function passed to LuaVM::Call");
347 | }
348 |
349 | return result;
350 | }
351 |
352 | ///
353 | /// Call a lua function via name
354 | ///
355 | /// Function name.
356 | /// Arguments.
357 | public DynValue Call(string functionName, params object[] args)
358 | {
359 | DynValue result = DynValue.Nil;
360 |
361 | if (!string.IsNullOrEmpty(functionName))
362 | {
363 | DynValue func = GetGlobal(functionName);
364 | if (func.Type == DataType.Function)
365 | {
366 | try
367 | {
368 | result = m_LuaScript.Call(func, args);
369 | }
370 | catch (InterpreterException ex)
371 | {
372 | Logger.Log(Channel.Lua, Priority.FatalError, "Lua error calling function {0}: {1}", functionName, ex.DecoratedMessage);
373 | }
374 | }
375 | else
376 | {
377 | Logger.Log(Channel.Lua, Priority.FatalError, "Failed to find lua function '{0}'", functionName);
378 | }
379 | }
380 | return result;
381 | }
382 |
383 | ///
384 | /// Starts the remote debugger and opens the interface in the users browser
385 | ///
386 | public static void StartRemoteDebugger()
387 | {
388 | if (s_RemoteDebugger == null)
389 | {
390 | s_RemoteDebugger = new MoonSharpVsCodeDebugServer();
391 | s_RemoteDebugger.Start ();
392 | }
393 | }
394 |
395 | ///
396 | /// Attaches to the remove debugger service
397 | ///
398 | public void AttachDebugger()
399 | {
400 | if(s_RemoteDebugger != null)
401 | {
402 | s_RemoteDebugger.AttachToScript (m_LuaScript, "Lua script");
403 | }
404 | else
405 | {
406 | Logger.Log(Channel.Lua, Priority.Error, "Tried to attach script to debugger before debugger was started");
407 | }
408 | }
409 |
410 | ///
411 | /// Return the current script object
412 | ///
413 | public Script GetScriptObject()
414 | {
415 | return m_LuaScript;
416 | }
417 |
418 | ///
419 | /// Create static api list and attach to this VM
420 | ///
421 | private void AttachAPIS()
422 | {
423 | if (m_APITypeList == null)
424 | {
425 | List apiTypeList = new List();
426 | foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
427 | {
428 | apiTypeList.AddRange(assembly.GetTypes()
429 | .Where(type => type.IsSubclassOf(typeof(LuaAPIBase))));
430 | }
431 |
432 | m_APITypeList = apiTypeList.ToArray();
433 | }
434 |
435 | m_APIList = new LuaAPIBase[m_APITypeList.Length];
436 |
437 | for (int i = 0; i < m_APITypeList.Length; ++i)
438 | {
439 | m_APIList[i] = Activator.CreateInstance(m_APITypeList[i]) as LuaAPIBase;
440 | }
441 |
442 | // Iterate apis and tell them to update this lua vm
443 | foreach (LuaAPIBase api in m_APIList)
444 | {
445 | api.AddAPIToLuaInstance(this);
446 | }
447 | }
448 |
449 | ///
450 | /// Create the reusable enum prime table and attach to this VM
451 | ///
452 | private void AttachLuaEnums()
453 | {
454 | if (m_EnumTables == null)
455 | {
456 | // Create a new prime table
457 | // Prime tables can be shared between scripts
458 | m_EnumTables = DynValue.NewPrimeTable();
459 |
460 | // Get all enums with the lua attribute
461 | List luaEnumList = new List();
462 | foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
463 | {
464 | luaEnumList.AddRange((assembly.GetTypes()
465 | .Where(luaEnumType => Attribute.IsDefined(luaEnumType, typeof(LuaApiEnum)))));
466 | }
467 |
468 | foreach (Type enumType in luaEnumList)
469 | {
470 | // Get the attribute
471 | LuaApiEnum apiEnumAttrib = (LuaApiEnum) enumType.GetCustomAttributes(typeof(LuaApiEnum), false)[0];
472 |
473 | // Create the table for this enum and get a reference to it
474 | m_EnumTables.Table.Set(apiEnumAttrib.name, DynValue.NewPrimeTable());
475 | Table enumTable = m_EnumTables.Table.Get(apiEnumAttrib.name).Table;
476 |
477 | // Foreach value in the enum list
478 | foreach (var enumValue in Enum.GetValues(enumType))
479 | {
480 | var memberInfo = enumType.GetMember(enumValue.ToString());
481 | var attribute = memberInfo[0].GetCustomAttributes(typeof(LuaApiEnumValue), false);
482 |
483 | // Double check they've not been flagged as hidden
484 | if (attribute.Length > 0 && ((LuaApiEnumValue) attribute[0]).hidden)
485 | {
486 | continue;
487 | }
488 |
489 | enumTable.Set(enumValue.ToString(), DynValue.NewNumber((int) enumValue));
490 | }
491 | }
492 | }
493 |
494 | // Iterate through the enum cache and copy the values into our globals
495 | foreach (var enumPair in m_EnumTables.Table.Pairs)
496 | {
497 | m_LuaScript.Globals.Set(enumPair.Key, enumPair.Value);
498 | }
499 | }
500 | }
501 |
--------------------------------------------------------------------------------
/Documentation/LuaDocGenerator.cs:
--------------------------------------------------------------------------------
1 | #if UNITY_EDITOR
2 | using System;
3 | using UnityEditor;
4 | using System.Collections.Generic;
5 | using System.Reflection;
6 | using System.Linq;
7 | using System.IO;
8 | using System.Text;
9 | using UnityEngine;
10 |
11 | ///
12 | /// Handles generating Lua documentation for varying sources via reflection
13 | ///
14 | public static class LuaDocGenerator
15 | {
16 | ///
17 | /// Info about a Lua API
18 | ///
19 | private class LuaApiInfo
20 | {
21 | public LuaApi Attribute;
22 | public Type Type;
23 |
24 | public readonly List functions = new List();
25 | public readonly List variables = new List();
26 | }
27 |
28 | ///
29 | /// Info about a Lua function
30 | ///
31 | private class LuaFunction_Info
32 | {
33 | public LuaApiFunction Attribute;
34 | public MethodInfo MethodInfo;
35 | }
36 |
37 | ///
38 | /// Info about a Lua variable
39 | ///
40 | private class LuaVariableInfo
41 | {
42 | public LuaApiVariable Attribute;
43 | public FieldInfo FieldInfo;
44 | }
45 |
46 | ///
47 | /// Info about a Lua enum
48 | ///
49 | private class LuaEnumInfo
50 | {
51 | public LuaApiEnum Attribute;
52 | public Type ApiType;
53 | public readonly List values = new List();
54 | }
55 |
56 | ///
57 | /// Info about a Lua enum value
58 | ///
59 | private class LuaEnumValueInfo
60 | {
61 | public LuaApiEnumValue Attribute;
62 | public string StringValue;
63 | }
64 |
65 | ///
66 | /// Grabs information about all the Lua Apis in the code base
67 | /// Å
68 | ///
69 | private static List GetAllApiInfo()
70 | {
71 | List apiTypeList = new List();
72 | foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
73 | {
74 | apiTypeList.AddRange(assembly.GetTypes()
75 | .Where(type => type.IsSubclassOf(typeof(LuaAPIBase))));
76 | }
77 |
78 | List apiInfoArray = new List(apiTypeList.Count);
79 |
80 | // Iterate all types deriving from LuaApiBase
81 | foreach (Type apiType in apiTypeList)
82 | {
83 | LuaApi[] apiAttribs = apiType.GetCustomAttributes(typeof(LuaApi), false) as LuaApi[];
84 | if (apiAttribs != null && apiAttribs.Length > 0)
85 | {
86 | LuaApiInfo apiInfo = new LuaApiInfo
87 | {
88 | Type = apiType,
89 | Attribute = apiAttribs[0]
90 | };
91 |
92 | // Iterate all methods
93 | foreach(MethodInfo methodType in apiType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
94 | {
95 | // Grab the LuaApiFunction if it exists
96 | LuaApiFunction[] functionAttribs = methodType.GetCustomAttributes(typeof(LuaApiFunction), false) as LuaApiFunction[];
97 |
98 | if (functionAttribs != null && functionAttribs.Length > 0)
99 | {
100 | apiInfo.functions.Add(new LuaFunction_Info
101 | {
102 | Attribute = functionAttribs[0],
103 | MethodInfo = methodType,
104 | });
105 | }
106 | }
107 |
108 | // Iterate all variables
109 | foreach(FieldInfo fieldInfo in apiType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic |
110 | BindingFlags.Public | BindingFlags.Static))
111 | {
112 | LuaApiVariable[] varaibleAttribs =
113 | fieldInfo.GetCustomAttributes(typeof(LuaApiVariable), false) as LuaApiVariable[];
114 |
115 | if (varaibleAttribs != null && varaibleAttribs.Length > 0)
116 | {
117 | apiInfo.variables.Add(new LuaVariableInfo{
118 | Attribute = varaibleAttribs[0],
119 | FieldInfo = fieldInfo,
120 | });
121 | }
122 | }
123 |
124 | apiInfoArray.Add(apiInfo);
125 | }
126 | }
127 |
128 | return apiInfoArray;
129 | }
130 |
131 | ///
132 | /// Grabs all information about enums that will be bound to Lua
133 | ///
134 | ///
135 | private static List GetAllEnums()
136 | {
137 | List luaEnumList = new List();
138 | foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
139 | {
140 | luaEnumList.AddRange((assembly.GetTypes()
141 | .Where(luaEnumType => Attribute.IsDefined(luaEnumType, typeof(LuaApiEnum)))));
142 | }
143 |
144 | List result = new List(luaEnumList.Count);
145 |
146 | foreach (Type enumType in luaEnumList)
147 | {
148 | LuaEnumInfo enumInfo = new LuaEnumInfo
149 | {
150 | ApiType = enumType,
151 | Attribute = (LuaApiEnum) enumType.GetCustomAttributes(typeof(LuaApiEnum), false)[0],
152 | };
153 |
154 | foreach (var enumVal in Enum.GetValues(enumType))
155 | {
156 | var valueInfo = enumType.GetMember(enumVal.ToString());
157 | var attribs = valueInfo[0].GetCustomAttributes(typeof(LuaApiEnumValue), false);
158 | enumInfo.values.Add(new LuaEnumValueInfo
159 | {
160 | Attribute = (LuaApiEnumValue)(attribs.Length > 0 ? attribs[0] : null),
161 | StringValue = enumVal.ToString(),
162 | });
163 | }
164 |
165 | result.Add(enumInfo);
166 | }
167 |
168 | return result;
169 | }
170 |
171 | ///
172 | /// Generate MediaWiki pages for all Lua apis and enums
173 | ///
174 | [MenuItem("Lua/Docs/Generate MediaWiki Docs")]
175 | private static void GenerateMediaWikiDocs()
176 | {
177 | Logger.Log (Channel.Lua, "Beginning Lua doc generation for MediaWiki");
178 |
179 | string documentationPath =
180 | EditorUtility.OpenFolderPanel("Choose a location to place wiki files", string.Empty, string.Empty);
181 |
182 | if (!string.IsNullOrEmpty(documentationPath))
183 | {
184 | // Iterate all types deriving from LuaApiBase
185 | foreach (LuaApiInfo api in GetAllApiInfo())
186 | {
187 | // Write out the header for the api type
188 | LuaApi luaApiDetails = api.Attribute;
189 |
190 | string apiDocPath = string.Format("{0}/{1}.mediawiki", documentationPath, luaApiDetails.luaName);
191 |
192 | StreamWriter documentation = File.CreateText(apiDocPath);
193 |
194 | Logger.Log(Channel.Lua, "Generating documentation for api: {0}", luaApiDetails.luaName);
195 |
196 | documentation.WriteLine("= {0} =", luaApiDetails.luaName);
197 |
198 | if (!string.IsNullOrEmpty(luaApiDetails.description))
199 | {
200 | documentation.WriteLine("== Description ==");
201 | documentation.WriteLine(luaApiDetails.description);
202 | }
203 | if (!string.IsNullOrEmpty(luaApiDetails.notes))
204 | {
205 | documentation.WriteLine("== Notes ==");
206 | documentation.WriteLine(luaApiDetails.notes);
207 | }
208 |
209 |
210 | documentation.WriteLine("== Functions ==");
211 |
212 | // Iterate all methods
213 | foreach (LuaFunction_Info method in api.functions)
214 | {
215 | // Grab the LuaApiFunction if it exists
216 | LuaApiFunction luaMethodDetails = method.Attribute;
217 |
218 | Logger.Log(Channel.Lua, "\t {0}", luaMethodDetails.name);
219 |
220 | documentation.WriteLine("=== {0} ===", luaMethodDetails.name);
221 |
222 | string functionSig = GetCleanFunctionSignature(method.MethodInfo, luaMethodDetails, luaApiDetails);
223 |
224 | documentation.WriteLine("{0}", functionSig);
225 |
226 | bool hasParams = method.MethodInfo.GetParameters().Length != 0;
227 |
228 | if (hasParams)
229 | {
230 | documentation.WriteLine("'''Expected parameter types'''");
231 | documentation.WriteLine("{| class=\"wikitable\"");
232 | }
233 |
234 | // Expected param types
235 | foreach (ParameterInfo param in method.MethodInfo.GetParameters())
236 | {
237 | documentation.WriteLine("|-");
238 | documentation.WriteLine("| {0} || {1}", param.Name, GetCleanTypeName(param.ParameterType));
239 | }
240 |
241 | if (hasParams)
242 | {
243 | documentation.WriteLine("|}");
244 | }
245 |
246 | documentation.WriteLine("'''Description''': {0}\n", luaMethodDetails.description);
247 |
248 | // Use custom return description if exists, else generate one
249 | documentation.WriteLine("'''Returns''': {0}\n",
250 | !string.IsNullOrEmpty(luaMethodDetails.returns)
251 | ? luaMethodDetails.returns
252 | : GetCleanTypeName(method.MethodInfo.ReturnType));
253 |
254 | if (!string.IsNullOrEmpty(luaMethodDetails.notes))
255 | {
256 | documentation.WriteLine("'''Notes''': {0}",
257 | luaMethodDetails.notes);
258 | }
259 |
260 | if (!string.IsNullOrEmpty(luaMethodDetails.warning))
261 | {
262 | documentation.WriteLine("'''Warning''': {0}",
263 | luaMethodDetails.warning);
264 | }
265 |
266 | if (!string.IsNullOrEmpty(luaMethodDetails.success))
267 | {
268 | documentation.WriteLine("'''Tip''': {0}",
269 | luaMethodDetails.success);
270 | }
271 | }
272 |
273 | bool wroteTitle = false;
274 | foreach (LuaVariableInfo fieldInfo in api.variables)
275 | {
276 | if (!wroteTitle)
277 | {
278 | documentation.WriteLine("== Variables ==");
279 | wroteTitle = true;
280 | }
281 |
282 | LuaApiVariable luaVariableDetails = fieldInfo.Attribute;
283 |
284 | documentation.WriteLine("=== {0} ===", luaVariableDetails.name);
285 |
286 | string varibleSig = string.Format("{0}.{1}", luaApiDetails.luaName, luaVariableDetails.name);
287 | documentation.WriteLine("{0}", varibleSig);
288 |
289 | documentation.WriteLine("'''Description''': {0}\n", luaVariableDetails.description);
290 | }
291 |
292 | // Add time stamp
293 | documentation.WriteLine("\n\n'''Docs last hacked together on''': {0:dd/MM/yyyy H:mm}", DateTime.Now);
294 |
295 | documentation.Close();
296 | }
297 |
298 | // TODO Enum docs
299 |
300 | string enumDocPath = string.Format("{0}/Constants.mediawiki", documentationPath);
301 |
302 | StreamWriter enumDocumentation = File.CreateText(enumDocPath);
303 |
304 | enumDocumentation.WriteLine("= Constants =");
305 |
306 | enumDocumentation.WriteLine("== Enums ==");
307 | foreach (LuaEnumInfo enumInfo in GetAllEnums())
308 | {
309 | enumDocumentation.WriteLine("=== {0} ===", enumInfo.Attribute.name);
310 | if (!string.IsNullOrEmpty(enumInfo.Attribute.description))
311 | {
312 | enumDocumentation.WriteLine(enumInfo.Attribute.description);
313 | }
314 |
315 | enumDocumentation.WriteLine("{| class=\"wikitable\"");
316 | enumDocumentation.WriteLine("|-");
317 | enumDocumentation.WriteLine("! Usage !! Description");
318 |
319 | foreach (LuaEnumValueInfo value in enumInfo.values)
320 | {
321 | if (value.Attribute != null && value.Attribute.hidden)
322 | {
323 | continue;
324 | }
325 | enumDocumentation.WriteLine("|-");
326 | enumDocumentation.WriteLine("| {0}.{1} || {2}",
327 | enumInfo.Attribute.name,
328 | value.StringValue,
329 | value.Attribute != null ? value.Attribute.description : string.Empty);
330 | }
331 |
332 | enumDocumentation.WriteLine("|}");
333 | }
334 |
335 | enumDocumentation.Close();
336 | }
337 |
338 | Logger.Log (Channel.Lua, "Completed Lua doc generation");
339 | }
340 |
341 | private class VSCodeSnippet
342 | {
343 | public string prefix;
344 | public string[] body;
345 | public string description;
346 | }
347 |
348 | [MenuItem("Lua/Docs/Generate VSCode Snippets")]
349 | public static void GenerateVSCodeSnippets()
350 | {
351 | StringBuilder snippets = new StringBuilder();
352 | VSCodeSnippet snippet = new VSCodeSnippet {body = new string[1]};
353 |
354 | foreach (LuaApiInfo api in GetAllApiInfo())
355 | {
356 | // Write out the header for the api type
357 | LuaApi luaApiDetails = api.Attribute;
358 |
359 | // Iterate all methods
360 | foreach (LuaFunction_Info method in api.functions)
361 | {
362 | // Grab the LuaApiFunction if it exists
363 | LuaApiFunction luaMethodDetails = method.Attribute;
364 |
365 | snippet.prefix = string.Format("{0}.{1}", luaApiDetails.luaName, luaMethodDetails.name);
366 |
367 | string paramString = string.Empty;
368 |
369 | var paramArray = method.MethodInfo.GetParameters();
370 | for (int i = 0; i < paramArray.Length; i++)
371 | {
372 | var param = paramArray[i];
373 |
374 | paramString += string.Format("${{{0}:{1}}}, ", i, param.Name);
375 | }
376 |
377 | if (!string.IsNullOrEmpty(paramString))
378 | {
379 | paramString = paramString.Remove(paramString.Length - 2, 2);
380 | }
381 |
382 | snippet.body[0] = string.Format("{0}({1})", snippet.prefix, paramString);
383 |
384 | snippet.description = luaMethodDetails.description;
385 |
386 | string finalBlock = string.Format("\"{0}\": {1},", snippet.prefix, JsonUtility.ToJson(snippet, true));
387 |
388 | snippets.AppendLine(finalBlock);
389 | }
390 |
391 | foreach (LuaVariableInfo fieldInfo in api.variables)
392 | {
393 | LuaApiVariable luaVariableDetails = fieldInfo.Attribute;
394 |
395 | snippet.prefix = string.Format("{0}.{1}", luaApiDetails.luaName, luaVariableDetails.name);
396 | snippet.body[0] = snippet.prefix;
397 | snippet.description = luaVariableDetails.description;
398 |
399 | string finalBlock = string.Format("\"{0}\" : {1},", snippet.prefix, JsonUtility.ToJson(snippet, true));
400 |
401 | snippets.AppendLine(finalBlock);
402 | }
403 | }
404 |
405 | EditorGUIUtility.systemCopyBuffer = snippets.ToString();
406 | }
407 |
408 | private class AtomSnippet
409 | {
410 | public string Prefix;
411 | public string Body;
412 | public string Description;
413 | public string DescriptionMoreUrl;
414 |
415 | private static string ToLiteral(string input)
416 | {
417 | input = input.Replace("'", @"\'");
418 | input = input.Replace("\"", @"""");
419 | return input;
420 | }
421 |
422 | public string ConstructCSONString()
423 | {
424 | return string.Format(@"
425 | '{0}':
426 | 'prefix': '{0}'
427 | 'body': '{1}'
428 | 'description' : '{2}'
429 | 'rightLabelHTML': 'Lua'
430 | 'descriptionMoreURL' : '{3}'",
431 | Prefix,
432 | ToLiteral(Body),
433 | ToLiteral(Description),
434 | DescriptionMoreUrl);
435 |
436 | }
437 | }
438 |
439 | [MenuItem("Lua/Docs/Generate Atom Snippets")]
440 | public static void GenerateAtomSnippets()
441 | {
442 | StringBuilder snippets = new StringBuilder();
443 | AtomSnippet snippet = new AtomSnippet();
444 |
445 | snippets.AppendLine("'.source.lua':");
446 |
447 | // Iterate all types deriving from LuaApiBase
448 | foreach (LuaApiInfo api in GetAllApiInfo())
449 | {
450 | // Write out the header for the api type
451 | LuaApi luaApiDetails = api.Attribute;
452 |
453 | // Iterate all methods
454 | foreach (LuaFunction_Info method in api.functions)
455 | {
456 | // Grab the LuaApiFunction if it exists
457 | LuaApiFunction luaMethodDetails = method.Attribute;
458 |
459 | snippet.Prefix = string.Format("{0}.{1}", luaApiDetails.luaName, luaMethodDetails.name);
460 |
461 | string paramString = string.Empty;
462 |
463 | var paramArray = method.MethodInfo.GetParameters();
464 | for (int i = 0; i < paramArray.Length; i++)
465 | {
466 | var param = paramArray[i];
467 |
468 | paramString += string.Format("${{{0}:{1}}}, ", i, param.Name);
469 | }
470 |
471 | if (!string.IsNullOrEmpty(paramString))
472 | {
473 | paramString = paramString.Remove(paramString.Length - 2, 2);
474 | }
475 |
476 | snippet.Body = string.Format("{0}({1})", snippet.Prefix, paramString);
477 |
478 | snippet.Description = luaMethodDetails.description;
479 |
480 | snippets.AppendLine(snippet.ConstructCSONString());
481 | }
482 |
483 | foreach (LuaVariableInfo fieldInfo in api.variables)
484 | {
485 | LuaApiVariable luaVariableDetails = fieldInfo.Attribute;
486 |
487 | snippet.Prefix = string.Format("{0}.{1}", luaApiDetails.luaName, luaVariableDetails.name);
488 | snippet.Body = snippet.Prefix;
489 | snippet.Description = luaVariableDetails.description;
490 |
491 | snippets.AppendLine(snippet.ConstructCSONString());
492 | }
493 | }
494 |
495 | foreach (LuaEnumInfo enumInfo in GetAllEnums())
496 | {
497 | foreach (LuaEnumValueInfo enumValueInfo in enumInfo.values)
498 | {
499 | if (enumValueInfo.Attribute != null && enumValueInfo.Attribute.hidden)
500 | {
501 | continue;
502 | }
503 |
504 | snippet.Prefix = string.Format("{0}.{1}", enumInfo.Attribute.name, enumValueInfo.StringValue);
505 | snippet.Body = snippet.Prefix;
506 | snippet.Description = enumValueInfo.Attribute != null ? enumValueInfo.Attribute.description : string.Empty;
507 |
508 | snippet.DescriptionMoreUrl = string.Empty;
509 |
510 | snippets.AppendLine(snippet.ConstructCSONString());
511 | }
512 | }
513 |
514 | EditorGUIUtility.systemCopyBuffer = snippets.ToString();
515 | }
516 |
517 | ///
518 | /// Gets a clean function signature
519 | ///
520 | /// The clean function signature.
521 | /// Method.
522 | /// Function Attribute.
523 | /// API Attribute.
524 | private static string GetCleanFunctionSignature(MethodInfo method, LuaApiFunction functionAttrib, LuaApi apiAttrib)
525 | {
526 | string apiName = apiAttrib.luaName;
527 | string functionName = functionAttrib.name;
528 | string paramsString = string.Empty;
529 |
530 | foreach (ParameterInfo param in method.GetParameters())
531 | {
532 | paramsString += string.Format ("{0}, ", param.Name);
533 | }
534 |
535 | // Clean up last comma and space
536 | if (!string.IsNullOrEmpty (paramsString))
537 | {
538 | paramsString = paramsString.Remove (paramsString.Length - 2, 2);
539 | }
540 |
541 | return string.Format ("{0}.{1}({2})", apiName, functionName, paramsString);
542 | }
543 |
544 | ///
545 | /// Gets a documentation friendly name for a type.
546 | ///
547 | /// The clean type name.
548 | /// Type.
549 | private static string GetCleanTypeName(Type type)
550 | {
551 | string result;
552 |
553 | // Ideally this would be a switch, but c# won't allow system.type in a switch
554 | if (type == typeof(void))
555 | {
556 | result = "Nothing";
557 | }
558 | else if (type == typeof(float?))
559 | {
560 | result = "number (optional)";
561 | }
562 | else if (type == typeof(float[]))
563 | {
564 | result = "Table of numbers";
565 | }
566 | else if (type == typeof(int) ||
567 | type == typeof(uint) ||
568 | type == typeof(float))
569 | {
570 | result = "number";
571 | }
572 | else if (type == typeof(string))
573 | {
574 | result = "string";
575 | }
576 | else if (type == typeof(bool))
577 | {
578 | result = "bool";
579 | }
580 | else if (type == typeof(MoonSharp.Interpreter.DynValue))
581 | {
582 | result = "Lua Type";
583 | }
584 | else if (type == typeof(MoonSharp.Interpreter.Table))
585 | {
586 | result = "Lua Table";
587 | }
588 | else if (type == typeof(bool?))
589 | {
590 | result = "bool (optional)";
591 | }
592 | else
593 | {
594 | Logger.Log (Channel.Lua, Priority.Error, "Failed to convert type {0} to cleaner name", type.ToString ());
595 | result = type.ToString ();
596 | }
597 |
598 | return result;
599 | }
600 |
601 | }
602 | #endif // UNITY_EDITOR
--------------------------------------------------------------------------------