forum

Design decisions for osu! for Linux

posted
Total Posts
11
Topic Starter
aismallard
I know it's my first post, so I apologize in advance for anything I do incorrectly. This is a topic I've given a good deal of thought to, and I want the community's opinion on what the best way to add Linux compatibility would be.

Introduction

Since the announcement of osu! becoming open source, I've been thinking about how to best make osu! available natively for Linux. Peppy's blog post on osu! going open source (http://blog.ppy.sh/post/143727030323/demystifying-open-source-osu) mentioned interest in having the community bring the game to different platforms. So I think it would be a good time to discuss how to best structure it all. Unix-like systems are very different than Windows, so we should try to make osu! play nicely with these sorts of environments. For instance, following the Linux Filesystem Hiearchy (http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/) and aiming for POSIX compatibility are important to making osu! run like any other native program.

osu! is written in C#, so the natural choice for cross-platform compilation is Mono (http://www.mono-project.com/). When I tried compiling the osu!sdk, it appeared to depend on the Microsoft XNA framework, which could be provided by Monogame (http://www.monogame.net/). I'm sure there will be other dependency difficulties, but Mono is well-supported, so I'm sure we'll find some cross-platform solution as we start seeing more the code.

The rest of this post are my ideas on how osu! can be \*nix friendly. I'm making this post to start discussion on the subject, and so I appreciate any commentary or criticism of my ideas.

Basics

Every binary on a Unix-like system should, at the very least:

  1. Accept "--help" and "--version" as options.
  2. Have a man page.
  3. Respond appropriately to SIGTERM (and other signals).
There are a lot of things that osu! can do to take advantage of being on Linux (e.g. inter-process communication, package management, symbolic links, etc.), but the above is pretty basic.

General file placement
Binaries and libraries generally exist in their own directory inside "/usr/lib", and static resources should exist in "/usr/share", so osu! should use "/usr/lib/osu" and "/usr/share/osu". Side note, while we could name the directories "/usr/lib/osu!" and "/usr/share/osu!", I feel we should avoid doing so because (1) using weird characters in "/usr" is frowned upon and (2) '!' is a special character in various shells and there's not point introducing unnecessary headache.

There should be an executable file on the $PATH to allow for easy invocation, and the most obvious choice would be "/usr/bin/osu" (Again, no exclamation point for the reasons named above). I think it should be a symbolic link to "/usr/lib/osu/osu!.exe", where the actual binaries are for reasons I'll discuss in "Updates and builds". (Also I think it's ok for the file to have a '!' character because you're not invoking the file directly from a shell and I want to keep naming conventions as similar as I can)

"/usr/lib/osu" should be only binaries, and "/usr/share/osu" should not have dynamic data, so data files like replays and databases should be located elsewhere. See "Data file locations".

Updates and builds
On Windows, it is the responsibility of every application to keep itself up-to-date. Linux distros provide a nice program called the package manager, which automates this process in a centralized and organized way. For those who don't know, a package manager is a program that you use to install nearly all the software on your computer, and keeps track of the files owned by each program you install. Therefore, making this port play nicely with the package manager is quite important, and makes things much easier in the long term.

osu! has its own update system built-in, which works quite well on Windows. However, I this is not ideal on Linux, as system upgrades should update all the software on your computer. The way I think of resolving this is to disable the osu! auto-updater by default (maybe make an option for it) and release changes as package updates, so they can be updated and upgraded along with the rest of the system. In addition, it provides the benefits of easily keeping track of dependencies and allowing downgrading versions in case the latest has issues.

One issue this create is that having different builds (cutting-edge, stable-fallback, etc.) is more difficult, since we'll need to have the package manager provide this. Here two solutions I thought of:

Each build will be its own package, such as "osu-stable-latest" or "osu-beta", all of which provide "osu" and conflict with each other. Very simple, but I think this puts too much of a burden on package maintainers having to work with lots of different packages.

The other possibility is to have the one true "osu" package have all the builds in it, but use a method of swapping which binaries are actually in use. I think a good solution to this is to use symbolic links. Each different build would have its own directory within "/usr/lib/osu":

  1. /usr/lib/osu/beta
  2. /usr/lib/osu/cutting-edge
  3. /usr/lib/osu/stable-latest
  4. /usr/lib/osu/stable-fallback
  5. etc.
Each will contain all the binaries and resources necessary to run that particular build. This solution has the disadvantage of requiring more hard drive space rather than having each build downloaded on-the-fly, but I like this solution because it minimizes the manipulation of files controlled by the package manager.

Inside "/usr/lib/osu" itself there would be all the files necessary to run the program like usual, but they would actually be symbolic links to the real binary. This way changing the build from, for example, "stable-fallback" to "stable-latest" would just require the replacement of the symbolic links. A person running "stable-fallback" would have a "/usr/lib/osu" directory that looks like this: (A "->" refers to a symbolic link)

  1. /usr/lib/osu/osu!.exe -> stable-fallback/osu!.exe
  2. /usr/lib/osu/something.dll -> stable-fallback/something.dll
  3. /usr/lib/osu/something2.dll -> stable-fallback/something2.dll
  4. etc.
The only remaining problem is how to set up permissions to allow this. Borrowing from the AUR osu package (https://aur.archlinux.org/packages/osu) has a potential solution: it has the user join the "games" group, and sets the ownership of the installation directory to be "root:games", meaning that the file's owner is root, and the file's group is games. If the permissions are 0775 (drwxrwxr-x) or similar, it would allow users to update the symbolic links.

Location of songs and skins
Program resources are generally located in "/usr/share", meaning that songs and skins would be located in "/usr/share/osu/songs" and "/usr/share/osu/skins", respectively. However, there could be some merit to storing these files in "~/.osu".

Reasons for "/usr/share/osu":
  1. Installed beatmaps and skins are available to all users
  2. Prevents the home directory from getting too big
Reasons for "~/.osu":
  1. No special permissions required (see the root:games explanation above)
  2. Acts as a kind of "portable mode" by just moving your ".osu" around
Data file Locations
Unix has always been a multi-user system, so I think it's good design to set up the file structure in such a way that it allows multiple users to play at the same time. This can be accomplished by segregating data files like scores into user-specific folders. (Which it should anyways, since the contents of "/usr" should not store non-static data.

This is the part I'm least sure about. The FHS specifies "/var/game" as a location for "variable data relating to games in /usr". (http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/var.html) The other location where it would be appropriate would be in the user's home directory in "~/.osu", if we decide to go that route. (See above). I've listed what I think each would look like.

  1. /var/games/osu
  2. /var/games/osu/$USER/chat
  3. /var/games/osu/$USER/data
  4. /var/games/osu/$USER/db/collection.db
  5. /var/games/osu/$USER/db/osu!.db
  6. /var/games/osu/$USER/log
  7. /var/games/osu/$USER/replays
vs

  1. ~/.osu
  2. ~/.osu/chat
  3. ~/.osu/db
  4. ~/.osu/log
  5. ~/.osu/replays
  6. ~/.osu/screenshots
It could also be some combination of the both, which could make more sense. Like downloads would be located at "~/.osu/downloads" since the user would like easy access to those files, and *.db files would stay in "/var/games/osu".

The big concern I have with "~/.osu" is bloat. I don't think having a dotfolder in your home directory that is more than a gigabyte is good, and should be avoided if possible.

Run time files
Temporary files should be located in "/tmp" or "/run". Since multiple users may be running osu!, the directory should be user-specific. So either "/tmp/$USER-osu" or "/run/user/$UID/osu" should be used. (Some systems lack a "/run", so in those cases "/var/run" should be used instead).

There are a few files that could reside in this directory other than temporary files:
"/run/user/$USER/osu/pid" - PID (process id) file for osu!, to easily determine if it's already running
"/run/user/$USER/osu/fifo" - A control FIFO (aka named pipe) to allow scripts to send simple commands to osu!
"/run/user/$USER/osu/socket" - A control socket to allow programs to interact with and send commands to osu!. Could be extended to add the API as well.

Linux-specific features
Finally, a list of a few random things I thought of that may or may not be worth looking into:

  1. Fancy IPC (inter-process communication, see above)
  2. Wayland support
  3. Conf-style config files.
  4. DBus server
  5. X11-based cursor (I think X allows for very large cursor sizes)
  6. osu!-related scripts in "/usr/bin" (backing up beatmaps, etc.)
These are just a few rough ideas, so I welcome any and all criticism.
peppy
We alreayd have osu! running using ~/.osu/ fwiw (even on windows)



All your other issues are already either fixed or almost fixed. Updating will still be handled by osu!, though, because osu! is a game not a system application.

- Fancy IPC (inter-process communication, see above)

is/will not be implemented platform specific.

Wayland support

is not at osu!'s end (up to whatever .net implementation we use).

- Conf-style config files.

why? no. the idea of cross platform is every platform will behave roughly the same.

- DBus server

why? no. the idea of cross platform is every platform will behave roughly the same.

- X11-based cursor (I think X allows for very large cursor sizes)

why? no. the idea of cross platform is every platform will behave roughly the same.

- osu!-related scripts in "/usr/bin" (backing up beatmaps, etc.)

this should be a separate project, and not packaged with osu! (just as it isn't on the current windows install).
thelittlestgirl

peppy wrote:

Updating will still be handled by osu!, though, because osu! is a game not a system application.
It's userland software just like anything else may need to install via package management. I don't really understand why being a game would be any different.

In a lot of *nix environments software is installed so it can be modified by root only. Having osu! update itself would surely either involve making osu! modifiable by non-superusers (which could have security implications), or have osu! run itself as root (which is not okay). One compromise I suppose would be to have a separate updater application which you run as root when needed, but you may as well just use normal package management in that case.
Raymonf
If you "port" it to be native, then it's not a port. Instead, it's a clone (at least, that's what I think).
Compiling osu! with mono does not result in a native binary.

Also, as a cutting-edge user, I would not want to run an update command in the package manager every single day. That's just stupid.
[Kanzaki Ranko]

thelittlestgirl wrote:

It's userland software just like anything else may need to install via package management. I don't really understand why being a game would be any different.
Care to mention any examples of a netgame that's updated through your package manager on Linux? Of course, the ones where basically everything is server-sided and the binary itself doesn't update in years doesn't count.

It's just too big of a hassle for something that has to be up-to-date to actually work.
peppy
mono is still native. unless you're about to suggest that .net on windows isn't native.

we will either be targetting mono or .net core. i have no intention on using package managers; users would install by downloading an installer as per basically everything else (see steam).

the binaries would be installed local to your user, not at a system level (unless you make the concious decision to install otherwise), similar to windows and OS X versions.
Topic Starter
aismallard
Steam for Linux doesn't use an installer though, users install the steam package from their distro's repositories like any other piece of software.

I can't really think of a way of installing binaries locally, since even "/usr/local/bin" requires root permissions by default (i.e. it has mode 0755).

Distributing software on Linux is a bit different than the Windows method of "download and run installer exe", which is one of the motivations for making this post. This way we can discuss the best way to package osu! in a distro-effective way before we start working on the code.
Raymonf

peppy wrote:

mono is still native. unless you're about to suggest that .net on windows isn't native.
I meant native as in something like sudo. That is, a native Linux binary.

aismallard wrote:

Steam for Linux doesn't use an installer though, users install the steam package from their distro's repositories like any other piece of software.

I can't really think of a way of installing binaries locally, since even "/usr/local/bin" requires root permissions by default (i.e. it has mode 0755).

Distributing software on Linux is a bit different than the Windows method of "download and run installer exe", which is one of the motivations for making this post. This way we can discuss the best way to package osu! in a distro-effective way before we start working on the code.
Doesn't Steam for Linux do its own updating after installing from the package manager?
peppy
The user would install to a folder local to their user directory in the case of osu!. If you want a package for your particular package manager you can make an unofficial one for your own use, but the change of there being an official one is very low.

The official osu! releases will never be "native" in your terms, because osu! is built on .NET framework.
Topic Starter
aismallard

Raymonf wrote:

I meant native as in something like sudo. That is, a native Linux binary.

peppy wrote:

The official osu! releases will never be "native" in your terms, because osu! is built on .NET framework.

I'm not really sure how to define "native", but at least with Mono, compiled artifacts can be run like any other executable.

Raymonf wrote:

Doesn't Steam for Linux do its own updating after installing from the package manager?

Yes, but Steam is pretty weird when it comes to packaging. It packs its own binaries rather than using dependencies and has its own updater, but even it uses a package rather than an install script.
peppy
Yes, this is possible but we will likely not compile in such a way in order to maintain cross-compatibility. You will need whatever framework we choose to use to actually run it.
Please sign in to reply.

New reply