-
Notifications
You must be signed in to change notification settings - Fork 1
/
ScriptEngine.cs
445 lines (392 loc) · 12.4 KB
/
ScriptEngine.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*
* Source code from: http://www.tuxenconsulting.dk/lua.zip
*/
using System;
using System.Collections.Generic;
namespace MAIN
{
public class ScriptEngine
{
public IntPtr L = IntPtr.Zero;
private List<Delegate> m_refs = new List<Delegate>();
/// <summary>
/// Returns the lua state, read-only
/// </summary>
/// <summary>
/// Is the top of the lua stack nil ?
/// </summary>
private bool IsTopNil
{
get { return Lua.lua_type(L, -1) == Lua.LUA_TNIL; }
}
/// <summary>
/// Default constructor, initializes a new Lua state by calling ResetLua() method
/// </summary>
public ScriptEngine()
{
//initialize lua
ResetLua();
}
/// <summary>
/// Constructor, initializes a new Lua state by calling ResetLua() method
/// if lua is already open, then close current lua state, and assign the passed in state as the curent state
/// </summary>
public ScriptEngine(IntPtr lua_State)
{
//if lua is already open, then close current lua state
CloseLua();
//assign injected lua state
L = lua_State;
}
/// <summary>
/// Closes lua and clear errorlog and any references to functions that was registered with lua
/// </summary>
public void CloseLua()
{
if (L != IntPtr.Zero) {
Lua.lua_settop(L, 0);
Lua.lua_close(L);
L = IntPtr.Zero;
}
//clear any references to callbacks to allow the garbage collector collect them
m_refs.Clear();
}
/// <summary>
/// Closes lua by calling CloseLua() method, then opens a new lua state with all libs open
/// </summary>
public void ResetLua()
{
//if lua is already open, then close current lua state, and open a new one
CloseLua();
//open lua
L = Lua.lua_open();
//open libraries
Lua.luaL_openlibs(L);
}
/// <summary>
/// Does the same as the staic method CloseLua(), but this method is a wrapper as an instance method
/// </summary>
public void StopEngineAndCleanup()
{
CloseLua();
}
/// <summary>
/// Registers a .net method denoted by the delegate Lua.LuaFunction as a lua function
/// the function will take on the name luaFuncName, in lua
/// </summary>
/// <param name="func">.net function to register with lua</param>
/// <param name="luaFuncName">name of .net function in lua</param>
public void RegisterLuaFunction(Lua.LuaFunction func, string luaFuncName)
{
Lua.lua_pushcfunction(L, func);
Lua.lua_setglobal(L, luaFuncName);
//Lua.lua_register(m_lua_State, luaFuncName, func);
//make sure the delegate callback is not collected by the garbage collector before
//unmanaged code has called back
m_refs.Add(func);
}
/// <summary>
/// Calls the Lua function 'luaFuncName' with the given parameters
/// </summary>
/// <param name="returnType">Error code</param>
public int CallLuaFunction(string luaFuncName, object[] args)
{
Lua.lua_getglobal(L, luaFuncName);
if (!Lua.lua_isfunction(L, -1)) {
Lua.lua_pop(L, 1);
return 1;
}
int argc = (args != null) ? args.Length : 0;
for (int i = 0; i < argc; i++)
PushBasicValue(args[i]);
return Lua.lua_pcall(L, argc, 0, 0);
}
/// <summary>
/// Calls the Lua function 'luaFuncName' with the given parameters
/// </summary>
/// <param name="returnType">Lua type of the return value</param>
public object CallLuaFunction(string luaFuncName, object[] args, out int luaType)
{
luaType = 0;
Lua.lua_getglobal(L, luaFuncName);
if (!Lua.lua_isfunction(L, -1)) {
Lua.lua_pop(L, 1);
return null;
}
int argc = (args != null) ? args.Length : 0;
for (int i = 0; i < argc; i++)
PushBasicValue(args[i]);
if (Lua.lua_pcall(L, argc, 1, 0) != 0)
return null;
return GetValueOfStack(out luaType);
}
/// <summary>
/// Creates a table with tableName if it does not already exist.
/// After creation or if the table already exists, stack will be empty and balanced.
/// </summary>
/// <param name="tableName"></param>
public void CreateLuaTable(string tableName)
{
Lua.lua_getglobal(L, tableName);
bool is_nil = IsTopNil;
// Back to root
Lua.lua_pop(L, 1);
if (is_nil) {
Lua.lua_newtable(L);
Lua.lua_setglobal(L, tableName);
//stack is empty
}
}
/// <summary>
/// Gets the value of a field of a table and returns it as type object i.e. return luaTable.someField
/// After the call the lua stack is balanced
/// </summary>
/// <param name="tableName">name of lua table</param>
/// <param name="fieldName">name of table field</param>
/// <returns>returns luaTable.someField</returns>
public object GetTableField(string tableName, string fieldName)
{
//push table on the stack
Lua.lua_getglobal(L, tableName);
if (IsTopNil)
throw new Exception("GetTableField, the table does not exist: " + tableName);
//push table field name on the stack
Lua.lua_pushstring(L, fieldName);
//get value of field of table: get tableName[fieldName]
Lua.lua_gettable(L, -2);
//get the result of the stack
int luaType = 0;
object value = GetValueOfStack(out luaType);
//pop table of the stack
Lua.lua_pop(L, 1);
//stack is balanced
return value;
}
/// <summary>
/// Gets the value of a field of a lua table and returns it as type object i.e. return luaTable.someField
/// The table to operate on must currently be on top of the lua stack.
/// After the call the lua table is still on top of the stack
/// </summary>
/// <param name="fieldName">
/// the name of the field, belonging to a lua table currently sitting on
/// top of the lua stack
/// </param>
/// <returns>returns luaTable.someField</returns>
public object GetTableField(string fieldName)
{
if (IsTopNil)
throw new Exception("GetTableField, no table on top of stack");
//push table field name on the stack
Lua.lua_pushstring(L, fieldName);
//get value of field of table: get tableName[fieldName]
Lua.lua_gettable(L, -2);
//get the result of the stack
int luaType = 0;
object value = GetValueOfStack(out luaType);
//table is still on top of the stack
return value;
}
/// <summary>
/// Sets a field of a lua table to a lua variable identified by luaIdentifier i.e. luaTable.someField = luaVariable
/// After the call the lua stack is balanced.
/// </summary>
/// <param name="tableName">name of the lua table</param>
/// <param name="fieldName">name of the field</param>
/// <param name="luaIdentifier">the identifier identifying the lua variable</param>
public void SetTableFieldToLuaIdentifier(string tableName, string fieldName, string luaIdentifier)
{
Lua.lua_getglobal(L, tableName);
Lua.lua_pushstring(L, fieldName);
Lua.lua_getglobal(L, luaIdentifier);
Lua.lua_settable(L, -3);
Lua.lua_pop(L, 1);
//stack is balanced
}
/// <summary>
/// Sets a field of a lua table to a lua variable identified by luaIdentifier i.e. luaTable.someField = luaVariable
/// The table to operate on must currently be on top of the lua stack.
/// After the call the lua table is still on top of the stack.
/// </summary>
/// <param name="fieldName">name of the field</param>
/// <param name="luaIdentifier">the identifier identifying the lua variable</param>
public void SetTableFieldToLuaIdentifier(string fieldName, string luaIdentifier)
{
Lua.lua_pushstring(L, fieldName);
Lua.lua_getglobal(L, luaIdentifier);
Lua.lua_settable(L, -3);
//table is still on top of the stack
}
/// <summary>
/// Sets a field of a lua table to a .net variable value i.e. luaTable.someField = value of .net variable.
/// After the call the lua table is still on top of the stack.
/// </summary>
/// <param name="tableName">name of the lua table</param>
/// <param name="fieldName">name of the field</param>
/// <param name="value">.net variable</param>
public void SetTableField(string tableName, string fieldName, object value)
{
//push table on the stack
Lua.lua_getglobal(L, tableName);
if (IsTopNil)
throw new Exception("SetTableField, the table does not exist: " + tableName);
//push table field name on the stack
Lua.lua_pushstring(L, fieldName);
//push value on the stack
PushBasicValue(value);
//set field of table to value: tableName[fieldName]=value
Lua.lua_settable(L, -3);
//pop table of the stack
}
/// <summary>
/// Sets a field of a lua table to a .net variable value i.e. luaTable.someField = value of .net variable.
/// After the call the lua table is still on top of the stack.
/// </summary>
/// <param name="fieldName">name of the field</param>
/// <param name="value">.net variable</param>
public void SetTableField(string fieldName, object value)
{
if (IsTopNil)
throw new Exception("SetTableField, no table on top of the stack");
//push table field name on the stack
Lua.lua_pushstring(L, fieldName);
//push value on the stack
PushBasicValue(value);
//set field of table to value: tableName[fieldName]=value
Lua.lua_settable(L, -3);
//table is still on top of the stack
}
/// <summary>
/// Pops the value sitting on top of the lua stack
/// </summary>
/// <param name="luaType">the lua type of the value that was retrieved from the stack, is stored in this out parameter</param>
/// <returns>the value popped off the stack</returns>
public object GetValueOfStack(out int luaType)
{
luaType = 0;
object value = null;
switch (Lua.lua_type(L, -1)) {
case Lua.LUA_TNONE: {
value = null;
luaType = Lua.LUA_TNONE;
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
case Lua.LUA_TNIL: {
value = null;
luaType = Lua.LUA_TNIL;
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
case Lua.LUA_TSTRING: {
luaType = Lua.LUA_TSTRING;
value = Lua.lua_tostring(L, -1);
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
case Lua.LUA_TNUMBER: {
luaType = Lua.LUA_TNUMBER;
value = Lua.lua_tonumber(L, -1);
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
case Lua.LUA_TBOOLEAN: {
luaType = Lua.LUA_TBOOLEAN;
int intToBool = Lua.lua_toboolean(L, -1);
value = intToBool > 0 ? true : false;
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
case Lua.LUA_TTABLE: {
luaType = Lua.LUA_TTABLE;
value = "table";
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
default: {
value = null;
//pop value from stack
Lua.lua_pop(L, 1);
break;
}
//case Lua.LUA_TINTEGER:
//{
// value = Lua.lua_tointeger(m_lua_State, -1);
// //pop value from stack
// Lua.lua_pop(m_lua_State, 1);
// break;
//}
}
return value;
}
/// <summary>
/// Pushes the value on to the lua stack based on value.GetType(), and how that type maps to a lua type.
/// Pushes nil if object is null or typeof object is not a basic type
/// </summary>
/// <param name="value">the value to push on to the stack</param>
public void PushBasicValue(object value)
{
//push value on the stack
if (value == null) {
Lua.lua_pushnil(L);
return;
}
if (value.GetType() == typeof(string)) {
Lua.lua_pushstring(L, value.ToString());
return;
}
if (value.GetType() == typeof(bool)) {
Lua.lua_pushboolean(L, (bool)value);
return;
}
if (value.GetType() == typeof(decimal)) {
Lua.lua_pushnumber(L, Convert.ToDouble(((decimal)value)));
return;
}
if (value.GetType() == typeof(float)) {
Lua.lua_pushnumber(L, Convert.ToDouble(((float)value)));
return;
}
if (value.GetType() == typeof(double)) {
Lua.lua_pushnumber(L, Convert.ToDouble(((double)value)));
return;
}
if (value.GetType() == typeof(int)) {
Lua.lua_pushinteger(L, (int)value);
return;
}
if (value.GetType() == typeof(DateTime)) {
Lua.lua_pushstring(L, ((DateTime)value).ToString());
return;
}
if (value.GetType() == typeof(Guid)) {
Lua.lua_pushstring(L, ((Guid)value).ToString());
return;
}
if (value.GetType().IsEnum) {
Lua.lua_pushstring(L, value.ToString());
return;
}
Lua.lua_pushnil(L);
}
public bool CheckString(IntPtr Q, string prefix, int idx)
{
if (Lua.lua_type(Q, idx) == Lua.LUA_TSTRING)
return true;
Lua.luaL_error(Q, prefix + ": Invalid argument #" + idx + ". Type 'string' expected");
return false;
}
public bool CheckNumber(IntPtr Q, string prefix, int idx)
{
if (Lua.lua_type(Q, idx) == Lua.LUA_TNUMBER)
return true;
Lua.luaL_error(Q, prefix + ": Invalid argument #" + idx + ". Type 'number' expected");
return false;
}
}
}