Skip to content
Empo is pre-release. It is not on the App Store.
Empo
Esc
↑↓navigate↵open⌘Jpreview
On this page

How it works

The parts of Empo, how the SwiftUI app and the C++ engine share a process, and where to read more about each piece.

Empo is an app and a game engine in one process. The app is SwiftUI and UIKit. The app owns the window and the user interface. The engine owns the game.

Each engine is a game core. MkxpCore is a C++ fork of mkxp-z that runs RPG Maker XP, VX and VX Ace games in Ruby 1.8, 1.9, or 3.1. The four PSDK cores are LiteRGSS2 and LiteCGSS, each with one Ruby: 2.5, 3.0, 3.2, or 3.3. The version in the game’s Game.yarb picks the core. MvmzCore runs RPG Maker MV and MZ games in a web view. Read cores.md for the rules that keep the cores apart.

The repository

  • cores/ One folder for each core: the release pin, the bridge to the launcher interface, and the framework build.
  • ios/
    • Empo/ The app. SwiftUI, with UIKit for the touch controls.
    • Dependencies/ The downloaded release of each core, and ANGLE.
    • GameProbe/ A Swift package that reads game metadata. It builds on Linux, so CI can test it.
  • docs/ This documentation.

Three Ruby interpreters in the RPG Maker XP, VX and VX Ace core

Ruby 1.8, 1.9, and 3.1 compile separately. Each version merges with its binding code into one relocatable object file, and each object exports exactly one symbol. At import, Empo reads the scripts of the game and decides which interpreter it needs. At launch, the app sends a session config, and the engine dispatches to the correct one.

Ruby 3.1 also carries syntax-transform patches, so Pokemon Essentials forks that mix old grammar with a modern runtime still parse. The app turns the mode on for each game that needs it.

Read multi-ruby.md for the detection rules and the dispatch mechanism.

One session for each process

The app creates SDL, the GL context, OpenAL, and the Ruby interpreter one time, and it keeps them for the lifetime of the process. iOS does not let an app relaunch itself between games, and ruby_init() runs one time for each process.

This is why Empo plays one Ruby game for each session. An MV or MZ game runs in a web view, and the core removes the web view with all of its state, so any game can come after it. Read multi-session.md for the quit paths and the state that stays behind.

Touch controls are keyboard events

The overlay does not talk to the engine through a control API. It calls SDL_PushEvent with synthetic key events, so the engine reads them as it reads a hardware keyboard. A new button or a new layout needs no change in the engine.

Games can ship their own layout. Read Ship custom controls.

Windows APIs in Ruby

RPG Maker games call Win32 functions through Win32API. Two preload scripts, win32_wrap.rb and platform_compat.rb, stub out the calls that games expect. They also neutralize system, fork, and spawn, so a game cannot start a process.

Import writes containers, not database rows

Each game is a folder under Games/<title>/ in the Empo data folder. There is no database. The import pipeline extracts the source one time, validates each game root that it finds, and commits one container for each game that you select.

Read import-pipeline.md for the stages and the invariants.

Pause is a frozen frame

The SDL window and the SwiftUI view hierarchy cannot animate together. Empo takes a snapshot of the last frame, shows the snapshot, and animates that instead.

Read pause-resume.md for the two pause modes.

  • design-system.md in the repository: the visual rules the app follows, sheets included.
  • sdl-ruby-workarounds.md in the engine repository: why SDL, the GL context, OpenAL, and the Ruby VM are persistent.
  • patches-format.md in the engine repository: the patches.json script-patching system.
  • CONTRIBUTING.md: the build requirements and the steps to build the app.

Was this page helpful?