From ef5ed027a148f579d38946d6524eecb30fa58a8a Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 24 Feb 2019 21:13:08 +0000 Subject: [PATCH] File loader with redmew files --- control.lua | 12 +- modules/test.lua | 6 +- utils/data_stages.lua | 13 ++ utils/debug.lua | 164 +++++++++++++++ utils/event.lua | 457 ++++++++++++++++++++++++++++++++++++++++++ utils/event_core.lua | 132 ++++++++++++ utils/global.lua | 79 ++++++++ utils/token.lua | 55 +++++ 8 files changed, 912 insertions(+), 6 deletions(-) create mode 100644 utils/data_stages.lua create mode 100644 utils/debug.lua create mode 100644 utils/event.lua create mode 100644 utils/event_core.lua create mode 100644 utils/global.lua create mode 100644 utils/token.lua diff --git a/control.lua b/control.lua index 7a53e789..3b4fbc77 100644 --- a/control.lua +++ b/control.lua @@ -1,4 +1,4 @@ ---[[ not_luadoc=true +-- not_luadoc=true function _log(...) log(...) end -- do not remove this is used for smaller verbose lines Manager = require("FactorioSoftmodManager") Manager.setVerbose{ @@ -12,15 +12,16 @@ Manager.setVerbose{ output=Manager._verbose -- can be: can be: print || log || other function } Manager() -- can be Manager.loadModules() if called else where -]] + +--[[ +require 'utils.data_stages' local Container = require 'container' Container.handlers = { - --event - --global + Event='utils.event', + Global='utils.global', error=error, logging=function(...) log(...) end, - --debug tableToString=serpent.line } Container.loadHandlers() @@ -28,3 +29,4 @@ Container.files = { 'modules.test' } Container.loadFiles() +]] \ No newline at end of file diff --git a/modules/test.lua b/modules/test.lua index 8c15a328..29814085 100644 --- a/modules/test.lua +++ b/modules/test.lua @@ -1,3 +1,7 @@ function thisIsATestFunction(...) game.print(serpent.line({...})) -end \ No newline at end of file +end + +Event.add(defines.events.on_console_chat,function(event) + if event.player_index then game.print('Message: '..event.message) end +end) \ No newline at end of file diff --git a/utils/data_stages.lua b/utils/data_stages.lua new file mode 100644 index 00000000..406cd4df --- /dev/null +++ b/utils/data_stages.lua @@ -0,0 +1,13 @@ +-- Info on the data lifecycle and how we use it: https://github.com/Refactorio/RedMew/wiki/The-data-lifecycle +-- Non-applicable stages are commented out. +_STAGE = { + --settings = 1, + --data = 2, + --migration = 3, + control = 4, + init = 5, + load = 6, + --config_change = 7, + runtime = 8 +} +_LIFECYCLE = _STAGE.control \ No newline at end of file diff --git a/utils/debug.lua b/utils/debug.lua new file mode 100644 index 00000000..1c1a3633 --- /dev/null +++ b/utils/debug.lua @@ -0,0 +1,164 @@ +-- localised functions +local format = string.format +local match = string.match +local gsub = string.gsub +local serialize = serpent.line +local debug_getupvalue = debug.getupvalue + +-- this +local Debug = {} + +global.debug_message_count = 0 + +---@return number next index +local function increment() + local next = global.debug_message_count + 1 + global.debug_message_count = next + + return next +end + +--- Takes the table output from debug.getinfo and pretties it +local function cleanup_debug(debug_table) + local short_src = match(debug_table.source, '/[^/]*/[^/]*$') + -- require will not return a valid string so short_src may be nil here + if short_src then + short_src = gsub(short_src, '%.lua', '') + end + + return format('[function: %s file: %s line number: %s]', debug_table.name, short_src, debug_table.currentline) +end + +---Shows the given message if debug is enabled. Uses serpent to print non scalars. +-- @param message +-- @param stack_traceback levels of stack trace to give, defaults to 1 level if nil +function Debug.print(message, trace_levels) + if not _DEBUG then + return + end + + if not trace_levels then + trace_levels = 2 + else + trace_levels = trace_levels + 1 + end + + local traceback_string = '' + if type(message) ~= 'string' and type(message) ~= 'number' and type(message) ~= 'boolean' then + message = serialize(message) + end + + message = format('[%d] %s', increment(), tostring(message)) + + if trace_levels >= 2 then + for i = 2, trace_levels do + local debug_table = debug.getinfo(i) + if debug_table then + traceback_string = format('%s -> %s', traceback_string, cleanup_debug(debug_table)) + else + break + end + end + message = format('%s - Traceback%s', message, traceback_string) + end + + if _LIFECYCLE == _STAGE.runtime then + game.print(message) + end + log(message) +end + +local function get(obj, prop) + return obj[prop] +end + +local function get_lua_object_type_safe(obj) + local s, r = pcall(get, obj, 'help') + + if not s then + return + end + + return r():match('Lua%a+') +end + +--- Returns the value of the key inside the object +-- or 'InvalidLuaObject' if the LuaObject is invalid. +-- or 'InvalidLuaObjectKey' if the LuaObject does not have an entry at that key +-- @param object LuaObject or metatable +-- @param key +-- @return +function Debug.get_meta_value(object, key) + if Debug.object_type(object) == 'InvalidLuaObject' then + return 'InvalidLuaObject' + end + + local suc, value = pcall(get, object, key) + if not suc then + return 'InvalidLuaObjectKey' + end + + return value +end + +--- Returns the Lua data type or the factorio LuaObject type +-- or 'NoHelpLuaObject' if the LuaObject does not have a help function +-- or 'InvalidLuaObject' if the LuaObject is invalid. +-- @param object +-- @return string +function Debug.object_type(object) + local obj_type = type(object) + + if obj_type ~= 'table' or type(object.__self) ~= 'userdata' then + return obj_type + end + + local suc, valid = pcall(get, object, 'valid') + if not suc then + -- no 'valid' property + return get_lua_object_type_safe(object) or 'NoHelpLuaObject' + end + + if not valid then + return 'InvalidLuaObject' + else + return get_lua_object_type_safe(object) or 'NoHelpLuaObject' + end +end + +---Shows the given message if debug is on. +---@param position Position +---@param message string +function Debug.print_position(position, message) + Debug.print(format('%s %s', serialize(position), message)) +end + +---Executes the given callback if cheating is enabled. +---@param callback function +function Debug.cheat(callback) + if _CHEATS then + callback() + end +end + +--- Returns true if the function is a closure, false otherwise. +-- A closure is a function that contains 'upvalues' or in other words +-- has a reference to a local variable defined outside the function's scope. +-- @param func +-- @return boolean +function Debug.is_closure(func) + local i = 1 + while true do + local n = debug_getupvalue(func, i) + + if n == nil then + return false + elseif n ~= '_ENV' then + return true + end + + i = i + 1 + end +end + +return Debug diff --git a/utils/event.lua b/utils/event.lua new file mode 100644 index 00000000..e183bb2c --- /dev/null +++ b/utils/event.lua @@ -0,0 +1,457 @@ +--- This Module allows for registering multiple handlers to the same event, overcoming the limitation of script.register. +-- +-- ** Event.add(event_name, handler) ** +-- +-- Handlers added with Event.add must be added at the control stage or in Event.on_init or Event.on_load. +-- Remember that for each player, on_init or on_load is run, never both. So if you can't add the handler in the +-- control stage add the handler in both on_init and on_load. +-- Handlers added with Event.add cannot be removed. +-- For handlers that need to be removed or added at runtime use Event.add_removable. +-- @usage +-- local Event = require 'utils.event' +-- Event.add( +-- defines.events.on_built_entity, +-- function(event) +-- game.print(serpent.block(event)) -- prints the content of the event table to console. +-- end +-- ) +-- +-- ** Event.add_removable(event_name, token) ** +-- +-- For conditional event handlers. Event.add_removable can be safely called at runtime without desync risk. +-- Only use this if you need to add the handler at runtime or need to remove the handler, otherwise use Event.add +-- +-- Event.add_removable can be safely used at the control stage or in Event.on_init. If used in on_init you don't +-- need to also add in on_load (unlike Event.add). +-- Event.add_removable cannot be called in on_load, doing so will crash the game on loading. +-- Token is used because it's a desync risk to store closures inside the global table. +-- +-- @usage +-- local Token = require 'utils.token' +-- local Event = require 'utils.event' +-- +-- Token.register must not be called inside an event handler. +-- local handler = +-- Token.register( +-- function(event) +-- game.print(serpent.block(event)) -- prints the content of the event table to console. +-- end +-- ) +-- +-- The below code would typically be inside another event or a custom command. +-- Event.add_removable(defines.events.on_built_entity, handler) +-- +-- When you no longer need the handler. +-- Event.remove_removable(defines.events.on_built_entity, handler) +-- +-- It's not an error to register the same token multiple times to the same event, however when +-- removing only the first occurrence is removed. +-- +-- ** Event.add_removable_function(event_name, func) ** +-- +-- Only use this function if you can't use Event.add_removable. i.e you are registering the handler at the console. +-- The same restrictions that apply to Event.add_removable also apply to Event.add_removable_function. +-- func cannot be a closure in this case, as there is no safe way to store closures in the global table. +-- A closure is a function that uses a local variable not defined in the function. +-- +-- @usage +-- local Event = require 'utils.event' +-- +-- If you want to remove the handler you will need to keep a reference to it. +-- global.handler = function(event) +-- game.print(serpent.block(event)) -- prints the content of the event table to console. +-- end +-- +-- The below code would typically be used at the command console. +-- Event.add_removable_function(defines.events.on_built_entity, global.handler) +-- +-- When you no longer need the handler. +-- Event.remove_removable_function(defines.events.on_built_entity, global.handler) +-- +-- ** Other Events ** +-- +-- Use Event.on_init(handler) for script.on_init(handler) +-- Use Event.on_load(handler) for script.on_load(handler) +-- +-- Use Event.on_nth_tick(tick, handler) for script.on_nth_tick(tick, handler) +-- Favour this event over Event.add(defines.events.on_tick, handler) +-- There are also Event.add_removable_nth_tick(tick, token) and Event.add_removable_nth_tick_function(tick, func) +-- That work the same as above. +-- +-- ** Custom Scenario Events ** +-- +-- local Event = require 'utils.event' +-- +-- local event_id = script.generate_event_name() +-- +-- Event.add( +-- event_id, +-- function(event) +-- game.print(serpent.block(event)) -- prints the content of the event table to console. +-- end +-- ) +-- +-- The table contains extra information that you want to pass to the handler. +-- script.raise_event(event_id, {extra = 'data'}) + +local EventCore = require 'utils.event_core' +local Global = require 'utils.global' +local Token = require 'utils.token' +local Debug = require 'utils.debug' + +local table_remove = table.remove +local core_add = EventCore.add +local core_on_init = EventCore.on_init +local core_on_load = EventCore.on_load +local core_on_nth_tick = EventCore.on_nth_tick +local stage_load = _STAGE.load +local script_on_event = script.on_event +local script_on_nth_tick = script.on_nth_tick + +local Event = {} + +local handlers_added = false -- set to true after the removeable event handlers have been added. + +local event_handlers = EventCore.get_event_handlers() +local on_nth_tick_event_handlers = EventCore.get_on_nth_tick_event_handlers() + +local token_handlers = {} +local token_nth_tick_handlers = {} +local function_handlers = {} +local function_nth_tick_handlers = {} + +Global.register( + { + token_handlers = token_handlers, + token_nth_tick_handlers = token_nth_tick_handlers, + function_handlers = function_handlers, + function_nth_tick_handlers = function_nth_tick_handlers + }, + function(tbl) + token_handlers = tbl.token_handlers + token_nth_tick_handlers = tbl.token_nth_tick_handlers + function_handlers = tbl.function_handlers + function_nth_tick_handlers = tbl.function_nth_tick_handlers + end +) + +local function remove(tbl, handler) + if tbl == nil then + return + end + + -- the handler we are looking for is more likly to be at the back of the array. + for i = #tbl, 1, -1 do + if tbl[i] == handler then + table_remove(tbl, i) + break + end + end +end + +--- Register a handler for the event_name event. +-- This function must be called in the control stage or in Event.on_init or Event.on_load. +-- See documentation at top of file for details on using events. +-- @param event_name +-- @param handler +function Event.add(event_name, handler) + if _LIFECYCLE == 8 then + error('Calling Event.add after on_init() or on_load() has run is a desync risk.', 2) + end + + core_add(event_name, handler) +end + +--- Register a handler for the script.on_init event. +-- This function must be called in the control stage or in Event.on_init or Event.on_load +-- See documentation at top of file for details on using events. +-- @param handler +function Event.on_init(handler) + if _LIFECYCLE == 8 then + error('Calling Event.on_init after on_init() or on_load() has run is a desync risk.', 2) + end + + core_on_init(handler) +end + +--- Register a handler for the script.on_load event. +-- This function must be called in the control stage or in Event.on_init or Event.on_load +-- See documentation at top of file for details on using events. +-- @param handler +function Event.on_load(handler) + if _LIFECYCLE == 8 then + error('Calling Event.on_load after on_init() or on_load() has run is a desync risk.', 2) + end + + core_on_load(handler) +end + +--- Register a handler for the nth_tick event. +-- This function must be called in the control stage or in Event.on_init or Event.on_load. +-- See documentation at top of file for details on using events. +-- @param tick The handler will be called every nth tick +-- @param handler +function Event.on_nth_tick(tick, handler) + if _LIFECYCLE == 8 then + error('Calling Event.on_nth_tick after on_init() or on_load() has run is a desync risk.', 2) + end + + core_on_nth_tick(tick, handler) +end + +--- Register a token handler that can be safely added and removed at runtime. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param event_name +-- @param token +function Event.add_removable(event_name, token) + if type(token) ~= 'number' then + error('token must be a number', 2) + end + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + + local tokens = token_handlers[event_name] + if not tokens then + token_handlers[event_name] = {token} + else + tokens[#tokens + 1] = token + end + + if handlers_added then + local handler = Token.get(token) + core_add(event_name, handler) + end +end + +--- Removes a token handler for the given event_name. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param event_name +-- @param token +function Event.remove_removable(event_name, token) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + local tokens = token_handlers[event_name] + + if not tokens then + return + end + + local handler = Token.get(token) + local handlers = event_handlers[event_name] + + remove(tokens, token) + remove(handlers, handler) + + if #handlers == 0 then + script_on_event(event_name, nil) + end +end + +--- Register a handler that can be safely added and removed at runtime. +-- The handler must not be a closure, as that is a desync risk. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param event_name +-- @param func +function Event.add_removable_function(event_name, func) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + if type(func) ~= 'function' then + error('func must be a function', 2) + end + + if Debug.is_closure(func) then + error( + 'func cannot be a closure as that is a desync risk. Consider using Event.add_removable(event_name, token) instead.', + 2 + ) + end + + local funcs = function_handlers[event_name] + if not funcs then + function_handlers[event_name] = {func} + else + funcs[#funcs + 1] = func + end + + if handlers_added then + core_add(event_name, func) + end +end + +--- Removes a handler for the given event_name. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param event_name +-- @param func +function Event.remove_removable_function(event_name, func) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + local funcs = function_handlers[event_name] + + if not funcs then + return + end + + local handlers = event_handlers[event_name] + + remove(funcs, func) + remove(handlers, func) + + if #handlers == 0 then + script_on_event(event_name, nil) + end +end + +--- Register a token handler for the nth tick that can be safely added and removed at runtime. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param tick +-- @param token +function Event.add_removable_nth_tick(tick, token) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + if type(token) ~= 'number' then + error('token must be a number', 2) + end + + local tokens = token_nth_tick_handlers[tick] + if not tokens then + token_nth_tick_handlers[tick] = {token} + else + tokens[#tokens + 1] = token + end + + if handlers_added then + local handler = Token.get(token) + core_on_nth_tick(tick, handler) + end +end + +--- Removes a token handler for the nth tick. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param tick +-- @param token +function Event.remove_removable_nth_tick(tick, token) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + local tokens = token_nth_tick_handlers[tick] + + if not tokens then + return + end + + local handler = Token.get(token) + local handlers = on_nth_tick_event_handlers[tick] + + remove(tokens, token) + remove(handlers, handler) + + if #handlers == 0 then + script_on_nth_tick(tick, nil) + end +end + +--- Register a handler for the nth tick that can be safely added and removed at runtime. +-- The handler must not be a closure, as that is a desync risk. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param tick +-- @param func +function Event.add_removable_nth_tick_function(tick, func) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + if type(func) ~= 'function' then + error('func must be a function', 2) + end + + if Debug.is_closure(func) then + error( + 'func cannot be a closure as that is a desync risk. Consider using Event.add_removable_nth_tick(tick, token) instead.', + 2 + ) + end + + local funcs = function_nth_tick_handlers[tick] + if not funcs then + function_nth_tick_handlers[tick] = {func} + else + funcs[#funcs + 1] = func + end + + if handlers_added then + core_on_nth_tick(tick, func) + end +end + +--- Removes a handler for the nth tick. +-- Do NOT call this method during on_load. +-- See documentation at top of file for details on using events. +-- @param tick +-- @param func +function Event.remove_removable_nth_tick_function(tick, func) + if _LIFECYCLE == stage_load then + error('cannot call during on_load', 2) + end + local funcs = function_nth_tick_handlers[tick] + + if not funcs then + return + end + + local handlers = on_nth_tick_event_handlers[tick] + + remove(funcs, func) + remove(handlers, func) + + if #handlers == 0 then + script_on_nth_tick(tick, nil) + end +end + +local function add_handlers() + for event_name, tokens in pairs(token_handlers) do + for i = 1, #tokens do + local handler = Token.get(tokens[i]) + core_add(event_name, handler) + end + end + + for event_name, funcs in pairs(function_handlers) do + for i = 1, #funcs do + local handler = funcs[i] + core_add(event_name, handler) + end + end + + for tick, tokens in pairs(token_nth_tick_handlers) do + for i = 1, #tokens do + local handler = Token.get(tokens[i]) + core_on_nth_tick(tick, handler) + end + end + + for tick, funcs in pairs(function_nth_tick_handlers) do + for i = 1, #funcs do + local handler = funcs[i] + core_on_nth_tick(tick, handler) + end + end + + handlers_added = true +end + +core_on_init(add_handlers) +core_on_load(add_handlers) + +return Event diff --git a/utils/event_core.lua b/utils/event_core.lua new file mode 100644 index 00000000..6ea0dac0 --- /dev/null +++ b/utils/event_core.lua @@ -0,0 +1,132 @@ +-- This module exists to break the circular dependency between event.lua and global.lua. +-- It is not expected that any user code would require this module instead event.lua should be required. + +local Public = {} + +local init_event_name = -1 +local load_event_name = -2 + +-- map of event_name to handlers[] +local event_handlers = {} +-- map of nth_tick to handlers[] +local on_nth_tick_event_handlers = {} + +local pcall = pcall +local log = log +local script_on_event = script.on_event +local script_on_nth_tick = script.on_nth_tick + +local function call_handlers(handlers, event) + if _DEBUG then + for i = 1, #handlers do + local handler = handlers[i] + handler(event) + end + else + for i = 1, #handlers do + local handler = handlers[i] + local success, error = pcall(handler, event) + if not success then + log(error) + end + end + end +end + +local function on_event(event) + local handlers = event_handlers[event.name] + call_handlers(handlers, event) +end + +local function on_init() + _LIFECYCLE = 5 -- on_init + local handlers = event_handlers[init_event_name] + call_handlers(handlers) + + event_handlers[init_event_name] = nil + event_handlers[load_event_name] = nil + + _LIFECYCLE = 8 -- Runtime +end + +local function on_load() + _LIFECYCLE = 6 -- on_load + local handlers = event_handlers[load_event_name] + call_handlers(handlers) + + event_handlers[init_event_name] = nil + event_handlers[load_event_name] = nil + + _LIFECYCLE = 8 -- Runtime +end + +local function on_nth_tick_event(event) + local handlers = on_nth_tick_event_handlers[event.nth_tick] + call_handlers(handlers, event) +end + +--- Do not use this function, use Event.add instead as it has safety checks. +function Public.add(event_name, handler) + local handlers = event_handlers[event_name] + if not handlers then + event_handlers[event_name] = {handler} + script_on_event(event_name, on_event) + else + table.insert(handlers, handler) + if #handlers == 1 then + script_on_event(event_name, on_event) + end + end +end + +--- Do not use this function, use Event.on_init instead as it has safety checks. +function Public.on_init(handler) + local handlers = event_handlers[init_event_name] + if not handlers then + event_handlers[init_event_name] = {handler} + script.on_init(on_init) + else + table.insert(handlers, handler) + if #handlers == 1 then + script.on_init(on_init) + end + end +end + +--- Do not use this function, use Event.on_load instead as it has safety checks. +function Public.on_load(handler) + local handlers = event_handlers[load_event_name] + if not handlers then + event_handlers[load_event_name] = {handler} + script.on_load(on_load) + else + table.insert(handlers, handler) + if #handlers == 1 then + script.on_load(on_load) + end + end +end + +--- Do not use this function, use Event.on_nth_tick instead as it has safety checks. +function Public.on_nth_tick(tick, handler) + local handlers = on_nth_tick_event_handlers[tick] + if not handlers then + on_nth_tick_event_handlers[tick] = {handler} + script_on_nth_tick(tick, on_nth_tick_event) + else + table.insert(handlers, handler) + if #handlers == 1 then + script_on_nth_tick(tick, on_nth_tick_event) + end + end +end + +function Public.get_event_handlers() + return event_handlers +end + +function Public.get_on_nth_tick_event_handlers() + return on_nth_tick_event_handlers +end + +return Public diff --git a/utils/global.lua b/utils/global.lua new file mode 100644 index 00000000..4d23bd43 --- /dev/null +++ b/utils/global.lua @@ -0,0 +1,79 @@ +local Event = require 'utils.event_core' +local Token = require 'utils.token' + +local Global = {} + +function Global.register(tbl, callback) + if _LIFECYCLE ~= _STAGE.control then + error('can only be called during the control stage', 2) + end + local token = Token.register_global(tbl) + + Event.on_load( + function() + callback(Token.get_global(token)) + end + ) +end + +function Global.register_init(tbl, init_handler, callback) + if _LIFECYCLE ~= _STAGE.control then + error('can only be called during the control stage', 2) + end + local token = Token.register_global(tbl) + + Event.on_init( + function() + init_handler(tbl) + callback(tbl) + end + ) + + Event.on_load( + function() + callback(Token.get_global(token)) + end + ) +end + +if _DEBUG then + local concat = table.concat + + local names = {} + Global.names = names + + function Global.register(tbl, callback) + local filepath = debug.getinfo(2, 'S').source:match('^.+/currently%-playing/(.+)$'):sub(1, -5) + local token = Token.register_global(tbl) + + names[token] = concat {token, ' - ', filepath} + + Event.on_load( + function() + callback(Token.get_global(token)) + end + ) + end + + function Global.register_init(tbl, init_handler, callback) + local filepath = debug.getinfo(2, 'S').source:match('^.+/currently%-playing/(.+)$'):sub(1, -5) + local token = Token.register_global(tbl) + + names[token] = concat {token, ' - ', filepath} + + Event.on_init( + function() + init_handler(tbl) + callback(tbl) + end + ) + + Event.on_load( + function() + callback(Token.get_global(token)) + end + ) + end +end + +return Global diff --git a/utils/token.lua b/utils/token.lua new file mode 100644 index 00000000..f9d511b7 --- /dev/null +++ b/utils/token.lua @@ -0,0 +1,55 @@ +local Token = {} + +local tokens = {} + +local counter = 0 + +--- Assigns a unquie id for the given var. +-- This function cannot be called after on_init() or on_load() has run as that is a desync risk. +-- Typically this is used to register functions, so the id can be stored in the global table +-- instead of the function. This is becasue closures cannot be safely stored in the global table. +-- @param var +-- @return number the unique token for the variable. +function Token.register(var) + if _LIFECYCLE == 8 then + error('Calling Token.register after on_init() or on_load() has run is a desync risk.', 2) + end + + counter = counter + 1 + + tokens[counter] = var + + return counter +end + +function Token.get(token_id) + return tokens[token_id] +end + +global.tokens = {} + +function Token.register_global(var) + local c = #global.tokens + 1 + + global.tokens[c] = var + + return c +end + +function Token.get_global(token_id) + return global.tokens[token_id] +end + +function Token.set_global(token_id, var) + global.tokens[token_id] = var +end + +local uid_counter = 0 + +function Token.uid() + uid_counter = uid_counter + 1 + + return uid_counter +end + +return Token