Skip to content

Commit

Permalink
Rework boot process;
Browse files Browse the repository at this point in the history
  • Loading branch information
bjornbytes committed Nov 5, 2023
1 parent eb8ffd6 commit d6c052b
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 185 deletions.
103 changes: 52 additions & 51 deletions etc/boot.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
lovr = require 'lovr'

local lovr = lovr

local conf = {
Expand Down Expand Up @@ -54,46 +55,57 @@ function lovr.boot()
lovr.filesystem = require('lovr.filesystem')

local bundle, root = lovr.filesystem.getBundlePath()
if lovr.filesystem.mount(bundle, nil, true, root) then
lovr.filesystem.setSource(bundle)
elseif arg[0] then
local source = arg[0]

if arg[0]:match('%.lua$') then
source = arg[0]:match('[/\\]') and arg[0]:gsub('[/\\][^/\\]+$', '') or '.'
end

if lovr.filesystem.mount(source) then
lovr.filesystem.setSource(source)
local fused = lovr.filesystem.mount(bundle, nil, true, root)
local cli = lovr.filesystem.isFile('arg.lua') and assert(pcall(require, 'arg')) and lovr.arg and lovr.arg(arg)
local source, main = bundle, 'main.lua'

if not fused then
if arg[1] and not arg[1]:match('^%-') then
for i = 0, #arg do
arg[i - 1], arg[i] = arg[i], nil
end
else
return function()
print(table.concat({
'usage: lovr <source>',
'<source> can be a Lua file, a folder with a main.lua file, or a zip archive'
}, '\n'))
return 1
end
end
end

local main = arg[0] and arg[0]:match('[^\\/]-%.lua$') or 'main.lua'
local hasConf, hasMain = lovr.filesystem.isFile('conf.lua'), lovr.filesystem.isFile(main)
if not lovr.filesystem.getSource() or not (hasConf or hasMain) then require('nogame') end

-- Shift args up in fused mode, instead of consuming one for the source path
if lovr.filesystem.isFused() then
for i = 1, #arg + 1 do
arg[i] = arg[i - 1]
if cli or not fused then
if arg[0] and arg[0]:match('[^/\\]+%.lua$') then
source = arg[0]:match('[/\\]') and arg[0]:match('(.+)[/\\][^/\\]+$') or '.'
main = arg[0]:match('[^/\\]+%.lua$')
else
source = arg[0]
end
arg[0] = lovr.filesystem.getSource()
end

local confOk, confError = true
if hasConf then confOk, confError = pcall(require, 'conf') end
if confOk and lovr.conf then confOk, confError = pcall(lovr.conf, conf) end
local ok, failure

conf.graphics.debug = arg['--graphics-debug'] or conf.graphics.debug
if source ~= bundle and not lovr.filesystem.mount(source) then
failure = ('Couldn\'t find path %q, or it wasn\'t a valid zip archive.'):format(source)
elseif not lovr.filesystem.isFile(main) then
failure = ('No %s file found in %q.'):format(main, source:match('[^/\\]+[/\\]?$'))
else
lovr.filesystem.setSource(source)
if lovr.filesystem.isFile('conf.lua') then ok, failure = pcall(require, 'conf') end
if ok and lovr.conf then ok, failure = pcall(lovr.conf, conf) end
end

lovr._setConf(conf)
lovr.filesystem.setIdentity(conf.identity, conf.saveprecedence)

if not failure and cli then ok, failure = pcall(cli, conf) end

for module in pairs(conf.modules) do
if conf.modules[module] then
local ok, result = pcall(require, 'lovr.' .. module)
if not ok then
print(string.format('Warning: Could not load module %q: %s', module, result))
lovr.log('warn', string.format('Could not load module %q: %s', module, result))
else
lovr[module] = result
end
Expand All @@ -108,9 +120,12 @@ function lovr.boot()
lovr.headset.start()
end

lovr.handlers = setmetatable({}, { __index = lovr })
if not confOk then error(confError) end
if hasMain then require(main:gsub('%.lua$', '')) end
if failure then
error(failure)
end

require(main:sub(1, -5))

return lovr.run()
end

Expand Down Expand Up @@ -163,7 +178,7 @@ function lovr.errhand(message)

print(message)

if not lovr.graphics or not lovr.graphics.isInitialized() then
if not lovr.graphics then
return function() return 1 end
end

Expand Down Expand Up @@ -244,37 +259,23 @@ function lovr.log(message, level, tag)
print(message)
end

return coroutine.create(function()
local errored = false
lovr.handlers = setmetatable({}, { __index = lovr })

return coroutine.create(function()
local function onerror(...)
if not errored then
errored = true -- Ensure errhand is only called once
return lovr.errhand(...) or function() return 1 end
else
local message = tostring(...) .. formatTraceback(debug.traceback('', 2))
print('Error occurred while trying to display another error:\n' .. message)
onerror = function(...)
print('Error:\n\n' .. tostring(...) .. formatTraceback(debug.traceback('', 3)))
return function() return 1 end
end
return lovr.errhand(...) or function() return 1 end
end

-- thread will be either lovr.run's step function, or the result of errhand
local _, thread = xpcall(lovr.boot, onerror)
local thread = select(2, xpcall(lovr.boot, onerror))

while true do
local ok, result, cookie = xpcall(thread, onerror)

-- If step function returned something, exit coroutine and return to C
if result and ok then
return result, cookie
elseif not ok then -- Switch to errhand loop
thread = result
if type(thread) ~= 'function' then
print('Error occurred while trying to display another error:\n' .. tostring(result))
return 1
end
end

if not ok then thread = result
elseif result then return result, cookie end
coroutine.yield()
end
end)
3 changes: 2 additions & 1 deletion src/api/l_lovr.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ static int l_lovrGetVersion(lua_State* L) {
lua_pushinteger(L, LOVR_VERSION_MAJOR);
lua_pushinteger(L, LOVR_VERSION_MINOR);
lua_pushinteger(L, LOVR_VERSION_PATCH);
return 3;
lua_pushliteral(L, LOVR_VERSION_ALIAS);
return 4;
}

static const luaL_Reg lovr[] = {
Expand Down
146 changes: 13 additions & 133 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,177 +3,57 @@
#include "core/os.h"
#include "util.h"
#include "boot.lua.h"
#include "nogame.lua.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

#ifdef EMSCRIPTEN
#include <emscripten.h>

typedef struct {
lua_State* L;
lua_State* T;
int argc;
char** argv;
} lovrEmscriptenContext;

static void emscriptenLoop(void*);
#endif

static Variant cookie;

static int luaopen_lovr_nogame(lua_State* L) {
if (!luaL_loadbuffer(L, (const char*) etc_nogame_lua, etc_nogame_lua_len, "@nogame.lua")) {
lua_call(L, 0, 1);
}

return 1;
}

int main(int argc, char** argv) {
if (argc > 1 && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v"))) {
os_open_console();
printf("LOVR %d.%d.%d (%s)\n", LOVR_VERSION_MAJOR, LOVR_VERSION_MINOR, LOVR_VERSION_PATCH, LOVR_VERSION_ALIAS);
exit(0);
}
os_init();

if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) {
os_open_console();
printf(
"usage: lovr [options] [<source>]\n\n"
"options:\n"
" -h, --help\t\tShow help and exit\n"
" -v, --version\t\tShow version and exit\n"
" --console\t\tAttach Windows console\n"
" --graphics-debug\tEnable graphics debug messages\n\n"
"<source> can be a Lua file, a folder, or a zip archive\n"
);
exit(0);
}

if (!os_init()) {
fprintf(stderr, "Failed to initialize platform");
exit(1);
}

int status;
bool restart;

do {
for (;;) {
lua_State* L = luaL_newstate();
luax_setmainthread(L);
luaL_openlibs(L);
luax_preload(L);

const luaL_Reg nogame[] = {
{ "nogame", luaopen_lovr_nogame },
{ NULL, NULL }
};

lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
luax_register(L, nogame);
lua_pop(L, 2);

// arg table
lua_newtable(L);
lua_pushstring(L, argc > 0 ? argv[0] : "lovr");
lua_setfield(L, -2, "exe");
static Variant cookie;
luax_pushvariant(L, &cookie);
lua_setfield(L, -2, "restart");

int argOffset = 1;
for (int i = 1; i < argc; i++, argOffset++) {
if (!strcmp(argv[i], "--console")) {
os_open_console();
} else if (!strcmp(argv[i], "--graphics-debug")) {
lua_pushboolean(L, true);
lua_setfield(L, -2, "--graphics-debug");
} else {
break; // This is the project path
}
}

// Now that we know the negative offset to start at, copy all args in the table
for (int i = 0; i < argc; i++) {
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, -argOffset + i);
lua_rawseti(L, -2, i);
}
lua_setglobal(L, "arg");

lua_pushcfunction(L, luax_getstack);
if (luaL_loadbuffer(L, (const char*) etc_boot_lua, etc_boot_lua_len, "@boot.lua") || lua_pcall(L, 0, 1, -2)) {
fprintf(stderr, "%s\n", lua_tostring(L, -1));
os_destroy();
return 1;
}

lua_State* T = lua_tothread(L, -1);
lovrSetErrorCallback(luax_vthrow, T);
lovrSetLogCallback(luax_vlog, T);

#ifdef EMSCRIPTEN
lovrEmscriptenContext context = { L, T, argc, argv };
emscripten_set_main_loop_arg(emscriptenLoop, (void*) &context, 0, 1);
return 0;
#endif

while (luax_resume(T, 0) == LUA_YIELD) {
os_sleep(0.);
}

restart = lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart");
status = lua_tonumber(T, 1);
luax_checkvariant(T, 2, &cookie);
if (cookie.type == TYPE_OBJECT) {
cookie.type = TYPE_NIL;
memset(&cookie.value, 0, sizeof(cookie.value));
}
lua_close(L);
} while (restart);

os_destroy();

return status;
}

#ifdef EMSCRIPTEN
// Called by JS, don't delete
void lovrDestroy(void* arg) {
if (arg) {
lovrEmscriptenContext* context = arg;
lua_State* L = context->L;
emscripten_cancel_main_loop();
lua_close(L);
os_destroy();
}
}

static void emscriptenLoop(void* arg) {
lovrEmscriptenContext* context = arg;
lua_State* T = context->T;

if (luax_resume(T, 0) != LUA_YIELD) {
bool restart = lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart");
int status = lua_tonumber(T, 1);
luax_checkvariant(T, 2, &cookie);
if (cookie.type == TYPE_OBJECT) {
cookie.type = TYPE_NIL;
memset(&cookie.value, 0, sizeof(cookie.value));
}

lua_close(context->L);
emscripten_cancel_main_loop();

if (restart) {
main(context->argc, context->argv);
if (lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart")) {
luax_checkvariant(T, 2, &cookie);
if (cookie.type == TYPE_OBJECT) memset(&cookie, 0, sizeof(cookie));
lua_close(L);
continue;
} else {
int status = lua_tointeger(T, 1);
lua_close(L);
os_destroy();
exit(status);
return status;
}
}
}
#endif

0 comments on commit d6c052b

Please sign in to comment.