mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-24 03:47:58 +02:00
commit
e02f93d84b
162
utils/color_presets.lua
Normal file
162
utils/color_presets.lua
Normal file
@ -0,0 +1,162 @@
|
||||
-- source: https://www.rapidtables.com/web/color/RGB_Color.html
|
||||
return {
|
||||
maroon = {r = 128, g = 0, b = 0},
|
||||
dark_red = {r = 139, g = 0, b = 0},
|
||||
brown = {r = 165, g = 42, b = 42},
|
||||
firebrick = {r = 178, g = 34, b = 34},
|
||||
crimson = {r = 220, g = 20, b = 60},
|
||||
red = {r = 255, g = 0, b = 0},
|
||||
tomato = {r = 255, g = 99, b = 71},
|
||||
coral = {r = 255, g = 127, b = 80},
|
||||
indian_red = {r = 205, g = 92, b = 92},
|
||||
light_coral = {r = 240, g = 128, b = 128},
|
||||
dark_salmon = {r = 233, g = 150, b = 122},
|
||||
salmon = {r = 250, g = 128, b = 114},
|
||||
light_salmon = {r = 255, g = 160, b = 122},
|
||||
orange_red = {r = 255, g = 69, b = 0},
|
||||
dark_orange = {r = 255, g = 140, b = 0},
|
||||
orange = {r = 255, g = 165, b = 0},
|
||||
gold = {r = 255, g = 215, b = 0},
|
||||
dark_golden_rod = {r = 184, g = 134, b = 11},
|
||||
golden_rod = {r = 218, g = 165, b = 32},
|
||||
pale_golden_rod = {r = 238, g = 232, b = 170},
|
||||
dark_khaki = {r = 189, g = 183, b = 107},
|
||||
khaki = {r = 240, g = 230, b = 140},
|
||||
olive = {r = 128, g = 128, b = 0},
|
||||
yellow = {r = 255, g = 255, b = 0},
|
||||
yellow_green = {r = 154, g = 205, b = 50},
|
||||
dark_olive_green = {r = 85, g = 107, b = 47},
|
||||
olive_drab = {r = 107, g = 142, b = 35},
|
||||
lawn_green = {r = 124, g = 252, b = 0},
|
||||
chart_reuse = {r = 127, g = 255, b = 0},
|
||||
green_yellow = {r = 173, g = 255, b = 47},
|
||||
dark_green = {r = 0, g = 100, b = 0},
|
||||
green = {r = 0, g = 128, b = 0},
|
||||
forest_green = {r = 34, g = 139, b = 34},
|
||||
lime = {r = 0, g = 255, b = 0},
|
||||
lime_green = {r = 50, g = 205, b = 50},
|
||||
light_green = {r = 144, g = 238, b = 144},
|
||||
pale_green = {r = 152, g = 251, b = 152},
|
||||
dark_sea_green = {r = 143, g = 188, b = 143},
|
||||
medium_spring_green = {r = 0, g = 250, b = 154},
|
||||
spring_green = {r = 0, g = 255, b = 127},
|
||||
sea_green = {r = 46, g = 139, b = 87},
|
||||
medium_aqua_marine = {r = 102, g = 205, b = 170},
|
||||
medium_sea_green = {r = 60, g = 179, b = 113},
|
||||
light_sea_green = {r = 32, g = 178, b = 170},
|
||||
dark_slate_gray = {r = 47, g = 79, b = 79},
|
||||
teal = {r = 0, g = 128, b = 128},
|
||||
dark_cyan = {r = 0, g = 139, b = 139},
|
||||
aqua = {r = 0, g = 255, b = 255},
|
||||
cyan = {r = 0, g = 255, b = 255},
|
||||
light_cyan = {r = 224, g = 255, b = 255},
|
||||
dark_turquoise = {r = 0, g = 206, b = 209},
|
||||
turquoise = {r = 64, g = 224, b = 208},
|
||||
medium_turquoise = {r = 72, g = 209, b = 204},
|
||||
pale_turquoise = {r = 175, g = 238, b = 238},
|
||||
aqua_marine = {r = 127, g = 255, b = 212},
|
||||
powder_blue = {r = 176, g = 224, b = 230},
|
||||
cadet_blue = {r = 95, g = 158, b = 160},
|
||||
steel_blue = {r = 70, g = 130, b = 180},
|
||||
corn_flower_blue = {r = 100, g = 149, b = 237},
|
||||
deep_sky_blue = {r = 0, g = 191, b = 255},
|
||||
dodger_blue = {r = 30, g = 144, b = 255},
|
||||
light_blue = {r = 173, g = 216, b = 230},
|
||||
sky_blue = {r = 135, g = 206, b = 235},
|
||||
light_sky_blue = {r = 135, g = 206, b = 250},
|
||||
midnight_blue = {r = 25, g = 25, b = 112},
|
||||
navy = {r = 0, g = 0, b = 128},
|
||||
dark_blue = {r = 0, g = 0, b = 139},
|
||||
medium_blue = {r = 0, g = 0, b = 205},
|
||||
blue = {r = 0, g = 0, b = 255},
|
||||
royal_blue = {r = 65, g = 105, b = 225},
|
||||
blue_violet = {r = 138, g = 43, b = 226},
|
||||
indigo = {r = 75, g = 0, b = 130},
|
||||
dark_slate_blue = {r = 72, g = 61, b = 139},
|
||||
slate_blue = {r = 106, g = 90, b = 205},
|
||||
medium_slate_blue = {r = 123, g = 104, b = 238},
|
||||
medium_purple = {r = 147, g = 112, b = 219},
|
||||
dark_magenta = {r = 139, g = 0, b = 139},
|
||||
dark_violet = {r = 148, g = 0, b = 211},
|
||||
dark_orchid = {r = 153, g = 50, b = 204},
|
||||
medium_orchid = {r = 186, g = 85, b = 211},
|
||||
purple = {r = 128, g = 0, b = 128},
|
||||
thistle = {r = 216, g = 191, b = 216},
|
||||
plum = {r = 221, g = 160, b = 221},
|
||||
violet = {r = 238, g = 130, b = 238},
|
||||
magenta = {r = 255, g = 0, b = 255},
|
||||
fuchsia = {r = 255, g = 0, b = 255},
|
||||
orchid = {r = 218, g = 112, b = 214},
|
||||
medium_violet_red = {r = 199, g = 21, b = 133},
|
||||
pale_violet_red = {r = 219, g = 112, b = 147},
|
||||
deep_pink = {r = 255, g = 20, b = 147},
|
||||
hot_pink = {r = 255, g = 105, b = 180},
|
||||
light_pink = {r = 255, g = 182, b = 193},
|
||||
pink = {r = 255, g = 192, b = 203},
|
||||
antique_white = {r = 250, g = 235, b = 215},
|
||||
beige = {r = 245, g = 245, b = 220},
|
||||
bisque = {r = 255, g = 228, b = 196},
|
||||
blanched_almond = {r = 255, g = 235, b = 205},
|
||||
wheat = {r = 245, g = 222, b = 179},
|
||||
corn_silk = {r = 255, g = 248, b = 220},
|
||||
lemon_chiffon = {r = 255, g = 250, b = 205},
|
||||
light_golden_rod_yellow = {r = 250, g = 250, b = 210},
|
||||
light_yellow = {r = 255, g = 255, b = 224},
|
||||
saddle_brown = {r = 139, g = 69, b = 19},
|
||||
sienna = {r = 160, g = 82, b = 45},
|
||||
chocolate = {r = 210, g = 105, b = 30},
|
||||
peru = {r = 205, g = 133, b = 63},
|
||||
sandy_brown = {r = 244, g = 164, b = 96},
|
||||
burly_wood = {r = 222, g = 184, b = 135},
|
||||
tan = {r = 210, g = 180, b = 140},
|
||||
rosy_brown = {r = 188, g = 143, b = 143},
|
||||
moccasin = {r = 255, g = 228, b = 181},
|
||||
navajo_white = {r = 255, g = 222, b = 173},
|
||||
peach_puff = {r = 255, g = 218, b = 185},
|
||||
misty_rose = {r = 255, g = 228, b = 225},
|
||||
lavender_blush = {r = 255, g = 240, b = 245},
|
||||
linen = {r = 250, g = 240, b = 230},
|
||||
old_lace = {r = 253, g = 245, b = 230},
|
||||
papaya_whip = {r = 255, g = 239, b = 213},
|
||||
sea_shell = {r = 255, g = 245, b = 238},
|
||||
mint_cream = {r = 245, g = 255, b = 250},
|
||||
slate_gray = {r = 112, g = 128, b = 144},
|
||||
light_slate_gray = {r = 119, g = 136, b = 153},
|
||||
light_steel_blue = {r = 176, g = 196, b = 222},
|
||||
lavender = {r = 230, g = 230, b = 250},
|
||||
floral_white = {r = 255, g = 250, b = 240},
|
||||
alice_blue = {r = 240, g = 248, b = 255},
|
||||
ghost_white = {r = 248, g = 248, b = 255},
|
||||
honeydew = {r = 240, g = 255, b = 240},
|
||||
ivory = {r = 255, g = 255, b = 240},
|
||||
azure = {r = 240, g = 255, b = 255},
|
||||
snow = {r = 255, g = 250, b = 250},
|
||||
black = {r = 0, g = 0, b = 0},
|
||||
silver = {r = 192, g = 192, b = 192},
|
||||
dim_grey = {r = 105, g = 105, b = 105},
|
||||
dim_gray = {r = 105, g = 105, b = 105},
|
||||
grey = {r = 128, g = 128, b = 128},
|
||||
gray = {r = 128, g = 128, b = 128},
|
||||
dark_grey = {r = 169, g = 169, b = 169},
|
||||
dark_gray = {r = 169, g = 169, b = 169},
|
||||
light_grey = {r = 211, g = 211, b = 211},
|
||||
light_gray = {r = 211, g = 211, b = 211},
|
||||
gainsboro = {r = 220, g = 220, b = 220},
|
||||
white_smoke = {r = 245, g = 245, b = 245},
|
||||
white = {r = 255, g = 255, b = 255},
|
||||
jailed = {r = 255, g = 255, b = 255},
|
||||
probation = {r = 255, g = 255, b = 255},
|
||||
guest = {r = 255, g = 255, b = 255},
|
||||
auto_trusted = {r = 192, g = 192, b = 192},
|
||||
regular = {r = 0.155, g = 0.540, b = 0.898},
|
||||
admin = {r = 0.093, g = 0.768, b = 0.172},
|
||||
[-10] = {r = 255, g = 255, b = 255}, -- probation
|
||||
[0] = {r = 255, g = 255, b = 255}, -- guest
|
||||
[10] = {r = 192, g = 192, b = 192}, -- auto_trusted
|
||||
[20] = {r = 0.155, g = 0.540, b = 0.898}, -- regular
|
||||
[30] = {r = 0.093, g = 0.768, b = 0.172}, -- admin
|
||||
success = {r = 0, g = 255, b = 0},
|
||||
warning = {r = 255, g = 255, b = 0},
|
||||
fail = {r = 255, g = 0, b = 0},
|
||||
info = {r = 255, g = 255, b = 255}
|
||||
}
|
11
utils/data_stages.lua
Normal file
11
utils/data_stages.lua
Normal file
@ -0,0 +1,11 @@
|
||||
-- Non-applicable stages are commented out.
|
||||
_STAGE = {
|
||||
--settings = 1,
|
||||
--data = 2,
|
||||
--migration = 3,
|
||||
control = 4,
|
||||
init = 5,
|
||||
load = 6,
|
||||
--config_change = 7,
|
||||
runtime = 8
|
||||
}
|
164
utils/debug.lua
Normal file
164
utils/debug.lua
Normal file
@ -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 <table|string|number|boolean>
|
||||
-- @param stack_traceback <number|nil> 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 <table> LuaObject or metatable
|
||||
-- @param key <string>
|
||||
-- @return <any>
|
||||
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 <any>
|
||||
-- @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<function>
|
||||
-- @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
|
34
utils/dump_env.lua
Normal file
34
utils/dump_env.lua
Normal file
@ -0,0 +1,34 @@
|
||||
-- A small debugging tool that writes the contents of _ENV to a file when the game loads.
|
||||
-- Useful for ensuring you get the same information when loading
|
||||
-- the reference and desync levels in desync reports.
|
||||
-- dependencies
|
||||
local table = require 'utils.table'
|
||||
local Event = require 'utils.event'
|
||||
|
||||
-- localized functions
|
||||
local inspect = table.inspect
|
||||
|
||||
-- local constants
|
||||
local filename = 'env_dump.lua'
|
||||
|
||||
-- Removes metatables and the package table
|
||||
local filter = function(item, path)
|
||||
if path[#path] ~= inspect.METATABLE and item ~= 'package' then
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
local function player_joined(event)
|
||||
game.tick_paused = true
|
||||
local dump_string = inspect(_ENV, {process = filter})
|
||||
if dump_string then
|
||||
local s = string.format('tick on join: %s\n%s', event.tick, dump_string)
|
||||
game.write_file(filename, s)
|
||||
game.print('_ENV dumped into ' .. filename)
|
||||
else
|
||||
game.print('_ENV not dumped, dump_string was nil')
|
||||
end
|
||||
game.print('Game is paused. Use /c game.tick_paused = false to resume play')
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, player_joined)
|
542
utils/event.lua
542
utils/event.lua
@ -1,122 +1,241 @@
|
||||
--- 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 generate_event_name = script.generate_event_name
|
||||
|
||||
local Event = {}
|
||||
|
||||
local init_event_name = -1
|
||||
local load_event_name = -2
|
||||
local handlers_added = false -- set to true after the removeable event handlers have been added.
|
||||
|
||||
local control_stage = true
|
||||
local event_handlers = EventCore.get_event_handlers()
|
||||
local on_nth_tick_event_handlers = EventCore.get_on_nth_tick_event_handlers()
|
||||
|
||||
-- map of event_name to handlers[]
|
||||
local event_handlers = {}
|
||||
-- map of nth_tick to handlers[]
|
||||
local on_nth_tick_event_handlers = {}
|
||||
local token_handlers = {}
|
||||
local token_nth_tick_handlers = {}
|
||||
local function_handlers = {}
|
||||
local function_nth_tick_handlers = {}
|
||||
|
||||
local function call_handlers(handlers, event)
|
||||
if _DEBUG then
|
||||
for _, handler in ipairs(handlers) do
|
||||
handler(event)
|
||||
end
|
||||
else
|
||||
for _, handler in ipairs(handlers) do
|
||||
local success, error = pcall(handler, event)
|
||||
if not success then
|
||||
log(error)
|
||||
end
|
||||
end
|
||||
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
|
||||
end
|
||||
)
|
||||
|
||||
local function on_event(event)
|
||||
local handlers = event_handlers[event.name]
|
||||
call_handlers(handlers, event)
|
||||
end
|
||||
|
||||
local function on_init()
|
||||
local handlers = event_handlers[init_event_name]
|
||||
call_handlers(handlers)
|
||||
end
|
||||
|
||||
local function on_load()
|
||||
local handlers = event_handlers[load_event_name]
|
||||
call_handlers(handlers)
|
||||
end
|
||||
|
||||
local function on_nth_tick_event(event)
|
||||
local handlers = on_nth_tick_event_handlers[event.nth_tick]
|
||||
call_handlers(handlers, event)
|
||||
end
|
||||
|
||||
function Event.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)
|
||||
end
|
||||
end
|
||||
|
||||
function Event.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)
|
||||
end
|
||||
end
|
||||
|
||||
function Event.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)
|
||||
end
|
||||
end
|
||||
|
||||
function Event.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)
|
||||
end
|
||||
end
|
||||
|
||||
local Token = require 'utils.global_token'
|
||||
global.event_tokens = {}
|
||||
|
||||
function Event.add_removable(event_name, token)
|
||||
local event_tokens = global.event_tokens
|
||||
|
||||
local tokens = event_tokens[event_name]
|
||||
if not tokens then
|
||||
event_tokens[event_name] = {token}
|
||||
else
|
||||
table.insert(tokens, token)
|
||||
local function remove(tbl, handler)
|
||||
if tbl == nil then
|
||||
return
|
||||
end
|
||||
|
||||
if not control_stage then
|
||||
local handler = Token.get(token)
|
||||
Event.add(event_name, handler)
|
||||
end
|
||||
end
|
||||
|
||||
local function remove(t, e)
|
||||
for i, v in ipairs(t) do
|
||||
if v == e then
|
||||
table.remove(t, i)
|
||||
-- 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
|
||||
|
||||
function Event.remove_removable(event_name, token)
|
||||
local event_tokens = global.event_tokens
|
||||
--- 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<number>
|
||||
-- @param handler<function>
|
||||
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
|
||||
|
||||
local tokens = event_tokens[event_name]
|
||||
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>
|
||||
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>
|
||||
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<number> The handler will be called every nth tick
|
||||
-- @param handler<function>
|
||||
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<number>
|
||||
-- @param token<number>
|
||||
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<number>
|
||||
-- @param token<number>
|
||||
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
|
||||
@ -129,24 +248,219 @@ function Event.remove_removable(event_name, token)
|
||||
remove(handlers, handler)
|
||||
|
||||
if #handlers == 0 then
|
||||
script.on_event(event_name, nil)
|
||||
script_on_event(event_name, nil)
|
||||
end
|
||||
end
|
||||
|
||||
local function add_token_handlers()
|
||||
control_stage = false
|
||||
--- 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<number>
|
||||
-- @param func<function>
|
||||
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
|
||||
|
||||
local event_tokens = global.event_tokens
|
||||
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
|
||||
|
||||
for event_name, tokens in pairs(event_tokens) do
|
||||
for _, token in ipairs(tokens) do
|
||||
local handler = Token.get(token)
|
||||
Event.add(event_name, handler)
|
||||
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<number>
|
||||
-- @param func<function>
|
||||
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<number>
|
||||
-- @param token<number>
|
||||
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<number>
|
||||
-- @param token<number>
|
||||
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<number>
|
||||
-- @param func<function>
|
||||
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<number>
|
||||
-- @param func<function>
|
||||
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
|
||||
|
||||
--- Generate a new, unique event ID.
|
||||
-- @param <string> name of the event/variable that is exposed
|
||||
function Event.generate_event_name(name)
|
||||
local event_id = generate_event_name()
|
||||
|
||||
defines.events[name] = event_id -- luacheck: ignore 122
|
||||
|
||||
return event_id
|
||||
end
|
||||
-- If we're not in debug, we just turn the above function into an alias of script.generate_event_name
|
||||
if not _DEBUG then
|
||||
Event.generate_event_name = generate_event_name
|
||||
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
|
||||
|
||||
Event.on_init(add_token_handlers)
|
||||
Event.on_load(add_token_handlers)
|
||||
core_on_init(add_handlers)
|
||||
core_on_load(add_handlers)
|
||||
|
||||
return Event
|
||||
|
@ -11,13 +11,23 @@ local event_handlers = {}
|
||||
-- map of nth_tick to handlers[]
|
||||
local on_nth_tick_event_handlers = {}
|
||||
|
||||
local function call_handlers(handlers, event)
|
||||
if _DEBUG then
|
||||
for _, handler in ipairs(handlers) do
|
||||
local pcall = pcall
|
||||
local log = log
|
||||
local script_on_event = script.on_event
|
||||
local script_on_nth_tick = script.on_nth_tick
|
||||
|
||||
local call_handlers
|
||||
if _DEBUG then
|
||||
function call_handlers(handlers, event)
|
||||
for i = 1, #handlers do
|
||||
local handler = handlers[i]
|
||||
handler(event)
|
||||
end
|
||||
else
|
||||
for _, handler in ipairs(handlers) do
|
||||
end
|
||||
else
|
||||
function call_handlers(handlers, event)
|
||||
for i = 1, #handlers do
|
||||
local handler = handlers[i]
|
||||
local success, error = pcall(handler, event)
|
||||
if not success then
|
||||
log(error)
|
||||
@ -63,11 +73,11 @@ 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)
|
||||
script_on_event(event_name, on_event)
|
||||
else
|
||||
table.insert(handlers, handler)
|
||||
if #handlers == 1 then
|
||||
script.on_event(event_name, on_event)
|
||||
script_on_event(event_name, on_event)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -105,11 +115,11 @@ 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)
|
||||
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)
|
||||
script_on_nth_tick(tick, on_nth_tick_event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -71,6 +71,20 @@ function Game.player_print(str)
|
||||
end
|
||||
end
|
||||
|
||||
function Game.get_player(mixed)
|
||||
if type(mixed) == "table" then
|
||||
if mixed.__self then
|
||||
return mixed and mixed.valid and mixed
|
||||
elseif mixed.player_index then
|
||||
local player = game.players[mixed.player_index]
|
||||
return player and player.valid and player
|
||||
end
|
||||
elseif mixed then
|
||||
local player = game.players[mixed]
|
||||
return player and player.valid and player
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
@param Position String to display at
|
||||
@param text String to display
|
||||
|
@ -1,46 +1,79 @@
|
||||
local Event = require 'utils.event'
|
||||
local Token = require 'utils.global_token'
|
||||
local Event = require 'utils.event_core'
|
||||
local Token = require 'utils.token'
|
||||
|
||||
local Global = {}
|
||||
|
||||
local load_data = {}
|
||||
local init_data = {}
|
||||
|
||||
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)
|
||||
table.insert(load_data, {tbl = tbl, callback = callback, token = token})
|
||||
|
||||
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)
|
||||
table.insert(load_data, {tbl = tbl, callback = callback, token = token})
|
||||
|
||||
table.insert(init_data, {token = token, init_handler = init_handler, callback = callback})
|
||||
Event.on_init(
|
||||
function()
|
||||
init_handler(tbl)
|
||||
callback(tbl)
|
||||
end
|
||||
)
|
||||
|
||||
Event.on_load(
|
||||
function()
|
||||
callback(Token.get_global(token))
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
Event.on_load(
|
||||
function()
|
||||
for _, d in ipairs(load_data) do
|
||||
local tbl = Token.get_global(d.token)
|
||||
d.callback(tbl)
|
||||
end
|
||||
if _DEBUG then
|
||||
local concat = table.concat
|
||||
|
||||
load_data = nil
|
||||
init_data = nil
|
||||
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
|
||||
)
|
||||
|
||||
Event.on_init(
|
||||
function()
|
||||
for _, d in ipairs(init_data) do
|
||||
local tbl = Token.get_global(d.token)
|
||||
d.init_handler(tbl)
|
||||
d.callback(tbl)
|
||||
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)
|
||||
|
||||
load_data = nil
|
||||
init_data = nil
|
||||
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
|
||||
|
342
utils/inspect.lua
Normal file
342
utils/inspect.lua
Normal file
@ -0,0 +1,342 @@
|
||||
local inspect ={
|
||||
_VERSION = 'inspect.lua 3.1.0',
|
||||
_URL = 'http://github.com/kikito/inspect.lua',
|
||||
_DESCRIPTION = 'human-readable representations of tables',
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2013 Enrique García Cota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
}
|
||||
|
||||
local tostring = tostring
|
||||
|
||||
inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
|
||||
inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
|
||||
|
||||
-- Apostrophizes the string if it has quotes, but not aphostrophes
|
||||
-- Otherwise, it returns a regular quoted string
|
||||
local function smartQuote(str)
|
||||
if str:match('"') and not str:match("'") then
|
||||
return "'" .. str .. "'"
|
||||
end
|
||||
return '"' .. str:gsub('"', '\\"') .. '"'
|
||||
end
|
||||
|
||||
-- \a => '\\a', \0 => '\\0', 31 => '\31'
|
||||
local shortControlCharEscapes = {
|
||||
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
|
||||
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
|
||||
}
|
||||
local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
|
||||
for i=0, 31 do
|
||||
local ch = string.char(i)
|
||||
if not shortControlCharEscapes[ch] then
|
||||
shortControlCharEscapes[ch] = "\\"..i
|
||||
longControlCharEscapes[ch] = string.format("\\%03d", i)
|
||||
end
|
||||
end
|
||||
|
||||
local function escape(str)
|
||||
return (str:gsub("\\", "\\\\")
|
||||
:gsub("(%c)%f[0-9]", longControlCharEscapes)
|
||||
:gsub("%c", shortControlCharEscapes))
|
||||
end
|
||||
|
||||
local function isIdentifier(str)
|
||||
return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
|
||||
end
|
||||
|
||||
local function isSequenceKey(k, sequenceLength)
|
||||
return type(k) == 'number'
|
||||
and 1 <= k
|
||||
and k <= sequenceLength
|
||||
and math.floor(k) == k
|
||||
end
|
||||
|
||||
local defaultTypeOrders = {
|
||||
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
|
||||
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
|
||||
}
|
||||
|
||||
local function sortKeys(a, b)
|
||||
local ta, tb = type(a), type(b)
|
||||
|
||||
-- strings and numbers are sorted numerically/alphabetically
|
||||
if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
|
||||
|
||||
local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
|
||||
-- Two default types are compared according to the defaultTypeOrders table
|
||||
if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
|
||||
elseif dta then return true -- default types before custom ones
|
||||
elseif dtb then return false -- custom types after default ones
|
||||
end
|
||||
|
||||
-- custom types are sorted out alphabetically
|
||||
return ta < tb
|
||||
end
|
||||
|
||||
-- For implementation reasons, the behavior of rawlen & # is "undefined" when
|
||||
-- tables aren't pure sequences. So we implement our own # operator.
|
||||
local function getSequenceLength(t)
|
||||
local len = 1
|
||||
local v = rawget(t,len)
|
||||
while v ~= nil do
|
||||
len = len + 1
|
||||
v = rawget(t,len)
|
||||
end
|
||||
return len - 1
|
||||
end
|
||||
|
||||
local function getNonSequentialKeys(t)
|
||||
local keys = {}
|
||||
local sequenceLength = getSequenceLength(t)
|
||||
for k,_ in pairs(t) do
|
||||
if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end
|
||||
end
|
||||
table.sort(keys, sortKeys)
|
||||
return keys, sequenceLength
|
||||
end
|
||||
|
||||
local function getToStringResultSafely(t, mt)
|
||||
local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
|
||||
local str, ok
|
||||
if type(__tostring) == 'function' then
|
||||
ok, str = pcall(__tostring, t)
|
||||
str = ok and str or 'error: ' .. tostring(str)
|
||||
end
|
||||
if type(str) == 'string' and #str > 0 then return str end
|
||||
end
|
||||
|
||||
local function countTableAppearances(t, tableAppearances)
|
||||
tableAppearances = tableAppearances or {}
|
||||
|
||||
if type(t) == 'table' then
|
||||
if not tableAppearances[t] then
|
||||
tableAppearances[t] = 1
|
||||
for k,v in pairs(t) do
|
||||
countTableAppearances(k, tableAppearances)
|
||||
countTableAppearances(v, tableAppearances)
|
||||
end
|
||||
countTableAppearances(getmetatable(t), tableAppearances)
|
||||
else
|
||||
tableAppearances[t] = tableAppearances[t] + 1
|
||||
end
|
||||
end
|
||||
|
||||
return tableAppearances
|
||||
end
|
||||
|
||||
local copySequence = function(s)
|
||||
local copy, len = {}, #s
|
||||
for i=1, len do copy[i] = s[i] end
|
||||
return copy, len
|
||||
end
|
||||
|
||||
local function makePath(path, ...)
|
||||
local keys = {...}
|
||||
local newPath, len = copySequence(path)
|
||||
for i=1, #keys do
|
||||
newPath[len + i] = keys[i]
|
||||
end
|
||||
return newPath
|
||||
end
|
||||
|
||||
local function processRecursive(process, item, path, visited)
|
||||
|
||||
if item == nil then return nil end
|
||||
if visited[item] then return visited[item] end
|
||||
|
||||
local processed = process(item, path)
|
||||
if type(processed) == 'table' then
|
||||
local processedCopy = {}
|
||||
visited[item] = processedCopy
|
||||
local processedKey
|
||||
|
||||
for k,v in pairs(processed) do
|
||||
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
|
||||
if processedKey ~= nil then
|
||||
processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
|
||||
end
|
||||
end
|
||||
|
||||
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
|
||||
setmetatable(processedCopy, mt)
|
||||
processed = processedCopy
|
||||
end
|
||||
return processed
|
||||
end
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
local Inspector = {}
|
||||
local Inspector_mt = {__index = Inspector}
|
||||
|
||||
function Inspector:puts(...)
|
||||
local args = {...}
|
||||
local buffer = self.buffer
|
||||
local len = #buffer
|
||||
for i=1, #args do
|
||||
len = len + 1
|
||||
buffer[len] = args[i]
|
||||
end
|
||||
end
|
||||
|
||||
function Inspector:down(f)
|
||||
self.level = self.level + 1
|
||||
f()
|
||||
self.level = self.level - 1
|
||||
end
|
||||
|
||||
function Inspector:tabify()
|
||||
self:puts(self.newline, string.rep(self.indent, self.level))
|
||||
end
|
||||
|
||||
function Inspector:alreadyVisited(v)
|
||||
return self.ids[v] ~= nil
|
||||
end
|
||||
|
||||
function Inspector:getId(v)
|
||||
local id = self.ids[v]
|
||||
if not id then
|
||||
local tv = type(v)
|
||||
id = (self.maxIds[tv] or 0) + 1
|
||||
self.maxIds[tv] = id
|
||||
self.ids[v] = id
|
||||
end
|
||||
return tostring(id)
|
||||
end
|
||||
|
||||
function Inspector:putKey(k)
|
||||
if isIdentifier(k) then return self:puts(k) end
|
||||
self:puts("[")
|
||||
self:putValue(k)
|
||||
self:puts("]")
|
||||
end
|
||||
|
||||
function Inspector:putTable(t)
|
||||
if t == inspect.KEY or t == inspect.METATABLE then
|
||||
self:puts(tostring(t))
|
||||
elseif self:alreadyVisited(t) then
|
||||
self:puts('<table ', self:getId(t), '>')
|
||||
elseif self.level >= self.depth then
|
||||
self:puts('{...}')
|
||||
else
|
||||
if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
|
||||
|
||||
local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t)
|
||||
local mt = getmetatable(t)
|
||||
local toStringResult = getToStringResultSafely(t, mt)
|
||||
|
||||
self:puts('{')
|
||||
self:down(function()
|
||||
if toStringResult then
|
||||
self:puts(' -- ', escape(toStringResult))
|
||||
if sequenceLength >= 1 then self:tabify() end
|
||||
end
|
||||
|
||||
local count = 0
|
||||
for i=1, sequenceLength do
|
||||
if count > 0 then self:puts(',') end
|
||||
self:puts(' ')
|
||||
self:putValue(t[i])
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
for _,k in ipairs(nonSequentialKeys) do
|
||||
if count > 0 then self:puts(',') end
|
||||
self:tabify()
|
||||
self:putKey(k)
|
||||
self:puts(' = ')
|
||||
self:putValue(t[k])
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
if mt then
|
||||
if count > 0 then self:puts(',') end
|
||||
self:tabify()
|
||||
self:puts('<metatable> = ')
|
||||
self:putValue(mt)
|
||||
end
|
||||
end)
|
||||
|
||||
if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
|
||||
self:tabify()
|
||||
elseif sequenceLength > 0 then -- array tables have one extra space before closing }
|
||||
self:puts(' ')
|
||||
end
|
||||
|
||||
self:puts('}')
|
||||
end
|
||||
end
|
||||
|
||||
function Inspector:putValue(v)
|
||||
local tv = type(v)
|
||||
|
||||
if tv == 'string' then
|
||||
self:puts(smartQuote(escape(v)))
|
||||
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
|
||||
tv == 'cdata' or tv == 'ctype' then
|
||||
self:puts(tostring(v))
|
||||
elseif tv == 'table' then
|
||||
self:putTable(v)
|
||||
else
|
||||
self:puts('<',tv,' ',self:getId(v),'>')
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
function inspect.inspect(root, options)
|
||||
options = options or {}
|
||||
|
||||
local depth = options.depth or math.huge
|
||||
local newline = options.newline or '\n'
|
||||
local indent = options.indent or ' '
|
||||
local process = options.process
|
||||
|
||||
if process then
|
||||
root = processRecursive(process, root, {}, {})
|
||||
end
|
||||
|
||||
local inspector = setmetatable({
|
||||
depth = depth,
|
||||
level = 0,
|
||||
buffer = {},
|
||||
ids = {},
|
||||
maxIds = {},
|
||||
newline = newline,
|
||||
indent = indent,
|
||||
tableAppearances = countTableAppearances(root)
|
||||
}, Inspector_mt)
|
||||
|
||||
inspector:putValue(root)
|
||||
|
||||
return table.concat(inspector.buffer)
|
||||
end
|
||||
|
||||
setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
|
||||
|
||||
return inspect
|
||||
|
@ -61,7 +61,7 @@ local data_set_handlers = {}
|
||||
-- function()
|
||||
-- Server.try_get_all_data('regulars', callback)
|
||||
-- end)
|
||||
Public.events = {on_server_started = script.generate_event_name()}
|
||||
Public.events = {on_server_started = Event.generate_event_name('on_server_started')}
|
||||
|
||||
--- Sends a message to the linked discord channel. The message is sanitized of markdown server side.
|
||||
-- @param message<string> message to send.
|
||||
|
265
utils/table.lua
Normal file
265
utils/table.lua
Normal file
@ -0,0 +1,265 @@
|
||||
--luacheck:ignore global table
|
||||
local random = math.random
|
||||
local floor = math.floor
|
||||
local remove = table.remove
|
||||
local tonumber = tonumber
|
||||
local pairs = pairs
|
||||
local table_size = table_size
|
||||
|
||||
--- Searches a table to remove a specific element without an index
|
||||
-- @param t <table> to search
|
||||
-- @param <any> table element to search for
|
||||
function table.remove_element(t, element)
|
||||
for k, v in pairs(t) do
|
||||
if v == element then
|
||||
remove(t, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Removes an item from an array in O(1) time.
|
||||
-- The catch is that fast_remove doesn't guarantee to maintain the order of items in the array.
|
||||
-- @param tbl <table> arrayed table
|
||||
-- @param index <number> Must be >= 0. The case where index > #tbl is handled.
|
||||
function table.fast_remove(tbl, index)
|
||||
local count = #tbl
|
||||
if index > count then
|
||||
return
|
||||
elseif index < count then
|
||||
tbl[index] = tbl[count]
|
||||
end
|
||||
|
||||
tbl[count] = nil
|
||||
end
|
||||
|
||||
--- Adds the contents of table t2 to table t1
|
||||
-- @param t1 <table> to insert into
|
||||
-- @param t2 <table> to insert from
|
||||
function table.add_all(t1, t2)
|
||||
for k, v in pairs(t2) do
|
||||
if tonumber(k) then
|
||||
t1[#t1 + 1] = v
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Checks if a table contains an element
|
||||
-- @param t <table>
|
||||
-- @param e <any> table element
|
||||
-- @returns <any> the index of the element or nil
|
||||
function table.index_of(t, e)
|
||||
for k, v in pairs(t) do
|
||||
if v == e then
|
||||
return k
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Checks if the arrayed portion of a table contains an element
|
||||
-- @param t <table>
|
||||
-- @param e <any> table element
|
||||
-- @returns <number|nil> the index of the element or nil
|
||||
function table.index_of_in_array(t, e)
|
||||
for i = 1, #t do
|
||||
if t[i] == e then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local index_of = table.index_of
|
||||
--- Checks if a table contains an element
|
||||
-- @param t <table>
|
||||
-- @param e <any> table element
|
||||
-- @returns <boolean> indicating success
|
||||
function table.contains(t, e)
|
||||
return index_of(t, e) and true or false
|
||||
end
|
||||
|
||||
local index_of_in_array = table.index_of_in_array
|
||||
--- Checks if the arrayed portion of a table contains an element
|
||||
-- @param t <table>
|
||||
-- @param e <any> table element
|
||||
-- @returns <boolean> indicating success
|
||||
function table.array_contains(t, e)
|
||||
return index_of_in_array(t, e) and true or false
|
||||
end
|
||||
|
||||
--- Adds an element into a specific index position while shuffling the rest down
|
||||
-- @param t <table> to add into
|
||||
-- @param index <number> the position in the table to add to
|
||||
-- @param element <any> to add to the table
|
||||
function table.set(t, index, element)
|
||||
local i = 1
|
||||
for k in pairs(t) do
|
||||
if i == index then
|
||||
t[k] = element
|
||||
return nil
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
error('Index out of bounds', 2)
|
||||
end
|
||||
|
||||
--- Chooses a random entry from a table
|
||||
-- because this uses math.random, it cannot be used outside of events
|
||||
-- @param t <table>
|
||||
-- @param key <boolean> to indicate whether to return the key or value
|
||||
-- @return <any> a random element of table t
|
||||
function table.get_random_dictionary_entry(t, key)
|
||||
local target_index = random(1, table_size(t))
|
||||
local count = 1
|
||||
for k, v in pairs(t) do
|
||||
if target_index == count then
|
||||
if key then
|
||||
return k
|
||||
else
|
||||
return v
|
||||
end
|
||||
end
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
--- Chooses a random entry from a weighted table
|
||||
-- because this uses math.random, it cannot be used outside of events
|
||||
-- @param weight_table <table> of tables with items and their weights
|
||||
-- @param item_index <number> of the index of items, defaults to 1
|
||||
-- @param weight_index <number> of the index of the weights, defaults to 2
|
||||
-- @return <any> table element
|
||||
-- @see features.chat_triggers::hodor
|
||||
function table.get_random_weighted(weighted_table, item_index, weight_index)
|
||||
local total_weight = 0
|
||||
item_index = item_index or 1
|
||||
weight_index = weight_index or 2
|
||||
|
||||
for _, w in pairs(weighted_table) do
|
||||
total_weight = total_weight + w[weight_index]
|
||||
end
|
||||
|
||||
local index = random() * total_weight
|
||||
local weight_sum = 0
|
||||
for _, w in pairs(weighted_table) do
|
||||
weight_sum = weight_sum + w[weight_index]
|
||||
if weight_sum >= index then
|
||||
return w[item_index]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Creates a fisher-yates shuffle of a sequential number-indexed table
|
||||
-- because this uses math.random, it cannot be used outside of events if no rng is supplied
|
||||
-- from: http://www.sdknews.com/cross-platform/corona/tutorial-how-to-shuffle-table-items
|
||||
-- @param t <table> to shuffle
|
||||
function table.shuffle_table(t, rng)
|
||||
local rand = rng or math.random
|
||||
local iterations = #t
|
||||
if iterations == 0 then
|
||||
error('Not a sequential table')
|
||||
return
|
||||
end
|
||||
local j
|
||||
|
||||
for i = iterations, 2, -1 do
|
||||
j = rand(i)
|
||||
t[i], t[j] = t[j], t[i]
|
||||
end
|
||||
end
|
||||
|
||||
--- Clears all existing entries in a table
|
||||
-- @param t <table> to clear
|
||||
-- @param array <boolean> to indicate whether the table is an array or not
|
||||
function table.clear_table(t, array)
|
||||
if array then
|
||||
for i = 1, #t do
|
||||
t[i] = nil
|
||||
end
|
||||
else
|
||||
for i in pairs(t) do
|
||||
t[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Returns the index where t[index] == target.
|
||||
If there is no such index, returns a negative value such that bit32.bnot(value) is
|
||||
the index that the value should be inserted to keep the list ordered.
|
||||
t must be a list in ascending order for the return value to be valid.
|
||||
|
||||
Usage example:
|
||||
local t = {1,3,5,7,9}
|
||||
local x = 5
|
||||
local index = table.binary_search(t, x)
|
||||
if index < 0 then
|
||||
game.print("value not found, smallest index where t[index] > x is: " .. bit32.bnot(index))
|
||||
else
|
||||
game.print("value found at index: " .. index)
|
||||
end
|
||||
]]
|
||||
function table.binary_search(t, target)
|
||||
--For some reason bit32.bnot doesn't return negative numbers so I'm using ~x = -1 - x instead.
|
||||
|
||||
local lower = 1
|
||||
local upper = #t
|
||||
|
||||
if upper == 0 then
|
||||
return -2 -- ~1
|
||||
end
|
||||
|
||||
repeat
|
||||
local mid = floor((lower + upper) * 0.5)
|
||||
local value = t[mid]
|
||||
if value == target then
|
||||
return mid
|
||||
elseif value < target then
|
||||
lower = mid + 1
|
||||
else
|
||||
upper = mid - 1
|
||||
end
|
||||
until lower > upper
|
||||
|
||||
return -1 - lower -- ~lower
|
||||
end
|
||||
|
||||
-- add table-related functions that exist in base factorio/util to the 'table' table
|
||||
require 'util'
|
||||
|
||||
--- Similar to serpent.block, returns a string with a pretty representation of a table.
|
||||
-- Notice: This method is not appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program.
|
||||
-- @param table <table> the table to serialize
|
||||
-- @param options <table> options are depth, newline, indent, process
|
||||
-- depth sets the maximum depth that will be printed out. When the max depth is reached, inspect will stop parsing tables and just return {...}
|
||||
-- process is a function which allow altering the passed object before transforming it into a string.
|
||||
-- A typical way to use it would be to remove certain values so that they don't appear at all.
|
||||
-- return <string> the prettied table
|
||||
table.inspect = require 'utils.inspect'
|
||||
|
||||
--- Takes a table and returns the number of entries in the table. (Slower than #table, faster than iterating via pairs)
|
||||
table.size = table_size
|
||||
|
||||
--- Creates a deepcopy of a table. Metatables and LuaObjects inside the table are shallow copies.
|
||||
-- Shallow copies meaning it copies the reference to the object instead of the object itself.
|
||||
-- @param object <table> the object to copy
|
||||
-- @return <table> the copied object
|
||||
table.deep_copy = table.deepcopy
|
||||
|
||||
--- Merges multiple tables. Tables later in the list will overwrite entries from tables earlier in the list.
|
||||
-- Ex. merge({{1, 2, 3}, {[2] = 0}, {[3] = 0}}) will return {1, 0, 0}
|
||||
-- @param tables <table> takes a table of tables to merge
|
||||
-- @return <table> a merged table
|
||||
table.merge = util.merge
|
||||
|
||||
--- Determines if two tables are structurally equal.
|
||||
-- Notice: tables that are LuaObjects or contain LuaObjects won't be compared correctly, use == operator for LuaObjects
|
||||
-- @param tbl1 <table>
|
||||
-- @param tbl2 <table>
|
||||
-- @return <boolean>
|
||||
table.equals = table.compare
|
||||
|
||||
return table
|
Loading…
x
Reference in New Issue
Block a user