libretro-lutro
libretro-lutro copied to clipboard
Would adding dummy functions be worth it?
I was trying to make several Löve games work in Lutro but I always stumble with problems.
I was thinking that maybe for Löve compatibility the missing functions could be filled with dummies that just reported the function as not supported but that would not cause an instant crash.
Something like the equivalent to this:
lutro.mouse.isVisible = function()
print("WARNING: unsupported function 'lutro.mouse.isVisible'")
return false
end
Maybe the games won't really work 100% as intended when doing this, but perhaps more games would at least be playable.
It'll also make it easier to see what functions need to be implemented/replaced for the game to run. Bonus points if the warning message could also show the file and line number of the call, or something like that.
What do you think? is it worth it?
We've been doing this for a few functions if I remember well.
I've made this snippet that can be used in the header of the main.lua for Löve games to try and use lutro for available functions, or otherwise display a message & stacktrace without crashing.
if not love then
love = {}
love_compat = {
__call = function(self)
print("WARNING: unsupported method: " .. love_compat[self])
print(debug.traceback())
end,
__index = function(self, key)
local key_stack = love_compat[self] .. "." .. key
local success, lutro_value = pcall(loadstring("return " .. key_stack))
if success and lutro_value ~= nil and type(lutro_value) ~= "table" then
print("love_compat: Using " .. key_stack)
return lutro_value
else
local new = {}
setmetatable(new, love_compat)
love_compat[new] = key_stack
self[key] = new
print("love_compat: indexed " .. key_stack)
return new
end
end
}
love_compat[love] = "lutro"
setmetatable(love, love_compat)
end
So far I haven't been successful running the games, but at least it helps seeing the functions that are missing for the game to run.
I forced lutro-roguelike to go through this compatibility layer and it worked. So that means that this should work as long as the game uses compatible functions.
It was spewing a lot of "love_compat: Using lutro.graphics.draw" which is something I did not see in the Löve games I tested it with, though.
They were simply showing a black screen and then the log was totally silent, with no attempt to draw, so something else must be missing.. hmm..
I tried it with your love-vespa and it almost works. It showcases what I meant, the game runs while spewing errors wherever incompatibilities arise, without completely crashing.
Made a few stub functions for common things, but there could be more. Which functions do you run into often?
Some functions would work as warnings. Others, not so much.... window.setFullscreen could be ignored, while lutro.joystick.getJoysticks couldn't be.
Well, I was thinking just making it generalized, instead of going one by one, any index accessed that is not known would display an error or warning in the terminal (even if you do lutro.asdasdf, it would just return a function that gives nil when executed and shows the warning). I sort of did it already in Lua with my wrapper, though it ended up being more complex than I expected.
if not love then
love = {}
love_compat = {
__call = function(self)
print("WARNING: Unsupported method: " .. love_compat[self])
print(debug.traceback())
end,
__index = function(self, key)
local key_stack = love_compat[self] .. "." .. key
if love_compat[key_stack] then
return love_compat[key_stack]
end
local success, lutro_value = pcall(loadstring("return " .. key_stack))
if success and lutro_value ~= nil and type(lutro_value) ~= "table" then
print("love_compat: Using " .. key_stack)
love_compat[key_stack] = lutro_value
return lutro_value
else
local new = {}
setmetatable(new, love_compat)
print("love_compat: Indexed " .. key_stack)
love_compat[new] = key_stack
love_compat[key_stack] = new
return new
end
end,
__newindex = function(self, key, value)
local selfkey = love_compat[self]
print("love_compat: Assigning " .. selfkey .. "." .. key)
local success, lutro_self = pcall(loadstring("return " .. selfkey))
if success and lutro_self ~= nil then
lutro_self[key] = value
end
end
}
love_compat[love] = "lutro"
love_compat["lutro"] = lutro
setmetatable(love, love_compat)
-- Compatibility
local DimensionX = 640
local DimensionY = 480
getTitle = function() return "TITLE" end
getVersion = function() return 0, 0, 0, 0 end
lutro.graphics.getDimensions = function() return DimensionX, DimensionY end
lutro.graphics.newShader = function() return love.graphics.ShaderObject end
-- newCanvas should support no arguments (default to screen dimensions)
lutro.graphics.newCanvas_old = lutro.graphics.newCanvas
lutro.graphics.newCanvas = function(a,b)
return lutro.graphics.newCanvas_old(a or DimensionX, b or DimensionY)
end
-- filesystem.load should return nil on wrong/missing path
lutro.filesystem.load_old = lutro.filesystem.load
lutro.filesystem.load = function(path)
if lutro.filesystem.exists(path) then
return lutro.filesystem.load_old(path)
end
end
end
Wrapping in Lua cannot really cover unimplemented methods in userdata objects, though (e.g. the ones returned by lutro.audio.newSource or lutro.graphics.newCanvas) as it'll fail when given as parameter to C functions that expect the userdata objects.
But after writing this and seeing the games running with all this boilerplate I'm realizing that it's not worth it, while it runs and can be helpful as base for modifying the Löve code and porting the game, in Lutro current state this is not really gonna be able to make most Löve games playable without additional work that would have to be put on each individual game.