-
Notifications
You must be signed in to change notification settings - Fork 656
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🔨 Smarter "Module Stacking" and reload #3300
Comments
Pop module: function popModule(full_func_name)
-- Split full_func_name by '.'
local parts = {}
for part in string.gmatch(full_func_name, "[^%.]+") do
table.insert(parts, part)
end
-- Look up the functions from _G, so we can capture base_table and name
local base_table = _G
for i = 1, #parts - 1 do
base_table = base_table[parts[i]]
if not base_table then
print("Error: Invalid module path: " .. full_func_name)
return
end
end
-- Collect current function
local name = parts[#parts]
local current = base_table[name]
if not current then
print("Error: Function " .. name .. " does not exist in " .. full_func_name)
return
end
local current = base_table[name]
local env = getfenv(current)
-- Replace
if env.super then
base_table[name] = env.super
else
print("No super function to restore.")
end
end |
if you ever need to use fenv stuff in higher versions of Lua (online compilers/interpreters) where it isn't available, this snippet re-implements them: if not setfenv then -- Lua 5.2+
local debug = require("debug")
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
-- this assumes f is a function
local function findenv(f)
if not debug.getupvalue then return nil end
local level = 1
repeat
local name, value = debug.getupvalue(f, level)
if name == '_ENV' then return level, value end
level = level + 1
until name == nil
return nil end
getfenv = function (f) return(select(2, findenv(f)) or _G) end
setfenv = function (f, t)
local level = findenv(f)
if level then debug.setupvalue(f, level, t) end
return f end
end |
Important to remember that a module is just a bag of overrides |
if not setfenv then -- Lua 5.2+
local debug = require("debug")
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
-- this assumes f is a function
local function findenv(f)
if not debug.getupvalue then return nil end
local level = 1
repeat
local name, value = debug.getupvalue(f, level)
if name == '_ENV' then return level, value end
level = level + 1
until name == nil
return nil end
getfenv = function (f) return(select(2, findenv(f)) or _G) end
setfenv = function (f, t)
local level = findenv(f)
if level then debug.setupvalue(f, level, t) end
return f end
end
function split(str, pattern)
local parts = {}
for part in string.gmatch(str, pattern) do
table.insert(parts, part)
end
return parts
end
function pushFunction(full_func_name, func)
-- Split full_func_name by '.'
local parts = split(full_func_name, "[^%.]+")
-- Look up the functions from _G, so we can capture base_table and name
local base_table = _G
for i = 1, #parts - 1 do
base_table = base_table[parts[i]]
if not base_table then
print("Error: Invalid module path: " .. full_func_name)
return
end
end
-- Collect current function
local name = parts[#parts]
local current = base_table[name]
if not current then
print("Error: Function " .. name .. " does not exist in " .. full_func_name)
return
end
local old = base_table[name]
local thisenv = getfenv(old)
local env = { super = old }
setmetatable(env, { __index = thisenv })
setfenv(func, env)
base_table[name] = func
end
function popFunction(full_func_name)
-- Split full_func_name by '.'
local parts = split(full_func_name, "[^%.]+")
-- Look up the functions from _G, so we can capture base_table and name
local base_table = _G
for i = 1, #parts - 1 do
base_table = base_table[parts[i]]
if not base_table then
print("Error: Invalid module path: " .. full_func_name)
return
end
end
-- Collect current function
local name = parts[#parts]
local current = base_table[name]
if not current then
print("Error: Function " .. name .. " does not exist in " .. full_func_name)
return
end
local current = base_table[name]
local env = getfenv(current)
-- Replace
if env.super then
base_table[name] = env.super
else
print("No super function to restore.")
end
end
xi = {}
xi.test = {}
xi.test.func = function()
print('Original function')
end
print('1')
xi.test.func()
pushFunction('xi.test.func', function()
print('New function!')
end)
print('2')
xi.test.func()
popFunction('xi.test.func')
print('3')
xi.test.func()
|
Modules being bags of overrides: we don't really store anything on the Core side apart from a big fat bag of overrides. We'll need a global registry of what lives where, so we can do lookups of modules by name so we can enable/disable them. Then when we do lookup of "some_module" it'll go to the registry, look it up, find which overrides are associated with it, and then use whatever mechanism is required to go to each override and pull it out of the "stack". I guess the problem is then re-applying it will apply it at the end of the stack, so it could be in a different order than the original. So, we do need an enable/disable, rather than an apply/remove |
I affirm:
Describe the feature
Currently, when we load a module and its constituent overrides, each override grabs its target function, stashes it "internally" to itself as
super
and then replaces it. This is a totally one-way process.As discussed here, it was mentioned that events can sometimes (or all the time) be activated/deactivated without a server restart.
I'm still 1000% on board to implement all seasonal events as modules, I think the benefits far outweigh any drawbacks from it being a bit more awkward to deal with.
This will require a couple of additional features though:
Module setEnabled true/false, through hot reload:
If we have a couple of modules, overriding the same function:
We have no way currently of removing module 1 or 2's override, without clobbering everything else. I also think that additional hot-reloads of modules might apply the override multiple times? (Which is why you shouldn't be using hot-reload on your live servers, folks!).
We need core to be able to hot-reload a module, and as soon as it sees the hot-reload, it goes through all of it's previous overrides and removes them from their host functions, leaving the host and other overrides intact. It can then go through its regular load cycle and apply its own overrides.
Some sort of mapping system would be the goal here.
Module hooks for onInit and onDeinit
If we're going to be enabling and disabling modules at runtime, we need hooks for these actions. The use case being:
Exactly where this would happen would be unknown, since a lot of things require the zone to be not-yet populated, and a lot of things do.
But this is the sort of direction I think we should be heading in. Limited-time event code shouldn't exist alongside the regular day-to-day server code. There shouldn't be global settings for it. It should all be self-contained in event modules.
The text was updated successfully, but these errors were encountered: