Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIRROR] Moves lua off of the timer subsystem and onto its own internal scheduler. #2530

Merged
merged 1 commit into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions code/controllers/subsystem/lua.dm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ SUBSYSTEM_DEF(lua)
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.
Expand Down Expand Up @@ -97,6 +98,7 @@ SUBSYSTEM_DEF(lua)
// 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()

Expand Down Expand Up @@ -136,6 +138,13 @@ SUBSYSTEM_DEF(lua)
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))
13 changes: 13 additions & 0 deletions code/modules/admin/verbs/lua/lua_state.dm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ GLOBAL_PROTECT(lua_usr)
/// Ckey of the last user who ran a script on this lua state.
var/ckey_last_runner = ""

/// Whether the timer.lua script has been included into this lua context state.
var/timer_enabled = FALSE

/// Callbacks that need to be ran on next tick
var/list/functions_to_execute = list()

/datum/lua_state/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == NAMEOF(src, internal_id))
Expand Down Expand Up @@ -89,6 +95,13 @@ GLOBAL_PROTECT(lua_usr)

return result

/datum/lua_state/process(seconds_per_tick)
if(timer_enabled)
call_function("__Timer_timer_process", seconds_per_tick)
for(var/function as anything in functions_to_execute)
call_function(list("__Timer_callbacks", function))
functions_to_execute.Cut()

/datum/lua_state/proc/call_function(function, ...)
var/call_args = length(args) > 1 ? args.Copy(2) : list()
if(islist(function))
Expand Down
255 changes: 8 additions & 247 deletions lua/SS13.lua
Original file line number Diff line number Diff line change
@@ -1,249 +1,10 @@
local SS13 = {}

__SS13_signal_handlers = __SS13_signal_handlers or {}
__SS13_timeouts = __SS13_timeouts or {}
__SS13_timeouts_id_mapping = __SS13_timeouts_id_mapping or {}

SS13.SSlua = dm.global_vars.vars.SSlua

SS13.global_proc = "some_magic_bullshit"

for _, state in SS13.SSlua.vars.states do
if state.vars.internal_id == dm.state_id then
SS13.state = state
break
end
end

function SS13.get_runner_ckey()
return SS13.state:get_var("ckey_last_runner")
end

function SS13.get_runner_client()
return dm.global_vars:get_var("GLOB"):get_var("directory"):get(SS13.get_runner_ckey())
end

function SS13.istype(thing, type)
return dm.global_proc("_istype", thing, dm.global_proc("_text2path", type)) == 1
end

function SS13.new(type, ...)
local datum = SS13.new_untracked(type, table.unpack({...}))
if datum then
local references = SS13.state.vars.references
references:add(datum)
SS13.state:call_proc("clear_on_delete", datum)
return datum
end
end

function SS13.type(string_type)
return dm.global_proc("_text2path", string_type)
end

function SS13.qdel(datum)
if SS13.is_valid(datum) then
dm.global_proc("qdel", datum)
return true
end
return false
end

function SS13.new_untracked(type, ...)
return dm.global_proc("_new", type, { ... })
end

function SS13.is_valid(datum)
if datum and not datum:is_null() and not datum:get_var("gc_destroyed") then
return true
end
return false
end

function SS13.await(thing_to_call, proc_to_call, ...)
if not SS13.istype(thing_to_call, "/datum") then
thing_to_call = SS13.global_proc
end
if thing_to_call == SS13.global_proc then
proc_to_call = "/proc/" .. proc_to_call
end
local promise = SS13.new("/datum/auxtools_promise", thing_to_call, proc_to_call, ...)
local promise_vars = promise.vars
while promise_vars.status == 0 do
sleep()
end
local return_value, runtime_message = promise_vars.return_value, promise_vars.runtime_message
SS13.stop_tracking(promise)
return return_value, runtime_message
end

function SS13.wait(time)
local callback = SS13.new("/datum/callback", SS13.SSlua, "queue_resume", SS13.state, __next_yield_index)
local timedevent = dm.global_proc("_addtimer", callback, time * 10, 8, nil, debug.info(1, "sl"))
coroutine.yield()
dm.global_proc("deltimer", timedevent)
SS13.stop_tracking(callback)
end

