From c30480520f6613552ec16ae37766fd97f27307b7 Mon Sep 17 00:00:00 2001 From: qfx Date: Mon, 27 Jan 2020 00:43:20 +0200 Subject: [PATCH] Hotswapping --- core/element.lua | 61 ++++++++++++++++++++------- debugLoader.lua | 106 +++++++++++++++++++++++++++++++++++++++++++++++ dummy.lua | 1 + init.lua | 4 +- 4 files changed, 156 insertions(+), 16 deletions(-) create mode 100644 debugLoader.lua diff --git a/core/element.lua b/core/element.lua index f89409e..e70b223 100755 --- a/core/element.lua +++ b/core/element.lua @@ -55,7 +55,8 @@ element.__index = element setmetatable(element,{ __call = function(cls, ...) local self - if type(...)=='function' then + local func, loader = ... + if type(func)=='function' then self = setmetatable({}, element) self.renderer = ... self.classless = true @@ -64,7 +65,14 @@ setmetatable(element,{ self.classless = false end - self:new(...) + if loader then + local function f(newFunc) + self:reLoader(newFunc) + end + loader(f) + end + + self:new() return self end @@ -77,6 +85,7 @@ function element:updater() end function element:constructor() end + --Control functions --The new function that should be used for element creation function element:new() @@ -109,6 +118,12 @@ function element:new() end end +--Hotswapping code +function element:reLoader(newFunc) + self.renderer = newFunc + self.context:bubbleUpdate() +end + --Called once dimensions are validated function element:setup() self.state = @@ -156,13 +171,12 @@ function element:setup() --Classless rendering if self.classless then self.classlessData.loadEffect = function (func) - if self.classlessData.loadCaptured then - return unpack(self.classlessData.loadCaptured) - else + if self.classlessData.loaded and self.classlessData.loadCaptured then + return self.classlessData.loadCaptured + elseif not self.classlessData.loaded then self.classlessData.loadCaptured = func() - if type(self.classlessData.loadCaptured) == 'table' then - return unpack(self.classlessData.loadCaptured) - elseif self.classlessData.loadCaptured then + self.classlessData.loaded = true + if self.classlessData.loadCaptured then return self.classlessData.loadCaptured end end @@ -184,7 +198,11 @@ function element:setup() self.classlessState[indice].state = set self.context:bubbleUpdate() end - return self.classlessState[indice].state, self.classlessState[indice].setState + + self.classlessState[indice].getState = function() + return self.classlessState[indice].state + end + return self.classlessState[indice].state, self.classlessState[indice].setState, self.classlessState[indice].getState end end end @@ -193,15 +211,28 @@ end function element:classlessRender() self.inputContext:set() self.settings.indice = 0 + if type(self.renderer)=='function' then + local denv = getfenv(self.renderer) + denv['useState'] = self.classlessData.useState + denv['loadEffect'] = self.classlessData.loadEffect - local denv = getfenv(self.renderer) - denv['useState'] = self.classlessData.useState - denv['loadEffect'] = self.classlessData.loadEffect + local status,err = pcall(self.renderer,self.parameters, self.view.w, self.view.h) - self.renderer(self.parameters, self.view.w, self.view.h) + if not status then + love.graphics.setColor(1,0,0) + love.graphics.rectangle('line',0,0,self.view.w,self.view.h) + love.graphics.setColor(1,1,1) + love.graphics.printf("Error: "..err,0,0,self.view.w) + end - denv['useState'] = nil - denv['loadEffect'] = nil + denv['useState'] = nil + denv['loadEffect'] = nil + elseif type(self.renderer)=='string' then + love.graphics.setColor(1,0,0) + love.graphics.rectangle('line',0,0,self.view.w,self.view.h) + love.graphics.setColor(1,1,1) + love.graphics.printf("Error: "..self.renderer,0,0,self.view.w) + end self.inputContext:unset() end diff --git a/debugLoader.lua b/debugLoader.lua new file mode 100644 index 0000000..b6e8f61 --- /dev/null +++ b/debugLoader.lua @@ -0,0 +1,106 @@ + +local path = string.sub(..., 1, string.len(...) - string.len(".debugLoader")) +local helium = require(path..'.dummy') +local elements = {} +local debugLoader = {} +--Return level: 1--string; 2--chunk; 3--return value; default: element factory +local function loader(path) + local succ = true + + --File string + local fileContents, err = love.filesystem.read(path) + + if fileContents==nil then + print('Error loading ',path,':',tostring(err),', will continue watching!') + succ = false + end + + local t, lastLoaded + if succ then + t = love.filesystem.getInfo(path) + lastLoaded = t['modtime'] + end + + --Chunk + local status, err + if succ then + status, err = pcall(loadstring,fileContents) + end + + if status==false or status==nil then + print('Error compiling ',path,':',tostring(err),', will continue watching!') + succ = false + end + + --Return values + local ret + if succ then + succ, ret = pcall(err,path) + if not succ then + print('Error calling ',path,':',tostring(ret)) + end + end + + return fileContents, err, ret, lastLoaded +end + +debugLoader.loader = function(path,returnLevel) + local level = returnLevel or 6 + if elements[path] then + return elements[path][level] + end + + local setfuncs = {} + + local fileContents, func, ret, lastLoaded = loader(path) + local reloader = function(setFunc) + setfuncs[#setfuncs+1] = setFunc + end + + local factory = function() + return helium.element(ret, reloader) + end + + elements[path] = { fileContents, func, ret, path, lastLoaded, factory, setfuncs = setfuncs } + return elements[path][level] +end + +local counter = 0 +function debugLoader.update(dt) + counter = counter+dt + if counter>2 then + for ind, elem in pairs(elements) do + --Get the current last save time + local t = love.filesystem.getInfo(elem[4]) + local ll = t['modtime'] + if ll ~= elem[5] then + --If last save time differs then start reload sequence + local _, _, ret, lastLoaded = loader(elem[4]) + + + local setfuncs = {} + + local reloader = function(setFunc) + setfuncs[#setfuncs+1] = setFunc + end + + local factory = function() + return helium.element(ret, reloader) + end + + elem[5] = lastLoaded + + elem[6] = factory + + for i, func in ipairs(elem.setfuncs) do + func(ret) + end + end + end + counter = 0 + end +end + +HeliumLoader = debugLoader.loader + +return debugLoader \ No newline at end of file diff --git a/dummy.lua b/dummy.lua index 3941af5..72fb6e4 100755 --- a/dummy.lua +++ b/dummy.lua @@ -1 +1,2 @@ +--thicc return {} \ No newline at end of file diff --git a/init.lua b/init.lua index f342779..4c637b1 100755 --- a/init.lua +++ b/init.lua @@ -8,6 +8,7 @@ local helium = require(path..".dummy") helium.utils = require(path..".utils") helium.element = require(path..".core.element") helium.input = require(path..".core.input") +helium.debugLoader = require(path..".debugLoader") helium.elementBuffer = {} function helium.render() @@ -99,7 +100,8 @@ function love.run() -- Call update and draw if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled - helium.update() + helium.update(dt) + helium.debugLoader.update(dt) if love.graphics and love.graphics.isActive() then love.graphics.clear(love.graphics.getBackgroundColor())