/
lua.dm
152 lines (132 loc) · 4.73 KB
/
lua.dm
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
SUBSYSTEM_DEF(lua)
name = "Lua Scripting"
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
wait = 0.1 SECONDS
flags = SS_OK_TO_FAIL_INIT
/// A list of all lua states
var/list/datum/lua_state/states = list()
/// A list of open editors, with each key in the list associated with a list of editors.
/// Tracks which UIs are open for each state so that they can be updated whenever
/// code is run in the state.
var/list/editors
var/list/sleeps = list()
var/list/resumes = list()
var/list/current_run = list()
var/list/current_states_run = list()
/// Protects return values from getting GCed before getting converted to lua values
/// Gets cleared every tick.
var/list/gc_guard = list()
/datum/controller/subsystem/lua/Initialize()
if(!CONFIG_GET(flag/auxtools_enabled))
warning("SSlua requires auxtools to be enabled to run.")
return SS_INIT_NO_NEED
try
// Initialize the auxtools library
AUXTOOLS_CHECK(AUXLUA)
// Set the wrappers for setting vars and calling procs
__lua_set_set_var_wrapper("/proc/wrap_lua_set_var")
__lua_set_datum_proc_call_wrapper("/proc/wrap_lua_datum_proc_call")
__lua_set_global_proc_call_wrapper("/proc/wrap_lua_global_proc_call")
__lua_set_print_wrapper("/proc/wrap_lua_print")
return SS_INIT_SUCCESS
catch(var/exception/e)
// Something went wrong, best not allow the subsystem to run
var/crash_message = "Error initializing SSlua: [e.name]"
initialization_failure_message = crash_message
warning(crash_message)
return SS_INIT_FAILURE
/datum/controller/subsystem/lua/OnConfigLoad()
// Read the paths from the config file
var/list/lua_path = list()
var/list/config_paths = CONFIG_GET(str_list/lua_path)
for(var/path in config_paths)
lua_path += path
world.SetConfig("env", "LUAU_PATH", jointext(lua_path, ";"))
/datum/controller/subsystem/lua/Shutdown()
AUXTOOLS_SHUTDOWN(AUXLUA)
/datum/controller/subsystem/lua/proc/queue_resume(datum/lua_state/state, index, arguments)
if(!initialized)
return
if(!istype(state))
return
if(!arguments)
arguments = list()
else if(!islist(arguments))
arguments = list(arguments)
resumes += list(list("state" = state, "index" = index, "arguments" = arguments))
/datum/controller/subsystem/lua/proc/kill_task(datum/lua_state/state, list/task_info)
if(!istype(state))
return
if(!islist(task_info))
return
if(!(istext(task_info["name"]) && istext(task_info["status"]) && isnum(task_info["index"])))
return
switch(task_info["status"])
if("sleep")
var/task_index = task_info["index"]
var/state_index = 1
// Get the nth sleep in the sleep list corresponding to the target state
for(var/i in 1 to length(sleeps))
var/datum/lua_state/sleeping_state = sleeps[i]
if(sleeping_state == state)
if(state_index == task_index)
sleeps.Cut(i, i+1)
break
state_index++
if("yield")
// Remove the resumt from the resumt list
for(var/i in 1 to length(resumes))
var/resume = resumes[i]
if(resume["state"] == state && resume["index"] == task_info["index"])
resumes.Cut(i, i+1)
break
state.kill_task(task_info)
/datum/controller/subsystem/lua/fire(resumed)
// Each fire of SSlua awakens every sleeping task in the order they slept,
// then resumes every yielded task in the order their resumes were queued
if(!resumed)
current_run = list("sleeps" = sleeps.Copy(), "resumes" = resumes.Copy())
current_states_run = states.Copy()
sleeps.Cut()
resumes.Cut()
gc_guard.Cut()
var/list/current_sleeps = current_run["sleeps"]
var/list/affected_states = list()
while(length(current_sleeps))
var/datum/lua_state/state = current_sleeps[1]
current_sleeps.Cut(1,2)
if(!istype(state))
continue
affected_states |= state
var/result = state.awaken()
state.log_result(result, verbose = FALSE)
if(MC_TICK_CHECK)
break
if(!length(current_sleeps))
var/list/current_resumes = current_run["resumes"]
while(length(current_resumes))
var/list/resume_params = current_resumes[1]
current_resumes.Cut(1,2)
var/datum/lua_state/state = resume_params["state"]
if(!istype(state))
continue
var/index = resume_params["index"]
if(isnull(index) || !isnum(index))
continue
var/arguments = resume_params["arguments"]
if(!islist(arguments))
continue
affected_states |= state
var/result = state.resume(arglist(list(index) + arguments))
state.log_result(result, verbose = FALSE)
if(MC_TICK_CHECK)
break
while(length(current_states_run))
var/datum/lua_state/state = current_states_run[current_states_run.len]
current_states_run.len--
state.process(wait)
if(MC_TICK_CHECK)
break
// Update every lua editor TGUI open for each state that had a task awakened or resumed
for(var/datum/lua_state/state in affected_states)
INVOKE_ASYNC(state, TYPE_PROC_REF(/datum/lua_state, update_editors))