function SS13.register_signal(datum, signal, func, make_easy_clear_function)
if not SS13.istype(datum, "/datum") then
return
end
if not __SS13_signal_handlers[datum] then
__SS13_signal_handlers[datum] = {}
end
if signal == "_cleanup" then
return
end
if not __SS13_signal_handlers[datum][signal] then
__SS13_signal_handlers[datum][signal] = {}
end
local callback = SS13.new("/datum/callback", SS13.state, "call_function_return_first")
callback:call_proc("RegisterSignal", datum, signal, "Invoke")
local path = { "__SS13_signal_handlers", dm.global_proc("WEAKREF", datum), signal, dm.global_proc("WEAKREF", callback), "func" }
callback.vars.arguments = { path }
if not __SS13_signal_handlers[datum]["_cleanup"] then
local cleanup_path = { "__SS13_signal_handlers", dm.global_proc("WEAKREF", datum), "_cleanup", "func" }
local cleanup_callback = SS13.new("/datum/callback", SS13.state, "call_function_return_first", cleanup_path)
cleanup_callback:call_proc("RegisterSignal", datum, "parent_qdeleting", "Invoke")
__SS13_signal_handlers[datum]["_cleanup"] = {
func = function(datum)
SS13.signal_handler_cleanup(datum)
SS13.stop_tracking(cleanup_callback)
end,
callback = cleanup_callback,
}
end
if signal == "parent_qdeleting" then --We want to make sure that the cleanup function is the very last signal handler called.
local comp_lookup = datum.vars._listen_lookup
if comp_lookup then
local lookup_for_signal = comp_lookup.entries.parent_qdeleting
if lookup_for_signal and not SS13.istype(lookup_for_signal, "/datum") then
local cleanup_callback_index =
dm.global_proc("_list_find", lookup_for_signal, __SS13_signal_handlers[datum]["_cleanup"].callback)
if cleanup_callback_index ~= 0 and cleanup_callback_index ~= #comp_lookup then
dm.global_proc("_list_swap", lookup_for_signal, cleanup_callback_index, #lookup_for_signal)
end
end
end
end
__SS13_signal_handlers[datum][signal][callback] = { func = func, callback = callback }
if make_easy_clear_function then
local clear_function_name = "clear_signal_" .. tostring(datum) .. "_" .. signal .. "_" .. tostring(callback)
SS13[clear_function_name] = function()
if callback then
SS13.unregister_signal(datum, signal, callback)
end
SS13[clear_function_name] = nil
end
end
return callback
end

function SS13.stop_tracking(datum)
SS13.state:call_proc("let_soft_delete", datum)
end

function SS13.unregister_signal(datum, signal, callback)
local function clear_handler(handler_info)
if not handler_info then
return
end
if not handler_info.callback then
return
end
local handler_callback = handler_info.callback
handler_callback:call_proc("UnregisterSignal", datum, signal)
SS13.stop_tracking(handler_callback)
end

local function clear_easy_clear_function(callback_to_clear)
local clear_function_name = "clear_signal_" .. tostring(datum) .. "_" .. signal .. "_" .. tostring(callback_to_clear)
SS13[clear_function_name] = nil
end

if not __SS13_signal_handlers[datum] then
return
end
if signal == "_cleanup" then
return
end
if not __SS13_signal_handlers[datum][signal] then
return
end

if not callback then
for handler_key, handler_info in __SS13_signal_handlers[datum][signal] do
clear_easy_clear_function(handler_key)
clear_handler(handler_info)
end
__SS13_signal_handlers[datum][signal] = nil
else
if not SS13.istype(callback, "/datum/callback") then
return
end
clear_easy_clear_function(callback)
clear_handler(__SS13_signal_handlers[datum][signal][callback])
__SS13_signal_handlers[datum][signal][callback] = nil
end
end

function SS13.signal_handler_cleanup(datum)
if not __SS13_signal_handlers[datum] then
return
end

for signal, _ in __SS13_signal_handlers[datum] do
SS13.unregister_signal(datum, signal)
end

__SS13_signal_handlers[datum] = nil
end

function SS13.set_timeout(time, func)
SS13.start_loop(time, 1, func)
end

function SS13.start_loop(time, amount, func)
if not amount or amount == 0 then
return
end
local callback = SS13.new("/datum/callback", SS13.state, "call_function")
local timedevent = dm.global_proc("_addtimer", callback, time * 10, 40, nil, debug.info(1, "sl"))
local doneAmount = 0
__SS13_timeouts[callback] = function()
doneAmount += 1
if amount ~= -1 and doneAmount >= amount then
SS13.end_loop(timedevent)
end
func()
end
local loop_data = {
callback = callback,
loop_amount = amount,
}
__SS13_timeouts_id_mapping[timedevent] = loop_data
local path = { "__SS13_timeouts", dm.global_proc("WEAKREF", callback) }
callback.vars.arguments = { path }
return timedevent
end

function SS13.end_loop(id)
local data = __SS13_timeouts_id_mapping[id]
if data then
__SS13_timeouts_id_mapping[id] = nil
__SS13_timeouts[data.callback] = nil
SS13.stop_tracking(data.callback)
dm.global_proc("deltimer", id)
end
end

function SS13.stop_all_loops()
for id, data in __SS13_timeouts_id_mapping do
if data.amount ~= 1 then
SS13.end_loop(id)
end
end
end
local SS13 = require("SS13_base")
local timer = require("timer")

SS13.wait = timer.wait
SS13.set_timeout = timer.set_timeout
SS13.start_loop = timer.start_loop
SS13.end_loop = timer.end_loop
SS13.stop_all_loops = timer.stop_all_loops

return SS13
Loading
Loading