Skip to content

T00fy/gmg_midi

Repository files navigation

GMG: Event-Driven Game Architecture Demo

A demo of a scalable, event-driven architecture (Kafka + Redis + microservices in Rust/Java) originally built for a collaborative MIDI-puzzle game (“Guess My Groove”). The game proved unfun and hard to extend given the encoding implementation, so the focus here is on the architecture. (If I did it again today, I’d swap in SpaceTimeDB.)

Note on R2 You don’t actually need Cloudflare R2—there’s no "disable R2" flag, so you’ll have to run a localstack (or any S3-compatible) service to satisfy those settings. If I’d spent more time I would have just written snapshots to local files, but this lets you load huge .mid files.


Game Concept

  1. Load a MIDI file.
  2. Encode each MIDI event (Note On/Off, Tempo, etc.) as a digit sequence.
  3. Show each sequence as a “row” players can increment/decrement.
  4. When a row’s digits match the hidden sequence, reveal that event.
  5. When a group of rows completes, play back that snippet.
  6. Goal: reconstruct and hear the entire piece collaboratively.

Architecture

       +--------------+      +-----------------+
       | gmg_frontend |----->| gmg_backend     |----->---+
       | (WASM/Dioxus)| (WS) | (Rust/Axum)     |         |
       +--------------+      +-----------------+         | (Produce/Consume Kafka Events)
                                                         |   e.g., Init, Updates, Broadcasts
                                                         v
+-------------------------+                   +-----------------+                   +-----------------+
| gmg_stream              |<------------------| Kafka (Redpanda)|------------------>| gmg_processor   |----->---+
| (Java/Kafka Streams)    |                   | (Event Bus)     |                   | (Rust)          |         |
|                         |                   +-----------------+                   |                 |         | (Read/Write State)
| *Consumes* row-state    |                       ^         ^                       | *Consumes* Init,|         |
| *Produces* completions  |                       |         |                       | Updates, Complete|         |
+-------------------------+                       |---------+-----------------------| *Produces* Snap- |         |
  (Produce/Consume Kafka Events)                  (Produce/Consume Kafka Events)      | shots, Updates |         |
                                                                                      +-----------------+         |
                                                                                                                |
                                                                                                                v
                                                                                                           +-------+-------+
                                                                                                           | Redis         |
                                                                                                           | (Game State)  |
                                                                                                           +---------------+

Core Components

  • gmg_core (Rust lib): shared types, MIDI codec, Redis/Kafka helpers
  • gmg_backend (Rust/Axum): WebSocket → Kafka relay
  • gmg_processor (Rust): game logic, MIDI I/O, Redis state
  • gmg_frontend (Rust/Dioxus/WASM): UI + audio playback (rustysynth)
  • gmg_stream (Java/Kafka Streams): section-completion logic

Concepts Demonstrated

  • Event-driven (Kafka)
  • Microservices + loose coupling
  • CQRS‐style separation
  • Externalized state in Redis
  • Real-time WebSocket updates
  • Stateful stream processing (Kafka Streams)
  • Rust + WASM frontend
  • Docker-containerized stack
  • Pluggable JSON vs MessagePack serialization

Setup & Run (Docker)

Prereqs: Docker, Docker Compose, Git

  1. Clone

    git clone <repo> && cd <repo>
  2. Create .env in each service folder:

    • gmg_backend/.env

      GMG_BACKEND_KAFKA_BROKERS=redpanda:9093
      GMG_BACKEND_AUTH_PASSWORD=your_password
    • gmg_processor/.env

      GMG_PROCESSOR_REDIS_URL=redis://redis:6379
      GMG_PROCESSOR_KAFKA_BROKERS=redpanda:9093
      # R2 (optional you'll need localstack or S3):
      GMG_PROCESSOR_R2_ACCESS_KEY_ID=
      GMG_PROCESSOR_R2_ACCESS_KEY_SECRET=
      GMG_PROCESSOR_R2_ACCOUNT_ID=
      GMG_PROCESSOR_R2_BUCKET=your-bucket
    • gmg_frontend/.env

      GMG_FRONTEND_CONFIG_PATH=../config/frontend/dev.yaml
      GMG_FRONTEND_PORT=8080
    • gmg_stream/.env

      GMG_STREAM_KAFKA_BROKERS=redpanda:9093
  3. Start

    sudo ./run-docker.sh
    • Choose serialization (json/rmp)
    • Choose mode (full or infra)
    • Rebuild? (y/N)

    Or skip prompts with defaults (JSON + infra):

    sudo ./run-docker.sh --default
  4. Access http://localhost:8080

  5. Initialize

    curl -u any_user:your_password \
      -F "file=@config/resources/example.mid" \
      http://localhost:3000/initialize
  6. Play Refresh UI → click/right-click digits → watch sections unlock & play.

  7. Stop

    sudo docker compose down

Native Build

With Rust, Cargo, JDK 21+, Gradle installed:

./build.sh
# Ensure Redpanda & Redis are up (e.g. via run-docker.sh --default)
# Then adapt and run ./run.sh (uses tmux)

Configuration

  • Env vars (.env per service + Docker Compose).
  • Frontend YAML in config/frontend/*.yaml (selected via GMG_FRONTEND_CONFIG_PATH).
  • Serialization toggled at startup (json vs rmp).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors