Skip to content
neeerpPublic

About

An attempt at implementing the Maplestory Server End from scratch with Rust; now an exercise in vibecoding

Topics

Resources

Stars

38 stars

Watchers

2 watching

Forks

Latest commit

 

History

473 Commits

Folders and files

Repository files navigation

RustMS 🍁

RustMS is an attempt at implementing the Maplestory server end from scratch in Rust.

It's very work-in-progress and really only barely off the ground at this point, but it's been a fun evening/weekend side project so far!

Getting places

Update: 2026

This project was abandoned a few months after I initially started it, but I've revisited it now (2026/03) after feeling an itch to vibe code/explore Codex while I'm on vacation and away from work-work. The initial purpose of this project was to learn Rust, but it doesn't look like I'm doing a whole lot of that in this revisit; instead, I'm exploring how I can vibe code more sustainably (and also get a feel for ecosystems outside of Claude Code).

The first thing that came to me is that I need to build some test-rails to guide implementation, hence I "built" an integration harness and also moved the entire HeavenClient project into the repository so that I can do manual verification as well.

Motivation

The motivation behind this project is two-fold:

When I started this project, I knew next to no Rust, however I was quite interested in learning it due to a general interest that I've always had in lower level languages. I've previously learned a bit of C, however I've only really used it in an academic setting (as well as to make this relatively bland shell I made for fun). I thought Rust sounded interesting so I decided to start a fun project as a means of learning it. I'd also been trying to learn Vim for a while and thought caging myself in with it for this project (initially via the VS Code plugin and later neovim alone) would be fun.

The second motivation behind this project comes from the fact that Maplestory has a special place in my heart as the game that probably defined my childhood. I knew people had written and ran their own servers before but for the longest time it hadn't hit me that a lot of these servers had their source up on Github. Having looked at a few servers such as HeavenMS and Valhalla, I realized that I could probably try my hand at writing my own server too and that I could probably have quite a bit of fun with it.

Overview

Crates

The crypt crate provides the means for encrypting and decrypting packets using Maplestory's custom AES algorithm as well as its secondary encryption algorithm that's applied prior to AES. In particular, this library defines the MapleAES object that is used to encrypt/decrypt and keep track of the encryption nonce for a unidirectional stream of communication between the client and server. There is also a module here that provides an interface for encrypting and decrypting account passwords with bcrypt.

The packet crate provides a simple wrapper for our network packets, as well as an extension of the Read and Write IO traits that allow us to read and write various data types to our packets, including little endian integers of different sizes and length-headered strings.

The game crate holds the domain model: a logged-in Player with its stats, avatar look, inventory and equipment, skills, keymap, and quest log. It knows nothing about databases or packets.

The db crate persists that model in PostgreSQL through Diesel and a connection pool. db::player loads and saves whole players; migrations are embedded and applied by both servers at startup.

The game-data crate reads static data (maps, portals, NPCs, quests) from the v83 NX files in assets/game-data (see the README there).

The net crate holds the protocol: opcodes, an opcode registry that routes each packet to a login or world handler, the handlers themselves, packet builders, and shared encoders (net::codec) for the structures that recur across packets (character stats, avatar looks, items, inventories).

The runtime crate runs the servers as Tokio actors. The rust-ms crate has two binaries: login (port 8484) and world, which hosts every channel in one process with one port per channel (8485, 8486, 8487 by default). See agent_docs/server-architecture.md for how they fit together.

Functionality

RustMS has a login server and a multi-channel world server.

Encryption

Once a new client initiates a connection, the server responds with a handshake to exchange AES initialization vectors as well as some metadata about the server such as the client version that it expects. Any subsequent communication between the client and the server is encrypted with Maplestory's custom encryption (for lack of a better name) followed by Maplestory's custom AES encryption. Send and Receive data is encrypted using separate nonces (i.e. the initialization vectors and their subsequent mutations) that are both kept in sync between the client and server.

Account Creation/Login

Currently, one can create an account by attempting to log into an account that does not yet exist. The provided password is encrypted and the user/encrypted pw combination is saved in the database. The first login will prompt the user to accept a TOS as well as select their gender before forwarding them to the world select... these two features are currently hardcoded to trigger however in the future they'll be controlled by a configuration property.

Existing accounts can be logged into as expected, and a successful login will forward you to the world select screen as expected.

Currently, the account PIN prompt is bypassed and PINs are not yet supported. In the future we'll implement this too (likely whenever we establish our architecture for handling multiple servers and moving sessions between them... this will likely require changes in how the client session is modeled, which will have a direct impact on pin logins). We'll be putting this behind a configuration property as well.

World Select

Currently, one may select a world and one of its channels once logged in. There is one world with three channels, each served on its own port, and players can change channels in game.

Character Selection/Creation/Deletion

Currently, character creation is supported to an extent. One may create persistent, account linked characters that may be viewed on the character selection screen. These characters keep what was selected during character creation, including the starting clothes and weapon, which are stored as equipped items and shown to other players. Equipment stats are not loaded from item data yet.

Created characters also enforce uniqueness in their names, hence the check done at the end of the character creation flow is actually working. This check is case insensitive, so Maple and maple can't both exist.

Character deletion works and is instantaneous, so be careful (not as though your characters really matter at this stage of development anyway though).

PICs are currently disabled and will be implemented alongside PINs as mentioned previously. They too will be toggleable via configuration.

Game World

Currently, one is able to log into the game world with their selected character. At this point, the client is rerouted to the world server (one of the two binaries built) and loads in with the selected character.

There's not really a whole lot to do in game currently, as very little is actually implemented, however you can still walk around!

Maps

New characters initially spawn in the town of Amherst. From here, one may walk around and enter portals to adjacent maps. If the player logs out, they will reappear in the map they logged out from.

Portals are resolved from the map data, so players appear at the portal they came through, and other players see them there.

Keybinds

One may re-assign their keybinds as they normally would with the client. The selected keybinds actually get saved and will be available if the player logs out and back in later.

Database

RustMS is backed by a PostgreSQL server that can be set up using the docker-compose file in the root of the project. The servers apply the schema migrations in the db crate themselves on startup.

Demonstration

This demonstration is out-dated and needs to be updated! For now, see the above overview for an idea of the implemented features!

Running the server

If you would like to run RustMS, start the database, put the v83 NX files in assets/game-data (check them with tools/verify-assets.sh), and run the two servers from the root with cargo run -p rust-ms --bin login and cargo run -p rust-ms --bin world (make sure you have Rust and Cargo installed). You should see something akin to the following:

Run the server

On the first run of the server, you will likely have more output as dependencies will need to be downloaded and the project itself will build.

Running the client

The server on its own is not particularly interesting without a client to communicate with it. At this point in time, this server is being developed with the nested HeavenClient project in this repository, however in theory any V83 Maplestory client that's pointed at localhost should work (no promises). From the RustMS repo root, build the client with:

cd HeavenClient
./build-deps.sh
cmake -S . -B cmake-build
cmake --build cmake-build -j"$(nproc)"

Note that RustMS supports encryption and hence you shouldn't disable it when building the client.

Once you've built the client, run it: Run the client

In the server's console, you should see that the handshake completed successfully and the Login Start packet was received. Handshake Success

Logging in

At the time of writing this, you can type in a username and password in the client's login window and submit. The server will parse the packet and echo it in the console. Credentials Echoed

Running the Integration Harness

The integration test harness spins up a containerized postgres and pair of login/world servers.

On my machine, I run the harness as follows, using sg to assume the docker user group:

sg docker -c 'cargo run -p integration-harness --bin harnessctl -- test'

Roadmap

  • Develop a library for encrypting and decrypting packets in the form that the client expects
  • Develop a model for packets and packet IO
  • Develop basic infrastructure for sending and receiving packets to/from the client
  • Hook up a containerized database
  • Implement proper login and account creation flow
  • Implement character creation
  • Implement a parallel World Server
  • Login Sessions to transfer from Login to World Server
  • Save character keybinds from in game
  • In game portals to move between maps handled
  • Revise and update roadmap (in progress)

About

An attempt at implementing the Maplestory Server End from scratch with Rust; now an exercise in vibecoding

Topics

Resources

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages