-
Notifications
You must be signed in to change notification settings - Fork 13
Call for suggestions: Hotloading #10
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 8000 and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@baszalmstra, one of the developers of Mun, suggested that I look into how hot reloading of games is handled in the Lua ecosystem. I've found some very useful information there. Similar to my thoughts above, it looks like hot reloading in Lua is usually achieved by simply re-running a source file to re-initialize various globals. Because Lua is late-bound, this tends to work very well without any special intervention from the programmer. There are three common corner cases:
I can think of four additional corner cases in GameLisp which aren't present in Lua:
Finally, small syntax errors while editing code are so common that I would consider error-recovery to be an essential feature. However, if an error interrupts hot-reloading partway through reloading a game's source tree, the codebase might be left in an incoherent state. I can see two possible solutions: (1) cache all globals and restore their old values if an error occurs, or (2) encourage the programmer to pause the game and display a "please try again" prompt when an error occurs during hotloading. |
Common Lisp user here, explaining how we approach that issue. The CL standard explicily states that trying to un/redefine any of the built-in standard functionality results in undefined behavior - so the implementation is allowed to permit this, signal an error, break in subtle ways, or outright crash. I think that this approach works well for CL as a language that is meant to empower the programmer: if the user is brave enough to redefine built-in functionality, they better know what they are doing, since they are on their own at this moment. As for load-time object identity, Common Lisp implementations also handle the issue of loading compiled files (called FASLs in the CL world) and resolving load-time references to other objects. I'm not very knowledgeable on that topic. This part is not fully standardized and each implementation does it slightly differently; perhaps other lispers will be able to provide more information, if required. |
I've had a great deal of success with hotloading using GameLisp as it exists right now, by simply returning an arr of classes from my (let-class Inchworm
(field heading)
(init (ent)
(= @heading (rand-vec2 1.0 1.0)))
(const static-update (fn ()
(when (< 0.001 (rand 1.0))
(let plant (rand-select ..(instances-of 'GrassClump))
new-worm (spawn-instance 'Inchworm))
(= [new-worm 'pos] [plant 'pos]))))
(meth update (ent)
(= @heading (.norm (.+ (rand-vec2 0.1 0.1) @heading)))
(.move ent (.* @heading 0.001)))) |
Opening this issue to gather suggestions for how best to implement hotloading (that is: editing a game's source code, saving it, and seeing the changes reflected in a running game without needing to restart it).
GameLisp is extremely late-bound, which is both a blessing and a curse in this case. A blessing, because a lot of code should "just work" if we tweak
bind-global!
slightly and then runload
again with the modified source file. A curse, because the user is free to make changes which have ridiculous knock-on effects, like redefining thedefclass
macro or theload
function, or storing a reference to a class in a local variable, which is captured by a closure, which is stored in aLib
...I'm struggling to decide whether to aim for a universal, highly-correct solution, or aim for a much simpler solution which will unpredictably fail sometimes. I chose the second option when designing compilation, and I'm quite happy with the result.
It would be easy enough to detect when a hot
load
call attempts to do something more complicated than callingbind-global!
, in which case we could emit a warning, or fall back to reloading the entire source tree, or force a restart. In other words, even if naive hot-loading fails 5% of the time, we could make it a graceful failure rather than an unpleasant, confusing one.I'm considering two options for dealing with dangling references:
Class
,GFn
orquote
shares the identity of an existing one, in which case it should be mutated rather than being duplicated.let
is redefined while hotloading, brute-force scan the entire heap for dangling references and change them to point to the new definition.Also relevant: #8, #9
The text was updated successfully, but these errors were encountered: