From d00a5c74d243895554873772767fc7d833a26719 Mon Sep 17 00:00:00 2001 From: AFCMS Date: Wed, 31 Mar 2021 19:04:24 +0200 Subject: [PATCH] split commands code into separate files --- mods/CORE/mcl_commands/api.lua | 365 ++++++++++++++++++++++++++++ mods/CORE/mcl_commands/init.lua | 400 +------------------------------ mods/CORE/mcl_commands/utils.lua | 38 +++ mods/CORE/mcl_util/entities.lua | 11 + mods/CORE/mcl_util/init.lua | 4 + 5 files changed, 420 insertions(+), 398 deletions(-) create mode 100644 mods/CORE/mcl_commands/api.lua create mode 100644 mods/CORE/mcl_commands/utils.lua create mode 100644 mods/CORE/mcl_util/entities.lua diff --git a/mods/CORE/mcl_commands/api.lua b/mods/CORE/mcl_commands/api.lua new file mode 100644 index 000000000..743c3ecfe --- /dev/null +++ b/mods/CORE/mcl_commands/api.lua @@ -0,0 +1,365 @@ +local S = minetest.get_translator("mcl_commands") + +local mod_death_messages = minetest.get_modpath("mcl_death_messages") + +local parse_json = minetest.parse_json +local get_modpath = minetest.get_modpath + +mcl_commands.types = { + pos = {"%(? *(%-?[%d.]+) *,? *(%-?[%d.]+) *,? *(%-?[%d.]+) *%)?", + function(res, pointer) + local pos = { + x = tonumber(res[pointer]), + y = tonumber(res[pointer + 1]), + z = tonumber(res[pointer + 2]) + } + if pos.x and pos.y and pos.z then + return nil, pos, pointer+3 + else + return S("Pos is invalid!") + end + end}, + text = {"(.+)", + function(res, pointer) + if res[pointer] == tostring(res[pointer]) then + return nil, res[pointer], pointer+1 + else + return S("Text is invalid!") + end + end}, + number = {"(%-?[%d.]+)}", + function(res, pointer) + if res[pointer] == tonumber(res[pointer]) then + return nil, tonumber(res[pointer]), pointer+1 + else + return S("Number is invalid!") + end + end}, + int = {"(%-?[%d]+)}", + function(res, pointer) + if res[pointer] == math.floor(tonumber(res[pointer])) then + return nil, tonumber(res[pointer]), pointer+1 + else + return S("Int is invalid!") + end + end}, + word = {"([^ ]+)", + function(res, pointer) + if res[pointer] == tostring(res[pointer]) then + return nil, tostring(res[pointer]), pointer+1 + else + return S("Word is invalid!") + end + end}, + alpha = {"([A-Za-z]+)}", + function(res, pointer) + if res[pointer] then + return nil, res[pointer], pointer+1 + else + return S("Alpha is invalid!") + end + end}, + modname = {"([a-z0-9_]+)}", + function(res, pointer) + if get_modpath(res[pointer]) then + return nil, res[pointer], pointer+1 + else + return S("Modname is invalid!") + end + end}, + alphascore = {"([A-Za-z_]+)", + function(res, pointer) + if res[pointer] then --What is alphascore? + return nil, res[pointer], pointer+1 + else + return S("Alphascore is invalid!") + end + end}, + alphanumeric = {"([A-Za-z0-9]+)}", + function(res, pointer) + if res[pointer] then --What is alphanumerical? + return nil, res[pointer], pointer+1 + else + return S("Alphanumerical is invalid!") + end + end}, + username = {"([A-Za-z0-9-_]+)", + function(res, pointer) + --if minetest.player_exists(res[pointer]) then + if res[pointer] then + return nil, res[pointer], pointer+1 + else + return S("Player doesn't exist.") + end + end}, + target = {"([A-Za-z0-9-_]+)", + function(res, pointer) + --if minetest.player_exists(res[pointer]) then + if res[pointer] then + return nil, res[pointer], pointer+1 + else + return S("Player doesn't exist.") + end + end}, + json = {"(.+)", --FIXME + function(res, pointer) + local parsed = parse_json(res[pointer]) + if parsed then + return nil, parsed, pointer+1 + else + return S("Json failed to parse!") + end + end}, + color = {"([^ ]+)", --FIXME + function(res, pointer) + local color = mcl_util.get_color(res[pointer]) + if color then + return nil, color, pointer+1 + else + return S("Color is not a valid color name or hexadecimal!") + end + end}, + nodename = {"(%l+[%w_]+%:?[_%l]+[%w_]*)", + function(res, pointer) + if minetest.registered_items[res[pointer]] then + return nil, res[pointer], pointer+1 + else + return S("Nodename is invalid") + end + end}, +} + +function mcl_commands.register_command(name, def) + def = def or {} + local cmd = mcl_commands.build(name, def) + if minetest.registered_chatcommands[name] then + error("[mcl_commands] Failed to register command: ["..name.."] command already existing! Use mcl_commands.overide_command() if you want to overide existing command") + end + minetest.register_chatcommand(name, cmd) + minetest.log("action", "[mcl_commands] ["..name.."] command registered successfully") + return cmd +end + +function mcl_commands.override_command(name, def) + def = def or {} + local cmd = mcl_commands.build(name, def) + if minetest.registered_chatcommands[name] then + minetest.unregister_chatcommand(name) + end + minetest.register_chatcommand(name, cmd) + minetest.log("action", "[mcl_commands] ["..name.."] command overridden successfully") + return cmd +end + +local STATE_READY = 1 +local STATE_PARAM = 2 +local STATE_PARAM_TYPE = 3 +local bad_chars = {"(", ")", ".", "%", "+", "-", "*", "?", "[", "^", "$"} +local function escape(char) + if bad_chars[char] then + return "%" .. char + else + return char + end +end + +local dprint = function() end + +function mcl_commands.build(name, chat_def) + local cmd = { + _subs = {} + } + function cmd:sub(route, def) + dprint("Parsing " .. route) + + if string.trim then + route = string.trim(route) + end + + local sub = { + pattern = "^", + params = {}, + func = def.func, + privs = def.privs or {}, + desc = def.desc, + params_desc = def.params or "", + } + + -- End of param reached: add it to the pattern + local param = "" + local param_type = "" + local should_be_eos = false + local function finishParam() + if param ~= "" and param_type ~= "" then + dprint(" - Found param " .. param .. " type " .. param_type) + + local pattern = mcl_commands.types[param_type][1] + if not pattern then + error("Unrecognised param_type=" .. param_type) + end + + sub.pattern = sub.pattern .. pattern + + table.insert(sub.params, param_type) + + param = "" + param_type = "" + end + end + + -- Iterate through the route to find params + local state = STATE_READY + local catching_space = false + local match_space = " " -- change to "%s" to also catch tabs and newlines + local catch_space = match_space.."+" + for i = 1, #route do + local c = route:sub(i, i) + if should_be_eos then + error("Should be end of string. Nothing is allowed after a param of type text.") + end + + if state == STATE_READY then + if c == ":" then + dprint(" - Found :, entering param") + state = STATE_PARAM + param_type = "word" + catching_space = false + elseif c:match(match_space) then + print(" - Found space") + if not catching_space then + catching_space = true + sub.pattern = sub.pattern .. catch_space + end + else + catching_space = false + sub.pattern = sub.pattern .. escape(c) + end + elseif state == STATE_PARAM then + if c == ":" then + dprint(" - Found :, entering param type") + state = STATE_PARAM_TYPE + param_type = "" + elseif c:match(match_space) then + print(" - Found whitespace, leaving param") + state = STATE_READY + finishParam() + catching_space = true + sub.pattern = sub.pattern .. catch_space + elseif c:match("%W") then + dprint(" - Found nonalphanum, leaving param") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param = param .. c + end + elseif state == STATE_PARAM_TYPE then + if c:match(match_space) then + print(" - Found space, leaving param type") + state = STATE_READY + finishParam() + catching_space = true + sub.pattern = sub.pattern .. catch_space + elseif c:match("%W") then + dprint(" - Found nonalphanum, leaving param type") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param_type = param_type .. c + end + end + end + dprint(" - End of route") + finishParam() + sub.pattern = sub.pattern .. "$" + dprint("Pattern: " .. sub.pattern) + + table.insert(self._subs, sub) + end + + if chat_def.func then + chat_def.func(cmd) + end + + cmd.func = function(name, param) + local msg + for i = 1, #cmd._subs do + local sub = cmd._subs[i] + local res = { string.match(param, sub.pattern) } + if #res > 0 then + local pointer = 1 + local params = { name } + for j = 1, #sub.params do + local param = sub.params[j] + local value + if mcl_commands.types[param] then + msg, value, pointer = mcl_commands.check_type(param, res, pointer) + table.insert(params, value) + else + table.insert(params, res[pointer]) + pointer = pointer + 1 + end + end + local can_execute, missing_privs = minetest.check_player_privs(name, sub.privs) + if can_execute then + if table.unpack then + -- lua 5.2 or later + return sub.func(table.unpack(params)) + else + -- lua 5.1 or earlier + return sub.func(unpack(params)) + end + else + local missing_privs_str = "" + for i = 1, #missing_privs do + if not i == #missing_privs then + missing_privs_str = missing_privs_str..missing_privs[i].." " + else + missing_privs_str = missing_privs_str..missing_privs[i] + end + end + return false, "You don't have permission to run this command (missing privilege: "..missing_privs_str..")" + end + end + end + return false, msg + end + if chat_def.params then + cmd.params = chat_def.params + else + cmd.params = "" + end + cmd.privs = chat_def.privs + cmd.description = chat_def.description + return cmd +end + +function mcl_commands.check_type(type, res, pointer) + return mcl_commands.types[type][2](res, pointer) +end + +function mcl_commands.register_chatcommand_alias(alias, cmd, bypass) + if not bypass then bypass = false end + if minetest.registered_chatcommands[alias] then + minetest.log("warning", "[mcl_commands] trying to alias ["..cmd.."] to already existing ["..alias.."] command") + elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then + minetest.register_chatcommand(alias, minetest.chatcommands[cmd]) + minetest.log("action", "[mcl_commands] ["..cmd.."] command aliased successfully to ["..alias.."]") + else + minetest.log("action", "[mcl_commands] ["..cmd.."] command not aliased to ["..alias.."]") + end +end + +function mcl_commands.rename_chatcommand(newname, cmd, bypass) + if not bypass then bypass = false end + if minetest.registered_chatcommands[newname] then + minetest.log("warning", "[mcl_commands] trying to rename ["..cmd.."] to already existing ["..alias.."] command") + elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then + minetest.register_chatcommand(newname, minetest.chatcommands[cmd]) + minetest.unregister_chatcommand(cmd) + minetest.log("action", "[mcl_commands] ["..cmd.."] command renamed successfully to ["..newname.."]") + else + minetest.log("action", "[mcl_commands] ["..cmd.."] command not renamed to ["..newname.."]") + end +end \ No newline at end of file diff --git a/mods/CORE/mcl_commands/init.lua b/mods/CORE/mcl_commands/init.lua index db6a49cb3..9f6ca42b4 100644 --- a/mods/CORE/mcl_commands/init.lua +++ b/mods/CORE/mcl_commands/init.lua @@ -7,403 +7,7 @@ local mod_death_messages = minetest.get_modpath("mcl_death_messages") local modpath = minetest.get_modpath(minetest.get_current_modname()) -local parse_json = minetest.parse_json -local get_modpath = minetest.get_modpath - mcl_commands = {} -mcl_commands.types = { - pos = {"%(? *(%-?[%d.]+) *,? *(%-?[%d.]+) *,? *(%-?[%d.]+) *%)?", - function(res, pointer) - local pos = { - x = tonumber(res[pointer]), - y = tonumber(res[pointer + 1]), - z = tonumber(res[pointer + 2]) - } - if pos.x and pos.y and pos.z then - return nil, pos, pointer+3 - else - return S("Pos is invalid!") - end - end}, - text = {"(.+)", - function(res, pointer) - if res[pointer] == tostring(res[pointer]) then - return nil, res[pointer], pointer+1 - else - return S("Text is invalid!") - end - end}, - number = {"(%-?[%d.]+)}", - function(res, pointer) - if res[pointer] == tonumber(res[pointer]) then - return nil, tonumber(res[pointer]), pointer+1 - else - return S("Number is invalid!") - end - end}, - int = {"(%-?[%d]+)}", - function(res, pointer) - if res[pointer] == math.floor(tonumber(res[pointer])) then - return nil, tonumber(res[pointer]), pointer+1 - else - return S("Int is invalid!") - end - end}, - word = {"([^ ]+)", - function(res, pointer) - if res[pointer] == tostring(res[pointer]) then - return nil, tostring(res[pointer]), pointer+1 - else - return S("Word is invalid!") - end - end}, - alpha = {"([A-Za-z]+)}", - function(res, pointer) - if res[pointer] then - return nil, res[pointer], pointer+1 - else - return S("Alpha is invalid!") - end - end}, - modname = {"([a-z0-9_]+)}", - function(res, pointer) - if get_modpath(res[pointer]) then - return nil, res[pointer], pointer+1 - else - return S("Modname is invalid!") - end - end}, - alphascore = {"([A-Za-z_]+)", - function(res, pointer) - if res[pointer] then --What is alphascore? - return nil, res[pointer], pointer+1 - else - return S("Alphascore is invalid!") - end - end}, - alphanumeric = {"([A-Za-z0-9]+)}", - function(res, pointer) - if res[pointer] then --What is alphanumerical? - return nil, res[pointer], pointer+1 - else - return S("Alphanumerical is invalid!") - end - end}, - username = {"([A-Za-z0-9-_]+)", - function(res, pointer) - --if minetest.player_exists(res[pointer]) then - if res[pointer] then - return nil, res[pointer], pointer+1 - else - return S("Player doesn't exist.") - end - end}, - target = {"([A-Za-z0-9-_]+)", - function(res, pointer) - --if minetest.player_exists(res[pointer]) then - if res[pointer] then - return nil, res[pointer], pointer+1 - else - return S("Player doesn't exist.") - end - end}, - json = {"(.+)", --FIXME - function(res, pointer) - local parsed = parse_json(res[pointer]) - if parsed then - return nil, parsed, pointer+1 - else - return S("Json failed to parse!") - end - end}, - color = {"([^ ]+)", --FIXME - function(res, pointer) - local color = mcl_util.get_color(res[pointer]) - if color then - return nil, color, pointer+1 - else - return S("Color is not a valid color name or hexadecimal!") - end - end}, - nodename = {"(%l+[%w_]+%:?[_%l]+[%w_]*)", - function(res, pointer) - if minetest.registered_items[res[pointer]] then - return nil, res[pointer], pointer+1 - else - return S("Nodename is invalid") - end - end}, -} - -function mcl_commands.register_command(name, def) - def = def or {} - local cmd = mcl_commands.build(name, def) - if minetest.registered_chatcommands[name] then - error("[mcl_commands] Failed to register command: ["..name.."] command already existing! Use mcl_commands.overide_command() if you want to overide existing command") - end - minetest.register_chatcommand(name, cmd) - minetest.log("action", "[mcl_commands] ["..name.."] command registered successfully") - return cmd -end - -function mcl_commands.override_command(name, def) - def = def or {} - local cmd = mcl_commands.build(name, def) - if minetest.registered_chatcommands[name] then - minetest.unregister_chatcommand(name) - end - minetest.register_chatcommand(name, cmd) - minetest.log("action", "[mcl_commands] ["..name.."] command overridden successfully") - return cmd -end - -local STATE_READY = 1 -local STATE_PARAM = 2 -local STATE_PARAM_TYPE = 3 -local bad_chars = {"(", ")", ".", "%", "+", "-", "*", "?", "[", "^", "$"} -local function escape(char) - if bad_chars[char] then - return "%" .. char - else - return char - end -end - -local dprint = function() end - -function mcl_commands.build(name, chat_def) - local cmd = { - _subs = {} - } - function cmd:sub(route, def) - dprint("Parsing " .. route) - - if string.trim then - route = string.trim(route) - end - - local sub = { - pattern = "^", - params = {}, - func = def.func, - privs = def.privs or {}, - desc = def.desc, - params_desc = def.params or "", - } - - -- End of param reached: add it to the pattern - local param = "" - local param_type = "" - local should_be_eos = false - local function finishParam() - if param ~= "" and param_type ~= "" then - dprint(" - Found param " .. param .. " type " .. param_type) - - local pattern = mcl_commands.types[param_type][1] - if not pattern then - error("Unrecognised param_type=" .. param_type) - end - - sub.pattern = sub.pattern .. pattern - - table.insert(sub.params, param_type) - - param = "" - param_type = "" - end - end - - -- Iterate through the route to find params - local state = STATE_READY - local catching_space = false - local match_space = " " -- change to "%s" to also catch tabs and newlines - local catch_space = match_space.."+" - for i = 1, #route do - local c = route:sub(i, i) - if should_be_eos then - error("Should be end of string. Nothing is allowed after a param of type text.") - end - - if state == STATE_READY then - if c == ":" then - dprint(" - Found :, entering param") - state = STATE_PARAM - param_type = "word" - catching_space = false - elseif c:match(match_space) then - print(" - Found space") - if not catching_space then - catching_space = true - sub.pattern = sub.pattern .. catch_space - end - else - catching_space = false - sub.pattern = sub.pattern .. escape(c) - end - elseif state == STATE_PARAM then - if c == ":" then - dprint(" - Found :, entering param type") - state = STATE_PARAM_TYPE - param_type = "" - elseif c:match(match_space) then - print(" - Found whitespace, leaving param") - state = STATE_READY - finishParam() - catching_space = true - sub.pattern = sub.pattern .. catch_space - elseif c:match("%W") then - dprint(" - Found nonalphanum, leaving param") - state = STATE_READY - finishParam() - sub.pattern = sub.pattern .. escape(c) - else - param = param .. c - end - elseif state == STATE_PARAM_TYPE then - if c:match(match_space) then - print(" - Found space, leaving param type") - state = STATE_READY - finishParam() - catching_space = true - sub.pattern = sub.pattern .. catch_space - elseif c:match("%W") then - dprint(" - Found nonalphanum, leaving param type") - state = STATE_READY - finishParam() - sub.pattern = sub.pattern .. escape(c) - else - param_type = param_type .. c - end - end - end - dprint(" - End of route") - finishParam() - sub.pattern = sub.pattern .. "$" - dprint("Pattern: " .. sub.pattern) - - table.insert(self._subs, sub) - end - - if chat_def.func then - chat_def.func(cmd) - end - - cmd.func = function(name, param) - local msg - for i = 1, #cmd._subs do - local sub = cmd._subs[i] - local res = { string.match(param, sub.pattern) } - if #res > 0 then - local pointer = 1 - local params = { name } - for j = 1, #sub.params do - local param = sub.params[j] - local value - if mcl_commands.types[param] then - msg, value, pointer = mcl_commands.check_type(param, res, pointer) - table.insert(params, value) - else - table.insert(params, res[pointer]) - pointer = pointer + 1 - end - end - local can_execute, missing_privs = minetest.check_player_privs(name, sub.privs) - if can_execute then - if table.unpack then - -- lua 5.2 or later - return sub.func(table.unpack(params)) - else - -- lua 5.1 or earlier - return sub.func(unpack(params)) - end - else - local missing_privs_str = "" - for i = 1, #missing_privs do - if not i == #missing_privs then - missing_privs_str = missing_privs_str..missing_privs[i].." " - else - missing_privs_str = missing_privs_str..missing_privs[i] - end - end - return false, "You don't have permission to run this command (missing privilege: "..missing_privs_str..")" - end - end - end - return false, msg - end - if chat_def.params then - cmd.params = chat_def.params - else - cmd.params = "" - end - cmd.privs = chat_def.privs - cmd.description = chat_def.description - return cmd -end - -function mcl_commands.check_type(type, res, pointer) - return mcl_commands.types[type][2](res, pointer) -end - -function mcl_commands.register_chatcommand_alias(alias, cmd, bypass) - if not bypass then bypass = false end - if minetest.registered_chatcommands[alias] then - minetest.log("warning", "[mcl_commands] trying to alias ["..cmd.."] to already existing ["..alias.."] command") - elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then - minetest.register_chatcommand(alias, minetest.chatcommands[cmd]) - minetest.log("action", "[mcl_commands] ["..cmd.."] command aliased successfully to ["..alias.."]") - else - minetest.log("action", "[mcl_commands] ["..cmd.."] command not aliased to ["..alias.."]") - end -end - -function mcl_commands.rename_chatcommand(newname, cmd, bypass) - if not bypass then bypass = false end - if minetest.registered_chatcommands[newname] then - minetest.log("warning", "[mcl_commands] trying to rename ["..cmd.."] to already existing ["..alias.."] command") - elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then - minetest.register_chatcommand(newname, minetest.chatcommands[cmd]) - minetest.unregister_chatcommand(cmd) - minetest.log("action", "[mcl_commands] ["..cmd.."] command renamed successfully to ["..newname.."]") - else - minetest.log("action", "[mcl_commands] ["..cmd.."] command not renamed to ["..newname.."]") - end -end - -function mcl_commands.get_target_selector(target, pos, name) - if minetest.player_exists(target) then - return minetest.get_player_by_name(target) - elseif target == "@a" then - return nil, minetest.get_connected_players() - elseif target == "@r" then - local connected = minetest.get_connected_players() - return nil, connected[math.random(1, #connected)] - elseif target == "@s" then - if name then - local player = minetest.get_player_by_name(name) - end - if player then - return nil, player - else - return S("Not a valid player") - end - elseif target == "@p" then - local smallest = math.huge - local nearest - for _,player in pairs(minetest.get_connected_players()) do - local distance = vector.distance(pos, player:get_pos()) - if distance < min_distance then - min_distance = distance - nearest = player - end - end - if nearest then - return nil, nearest - else - return S("No player online") - end - elseif target == "@e" then - return minetest.luaentities --TODO: add filtering of not valid entities. - end -end \ No newline at end of file +dofile(modpath.."/api.lua") +dofile(modpath.."/utils.lua") \ No newline at end of file diff --git a/mods/CORE/mcl_commands/utils.lua b/mods/CORE/mcl_commands/utils.lua new file mode 100644 index 000000000..ccc69b752 --- /dev/null +++ b/mods/CORE/mcl_commands/utils.lua @@ -0,0 +1,38 @@ +local S = minetest.get_translator("mcl_commands") + +function mcl_commands.get_target_selector(target, pos, name) + if minetest.player_exists(target) then + return minetest.get_player_by_name(target) + elseif target == "@a" then + return nil, minetest.get_connected_players() + elseif target == "@r" then + local connected = minetest.get_connected_players() + return nil, connected[math.random(1, #connected)] + elseif target == "@s" then + if name then + local player = minetest.get_player_by_name(name) + end + if player then + return nil, player + else + return S("Not a valid player") + end + elseif target == "@p" then + local smallest = math.huge + local nearest + for _,player in pairs(minetest.get_connected_players()) do + local distance = vector.distance(pos, player:get_pos()) + if distance < min_distance then + min_distance = distance + nearest = player + end + end + if nearest then + return nil, nearest + else + return S("No player online") + end + elseif target == "@e" then + return minetest.luaentities --TODO: add filtering of not valid entities. + end +end \ No newline at end of file diff --git a/mods/CORE/mcl_util/entities.lua b/mods/CORE/mcl_util/entities.lua new file mode 100644 index 000000000..7959b4a05 --- /dev/null +++ b/mods/CORE/mcl_util/entities.lua @@ -0,0 +1,11 @@ +mcl_util.registered_blacklisted_entities = {} +function mcl_util.register_blacklisted_entities(type) + mcl_util.registered_blacklisted_entities[type] = true +end +function mcl_util.get_real_entities() + for key, val in pairs(minetest.luaentities) do + local def = minetest.registered_entities[minetest.luaentities[key].name] + if not mcl_util.registered_blacklisted_entities[def.type] then + end + end +end \ No newline at end of file diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index a43c3d5d0..84b43cd85 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -1,3 +1,5 @@ +local modpath = minetest.get_modpath(minetest.get_current_modname()) + mcl_util = {} -- Based on minetest.rotate_and_place @@ -418,3 +420,5 @@ function mcl_util.get_color(colorstr) return colorstr, hex end end + +dofile(modpath.."/entities.lua") \ No newline at end of file