IDOLA PSOBB Rust Server

Eidolon

Member
Hey all, I've been working heavily on a server implementation for Blue Burst in Rust, a relatively new systems language from Mozilla.

IDOLA PSOBB Server Emulator

It is licensed under AGPLv3 (a bit derivative of Sylverant code, though architecturally it's quite a bit different) and works on MacOSX, Linux, and Windows (maybe one of the BSDs as well?). A bug with the low-level IO library I am using causes socket freezing issues on Windows and currently it should only be used for testing.

The server is highly incomplete and under certain circumstances may panic and shutdown (Result values aren't propagated well across parts of the code where error conditions are an unreachable state), but it's very configurable. The current version only has Sqlite3 available as a database backend but the infrastructure allows for hooking in backends for any DB, including flat files (though I don't see a reason since Sqlite3 is much easier to work with).
  • Run any or all of the servers needed by PSOBB in a single process, across multiple processes on one machine, or even across multiple machines. Servers communicate with a central Shipgate server for all database interaction, and eventually a JSON API service will be available to interact with the shipgate as well.
  • Can configure ships with as many blocks as desired.
  • Fast, native, safely concurrent and (mostly) stable.
  • Very easy to compile, only a single non-Rust dependency to install (Sqlite3)
As of this writing, the server has the following implemented:
  • The BB gameplay loop -- patch, login, ship, block, lobby, party.
  • Characters are saved and loaded (incorrect default stats atm)
  • Exp gains for all episodes.
  • Central shipgate DB server.
The project is part of my attempt at a broader MMO server development and archival initiative.

To build the server on Windows, install 32-bit msys2 and inside a MingW-w64 Win32 shell, run "pacman -S mingw-w64-i686-sqlite3". Then, install the most recent Rust stable build for GNU ABI. Reopen the mingw shell, navigate to the codebase, and run "cargo build". This will download all Rust dependencies and compile them, and may take a few minutes. When it's run, the server will be at "target/debug/idola", it uses a command line interface so run it with --help and look at the "data/default/idola_local.toml" for configuration details.

To enable logging to stderr, set the RUST_LOG environment variable to "info" before running. (RUST_LOG=info target/debug/idola -c data/default/idola_local.toml) Eventually this will be replaced with a logback/log4j-like configuration file.

Use a sqlite3 database editor to create your accounts. The only fields in the "accounts" table you need to set are username and password_hash, the rest can be set to 0 and the server will generate your "bb_guildcard" row for you on connect. You need to sha256sum your password with the template "{username}:{password}:" (with the added colon).
 
Last edited:

Broomop

Member
Another crazy programming language. What file holds the packet filtering?? I couldn't even locate it.
 

Eidolon

Member
The message serialization is done mostly by hand and not directly memory mapped like one might do in C. It's not nearly as efficient as that, but it's close enough and lets me have a different internal representation of the message, for example all strings regardless of what encoding they end up in the stream, they're always UTF-8 in the server. It's also a bit hard to read and write. I tried to write hygienic macros to do it all automatically, but PSO's message structures aren't very consistent. Still a little better than writing every class by hand in something like Java or C#.

A single enum is generated from a macro that binds the message IDs to all the messages in a namespace and produces a Serial implementation for the whole enum. Rust enums are strongly typed and are closer to C unions. Internally they keep a tag that indicates what variant of the enum is stored. You can then pattern match and destructure them using match blocks or if lets. It's a very nice language feature.

That code is under the psomsg* subdirs. psoserial subdir provides the Serial trait which is implemented by mostly everything in psomsg* and psodata.

I think, for my next server project, I'll do it in something other than Rust because certain parts of the language and its ecosystem aren't really developed enough for this kind of work. The lack of typed integers and generics over them really hurts when dealing with straight arrays; you can't implement a trait for [T; A] of any array size A of type T. My PSU server will likely be written in Scala instead, if I get on that.
 
Last edited:

Wilhuf

Overanalyzer
Nice work! Apparently I actually "liked" this, forgot about it and then wrote a .qst tool in Rust while figuring everything out from several C programs (including Sylverant...). Nice duplication of effort. :)

Some questions and remarks from a quick glance at the code:
  • Why so many crates?
  • The nom library might simplify the serialization code, if that ship hasn't already sailed
    • Also, a slight performance hit is totally worth it if it gives you safe and stable code
  • When would you ever need to implement a trait for [T; A]?
  • In case you're wondering the BB cipher is Blowfish in ECB mode (but with the addition of some crazy Sega magic, I believe)
  • The code is very readable, kudos!
  • Any pointers to share with Rust newbies?
You're a very proficient programmer if you managed to do all this in a matter of months (I assume in addition to work/school), I'm impressed. I hope you'll regain interest in this project, I'm very interested.
 
Top