There are a number of ways to pull in code and adjust it on the fly, but it depends on what your goal is and what your experience level is. I'm going to suggest, in order of complexity, the following methods:
- Persist certain game state tables to disk that you can reload on demand
- Persist entire game state to disk that you can reload on demand
- In-game console to set and fetch game state variables
- Persistence module to use LOADSTRING on saved functions
For the first 2, all you need to do is use any persistence module to write out the contents of a table to disk that your game uses for tracking state, and reload it later. Of course, I say "All you need to do", but there's always a hundred gotchas. One big gotcha is that not everything can be persisted - more on this later. Another big gotcha is that you'd need a way to avoid clobbering some other state you wanted to preserve if you're talking about just certain variables - which is where the third option is. In my game, quicksave/quickload do exactly this - all state related to the current running game can be saved to disk or reloaded on demand at any time, but things regarding the game setup (loading assets, profile settings, etc) are not part of that state.
For option 3 - if you just want a way to adjust certain variables in the game, an easier way would be to write an in-game developer console that you can use. I wrote one for my game that allows me to fetch any variable in the code, like so:
The console allows me to inspect anything in the game state and modify any variables that might be in the game, or run specific functions with specific arguments. You can add any functionality you want to that, including calling any function in your own code with any arguments, short of recompiling functions. You could also, theoretically, even paste in something that parsed to a valid lua function and tell your console to use loadstring and treat it as an anonymous function you could call. This is intermediate difficulty but probably the biggest value overall, though I wouldn't really recommend having a way to paste in function data.
For the last 2, these are more dangerous. If you are talking about logic changes, that's much more complicated, but not impossible. The two methods I would would be Unrequire and reload a module or Load an anonymous function from a file on demand.
The latter can be done with a persistence module, and it will re-load the code via LOADSTRING which you can then re-execute. Of course, this easily runs the possibility of you crashing your code if you try to call a method that doesn't exist or something. I did it during development, but now that I have released my game as a commercial product, I removed that ability because it introduces a strong risk of arbitrary code execution exploits. You can use this method pretty easily though, and if you want to be careful, you can first evaluate with pcall if something can execute, then use its logic. Here's the problem - in order to do this on a mass scale, you'd need to be using persistence to load and unload your modules, which itself takes a lot of setup to do properly. So it's not impossible, it's just a bit of a headache. The biggest gotchas here is that you need a way to generate the loadstring payload that will parse out the way you want, which itself is a hassle, and that you CANNOT persist upvalues. So anything using an upvalue just straight up won't work.
The former method can be implemented without persistence, though I got to be honest, I'm not entirely sure if this would result in the lua compiler actually rebuilding the source module. Basically, you'd unrequire like the below example. I actually use this in my game for 2 modules, light world and SUIT, to get around certain performance issues:
Code: Select all
function unrequire(m)
package.loaded[m] = nil
_G[m] = nil
end
--unload and destroy lighting objects
function f_ed_unload_light()
f_ed_clear_lights()
unrequire('modules.light')
collectgarbage("collect")
end
You can then re-require the module as desired, but I'd have to test if that actually rebuilds it. If that does rebuild it, that would be the cleanest way to do it on demand.