Live code reloading in Lua

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
martincohen
Prole
Posts: 23
Joined: Sat Feb 21, 2015 10:39 am
Contact:

Live code reloading in Lua

Post by martincohen »

Hey guys,

thought, it would be interesting for you: I've made HandmadeHero-like live code reloading. I've seen that there are alreay multiple topics on this, though this one can be done quite simply from scratch. It cannot be used in existing projects (probably) if you are using encapsulation. It requires you to structure the code in data-oriented manner.

The video:
http://youtu.be/WK84Nf1p8W0

The introduction to how it works:
http://martincohen.tumblr.com/post/1121 ... ith-love2d

The main.lua directly from the demo video:
https://gist.github.com/martincohen/27e ... b9c08d45a3

--M.
<3
User avatar
Evine
Citizen
Posts: 72
Joined: Wed May 28, 2014 11:46 pm

Re: Live code reloading in Lua

Post by Evine »

I've been working on something similar as well. I've made it slightly more robust. The biggest problem with what i have is that values doesn't get reset to nil if you removed them from the code and reload.
Say a = 5 is in the program when you start it.
love.graphics.print(a) -- will print 5.
Remove a = 5 . Save file.
love.graphics.print(a) -- will still print 5, "a" should now be "nil".
Close the love program and run again.
love.graphics.print(a) -- will crash because "a" is a "nil" value.

So hidden bugs might occur if you don't restart the program regularly.

A few pluses about the current implementation.
--It creates a "Push" file you can insert values into.
--It uses a protected function call, "pcall" so the program doesn't crash if you write an error. Instead it prints to console.
--Easy resetting of certain values if you don't set it in a function

Code: Select all

--main.lua
-- free values like this gets reset when the file is saved
a = 40



function love.draw(  )

	--stuff on screen

	a = a + 0.1
	love.graphics.line(0,a,100,a)
	love.graphics.print("apple pie  , "..applePIE.." , "..tostring(a))
	love.graphics.print(love.filesystem.getLastModified("main.lua"),0,14)
end


function love.timer.sleep() end -- is bugged 


function love.update(  )
	-- loading files should be wraped into functions.
	if LastHotModified ~= love.filesystem.getLastModified("main.lua") then
		LastHotModified = love.filesystem.getLastModified("main.lua")
		ok , hotData = pcall(love.filesystem.load,"main.lua")  -- Load program
		if ok then
			print("Loaded")
			ok,err = pcall(hotData) -- Execute program
			if not ok then
				print("Execute error: "..err)
			end
		else
			print(hotData)
		end
	end

	-- Bellow is a push file so you can insert variables into the program
	if LastPushModified ~= love.filesystem.getLastModified("push.lua") then
		LastPushModified = love.filesystem.getLastModified("push.lua")
		pushData = love.filesystem.load("push.lua")
		pushData()
		love.filesystem.write("push.lua" , "\n--[["..love.filesystem.read("push.lua"))
		print("Load Push")
	end
end

function love.keypressed( key )
	if key == "o" then
		love.system.openURL("file://"..love.filesystem.getSaveDirectory())
	end
	if key == "l" then
		load()
	end
	if key == "escape" then
		love.event.quit()
	end
	if key == "f" then
		forceData = love.filesystem.load("main.lua")
		forceData()
	end

end



-- A place to store values, Does NOT automaticlly get updated when file is reloaded.
-- 

function load(  )
	applePIE = 7
	scancode = " "
end


-- What happens below here only execute when the love file is opened

if loadFirst == nil then
	loadFirst = 0
	load()
	love.filesystem.write("push.lua" , "\n--[[ Anything bellow this line will NOT be commented out automatically ]]")
	LastPushModified = love.filesystem.getLastModified("push.lua")
end
Super Simple code reloading. Note that I use "love.filesystem.load" instead of "require". "love.filesystem.load" always loads the file.

Code: Select all

--main.lua
function love.update(dt)
      mainData = love.filesystem.load("main.lua")
      mainData()
end
Artal, A .PSD loader: https://github.com/EvineDev/Artal
User avatar
Jasoco
Inner party member
Posts: 3727
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Live code reloading in Lua

Post by Jasoco »

Evine wrote:

Code: Select all

--main.lua
function love.update(dt)
      mainData = love.filesystem.load("main.lua")
      mainData()
end
This is shorter:

Code: Select all

--main.lua
function love.update(dt)
      mainData = love.filesystem.load("main.lua")()
end
User avatar
martincohen
Prole
Posts: 23
Joined: Sat Feb 21, 2015 10:39 am
Contact:

Re: Live code reloading in Lua

Post by martincohen »

Not sure if I understand the problem correctly. So, please, ignore the following if I'm being dense here. :)

Of course, "my version" is a very basic one, the important part is the data orientation there.

With data-oriented version, you don't want to have any free variables, all the data are always kept in the memory and are kept the same between the reloads. If you want to change the data, then you'll either have to have a support for changing them while the game is running (some kind of a console, or a file, as you suggest) or to simply restart the whole game.

Usually you don't want the game to be running for entire game session (as the data might get really nasty). The idea of having it this way is to help you tune some code while the game is running. Say I'd like to tune a jump code, I want the data be always the same, though I want the functionality to be updated. In HandmadeHero, Casey also did a looping feature. So you can simply record your input to the game (each button pressed/joystick state stored for each frame) and let it be replayed automatically while you are tweaking the code. You can see it here https://www.youtube.com/watch?v=xrUSrVvB21c.
<3
User avatar
Evine
Citizen
Posts: 72
Joined: Wed May 28, 2014 11:46 pm

Re: Live code reloading in Lua

Post by Evine »

I'm also watching Handhero. And I don't plan to implement a looping feature similar to what Casey implemented. I'm not convinced it's really that super useful for anything, a slight time saver if anything.

The purpose of the "free variables" is to reset certain variables each time the code is reloaded. Can also be trigger from within the game. Ex: Resetting player and boss health when you're testing the boss. Or resetting the player position when you have a poorly implemented collision detection in a platformer. Or testing AI code where you want to see how the AI handles certain situations.
martincohen wrote:Usually you don't want the game to be running for entire game session (as the data might get really nasty).
Well... Why not. I'd like to have the opportunity. It's possible with what I have now if I'm careful to set values to nil if I remove them from the code.
Jasoco wrote: This is shorter:

Code: Select all

--main.lua
function love.update(dt)
      love.filesystem.load("main.lua") () -- EVEN SHORTER 
end
Fancy syntax tricks I didn't know of. You don't even have to assign it to a variable now. It's nigh unreadable to anyone reading a slight messy codebase. I'd much rather do it like this.

Code: Select all

--main.lua
function love.update(dt)
      love.filesystem.loadRun("main.lua") -- Much more readable
end

function love.filesystem.loadRun(filename)
      love.filesystem.load(filename) ()
end
Artal, A .PSD loader: https://github.com/EvineDev/Artal
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Live code reloading in Lua

Post by s-ol »

Evine: that Syntax isn't very uncommon and you should and will get used to it quickly. Especially in the JS world there are way fancier things going on (like creating and calling an anonymous function in the same statement to create a local scope)

My apologies, that was rude and unnecessary.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 5 guests