Improving multi-window/multiprocess support

Issue #1335 duplicate
SoniEx2
created an issue

There are 2 ways to do multi-window games/apps:

  1. Use multi-window APIs.
  2. Use multiple processes, and some sort of daemon/manager.

This issue concerns the latter.


There are 2 issues to having good multiprocess support: argument processing, and multiprocess APIs.

Argument processing

Currently, argument processing is very unintuitive:

  1. When calling the game with a command love game, whether the game is a zip/.love or a dir, game arguments (as in arg) contains 'love' (or whatever the path to the love bin is) in arg[-2] and 'embedded boot.lua' in arg[-1]. arg[1] may contain the game name, or it may contain arguments to love, or arguments to the game. same for arg[2] and so on. arg[0] is nil.

    This makes it really hard to parse command arguments and re-launch love with new arguments:

    1. If you want to call love again with the same arguments, you need to remove arg[-1] aka 'embedded boot.lua', and then move arg[-2] into arg[1]. Calling love with love 'embedded boot.lua' gamename gives you the no-game screen.
    2. To be able to parse arguments, you need to know which arguments are being handled by love. And that is basically all of them: love gamename --game anothergame launches anothergame!
  2. When calling a fused game, you instead get 'embedded boot.lua' in arg[-2] and fusedgame.exe in arg[-1]! And arg[0] is still nil.

(Would anyone be able to tell me, with '/usr/bin/embedded boot.lua' as the love bin, 'embedded boot.lua' as the game, and there are one or more 'embedded boot.lua' arguments, whether arg={[-2]='embedded boot.lua', [-1]='embedded boot.lua', [1]='embedded boot.lua', [2]='embedded boot.lua', [3]='--fused'} is running as a fused game with 2 arguments or an unfused game with 1 argument?)

I propose to remove the 'embedded boot.lua', move the actual game to arg[0], remove --game, stop parsing options after finding the game, and support -- to explicitly stop parsing options (for the case where the game name starts with --).

Multiprocess APIs

The issue of multiprocess APIs can be further split into 2 smaller issues: IPC/message passing/FFI shared memory (we can use the FFI here - all systems that support multiple windows, namely Windows, macOS and Linux, also support FFI), and an API for launching, interrupting and checking the status of subprocesses.

IPC

There are many options available for IPC. The most popular is by far ZeroMQ, however I believe nanomsg would be a better choice.

I propose just having nanomsg available.

Subprocess management

There should be a way to spawn a new process with a table of arguments, similar to Python's subprocess API, returning a subprocess object. There needs to be a way to asynchronously retrieve a subprocess's status ("running", "exited", etc, and exit codes and stuff).

These 3 things would let me write GUI apps with LÖVE significantly improve multiprocess/multi-window in LÖVE.

Comments (16)

  1. Alex Szpakowski

    As noted in issue #1229, LÖVE 0.11 allows you to retrieve the non-parsed arguments. 0.11 also has a love.filesystem.getExecutablePath function which should be preferred over arg[lowestindex] when possible.

    I don't think IPC apis are within the scope of a game framework, although you're free to use C libraries via the FFI, Lua C libraries, or OS C functions via the FFI, of course.

  2. SoniEx2 reporter

    As noted in #1229, argument handling is still completely unintuitive. And there's still that hidden BS argument which makes spawning subprocesses impossible.

  3. Alex Szpakowski

    Why wouldn't this work?

    function love.load(gameargs, allargs)
        local shouldspawn = true
    
        for i,v in ipairs(gameargs) do
            if v == "nospawn" then
                shouldspawn = false
                break
            end
        end
    
        local exe = love.filesystem.getExecutablePath()
    
        if shouldspawn then
            os.execute(string.format([["%s" %s %s]], exe, table.concat(allargs, " "), "nospawn"))
        end
    end
    
  4. Kyle McLamb

    I'd like to know a bit more about your use-case: this seems like a lot of work on love's part to avoid the single-window limitation, and maybe we can just work around the workaround instead

    In particular I'm doing a kind of "ghetto" multiprocess setup to test my netcode: I just have a shell script spawn a server process, then wait a fixed delay and spawn a bunch of client processes. would something like that work, maybe in addition to pulling in things like nanomsg as native libraries?

  5. SoniEx2 reporter

    Because, let's say I'm calling it with love --fused .. Your shitty script of shit calls the new instance with love . nospawn. Note the lack of --fused. This also goes for every other love argument.

    Thus, we need to GET RID OF THE 'embedded boot.lua' because it makes no sense being there. It's never in argv, why should it be in arg? Then I can just concat the whole arg table (skipping any nils) and execute that.

    That's all I ask. The part about changing boot.lua to not screw with args after gamename is mostly useful only for GUI apps, and not multiwindow games. Everything else is useful for multiwindow games.

    This way I can write the "shell script" in a multiplatform environment known as love, with much less debugging required.

    (Also, I can't use os.execute because it blocks, I can't use io.popen because it also blocks. I need to be able to check the status of the subprocesses so I can clean up, while also handling IPC requests from those subprocesses, so blocking is not an option.)

  6. Alex Szpakowski

    Because, let's say I'm calling it with love --fused .. Your shitty script of shit calls the new instance with love . nospawn. Note the lack of --fused. This also goes for every other love argument.

    No it doesn't. Note how I'm parsing gameargs but passing allargs into the os.execute call.

    (Also, I can't use os.execute because it blocks, I can't use io.popen because it also blocks.

    You can use a thread.

  7. SoniEx2 reporter

    I can't use os.execute/io.popen in a thread because it doesn't quote arguments. I can't portably quote arguments with os.execute. fork+exec and Windows's process spawning API both have deterministic argument quoting mechanisms (fork+exec is POSIX and takes an argv[], Windows takes special quoted strings which are part of the API).

  8. SoniEx2 reporter

    It is when you want a multi-window game that doesn't call rm -rf / because of spaces and backslashes in CLI arguments, and can't afford using proper multi-window APIs.

  9. SoniEx2 reporter

    It's literally marked wontfix. The excuse you gave is that love would need a major rewrite. Here I'm giving you a way that doesn't involve a major rewrite. Rather, it involves a few hundred LOC at worst and I can write the whole thing in less than a day.

  10. Bart van Strien

    Disregarding your rudeness:

    Argument processing

    It seems argument processing is a solved problem.

    IPC

    • It seems weird to add something like nanomsg to a game framework. And it's all "just sockets" anyway. I feel like luasocket is the much better (gaming) primitive.
    • Other IPC options are similarly weird, and most aren't cross-platform unless they rely on sockets.
    • Nothing stops you from using non-love solutions with love. You've got the ffi, but you've also got a plain lua(jit) interpreter, so you can use other libraries that provide IPC support.

    Subprocess management

    Yes, this is awkward. The only portably api there is to spawn processes is os.execute (or system) though, which is why lua uses that api. Some code already exists to use posix_spawn where available in love.system.openURL, but on other platforms there is no such code in love yet. Like IPC this suffers from the problem that it's not really a game thing, and most games simply don't need anything like this. Once again, I think this is more lua library territory.

  11. Alex Szpakowski

    It's literally marked wontfix. The excuse you gave is that love would need a major rewrite.

    In my response in the issue I linked, I mentioned that true multiple windows are not a feature that games generally have - partly because it makes for crappy end-user UX, since games aren't documents.

  12. Alex Szpakowski

    I know of maybe one game total out of thousands which uses 2 fullscreen windows when possible. There's basically no market asking for it, and the investment into creating and maintaining such a system in a game is just not worth it. For it to be viable for release you'd want to share resources between the windows, which is what I talk about in issue #1240 .

  13. Log in to comment