rustris 0.6.4

A guideline-compliant terminal Tetris with LAN multiplayer support
rustris-0.6.4 is not a library.

Rustris

Build Crates.io Release License Rust

A guideline-compliant terminal Tetris with LAN multiplayer support.

Install

cargo install rustris

Or build from source:

git clone https://github.com/koishi510/Rustris.git
cd Rustris
cargo run --release

Requires Rust 2024 edition (1.85+). On Linux, libasound2-dev (or equivalent) is needed for audio support.

Recommended Terminal Settings

The game renders blocks as ██ (two full-block characters). For correct appearance, the terminal should use:

  • Font: Monospace with full Unicode support (JetBrains Mono, Cascadia Code, Fira Code, etc.)
  • Line height: 1.0 (extra line spacing causes horizontal gaps between blocks)
  • Letter spacing: 0 (extra spacing breaks block and border alignment)
Terminal Where to check
Kitty modify_font cell_height 0px
Windows Terminal Settings > Profile > Appearance
iTerm2 Profiles > Text > Character/Line spacing: 100%

Game Modes

Mode Objective
Marathon Clear a target number of lines (default 150)
Sprint Clear lines (default 40) as fast as possible
Ultra Score as high as possible within a time limit (default 120s)
Endless Play with no goal until game over
Versus LAN 1v1 - send garbage lines to your opponent

Versus Mode (LAN Multiplayer)

Play 1v1 over a local network. One player hosts, the other joins.

Quick Start

Start the game and select Versus mode from the menu. One player selects Host (enter a port), the other selects Join (enter <host-ip>:<port>). The host's LAN IP is displayed on the lobby screen.

Garbage System

Clearing lines sends garbage to your opponent:

Clear Type Attack
Single 0
Double 1
Triple 2
Tetris 4
T-Spin Single 2
T-Spin Double 4
T-Spin Triple 6
T-Spin Mini Single 0
T-Spin Mini Double 1
Back-to-Back +1
Perfect Clear 10

Combo bonus (added on top): 0-1 combo = +0, 2-3 = +1, 4-5 = +2, 6-7 = +3, 8-10 = +4, 11+ = +5.

Pending garbage is absorbed when you clear lines (cancel before send). Uncleared garbage is applied to your board on lock. A red bar between the two boards shows the amount of pending garbage.

Versus Rules

  • Level is fixed (no level-up during a match)
  • Game does not pause; Esc opens a non-blocking Forfeit menu (gravity and network continue)
  • No records are saved for Versus games

Menu Navigation

All menus use Up/Down to navigate, Enter to select, and Left/Right to change mode or toggle values.

Main Menu
├── < Mode >          ← Left/Right to switch (works on any item)
├── Start             → Start game
├── Settings          → Settings (mode-specific + audio)
├── Records           → Leaderboard (Left/Right to switch mode)
├── Help              → Controls reference
└── Quit              → Exit

Versus Menu
├── Host Game         → Port Input
│   ├── Confirm       → Host Lobby
│   ├── Back          → Versus Menu
│   └── Menu          → Main Menu
├── Join Game         → IP Input → Port Input
│   ├── Confirm       → Client Lobby
│   ├── Back          → Previous step (Port→IP, IP→Versus Menu)
│   └── Menu          → Main Menu
└── Back              → Main Menu

Host Lobby (waiting for connection)
├── Back              → Versus Menu
└── Menu              → Main Menu

Client Lobby (connection failed)
├── Retry             → Retry connection
├── Back              → Versus Menu
└── Menu              → Main Menu

Pause (single-player, Esc/P to open)
├── Resume            → Resume game (Esc also resumes)
├── Settings          → BGM/SFX toggles
├── Help              → Controls reference
├── Retry             → Restart game
└── Menu              → Main Menu

Game Over (single-player)
├── Retry             → Restart game
└── Menu              → Main Menu

Forfeit (versus, Esc to open, non-blocking)
├── Continue          → Resume (Esc also resumes)
├── Forfeit           → Lose and end match
├── BGM               ← Left/Right/Enter to toggle
└── SFX               ← Left/Right/Enter to toggle

Versus Result
├── Rematch           → Request rematch (waiting screen)
│   ├── Back          → Result screen
│   └── Menu          → Disconnect, Main Menu
└── Menu              → Disconnect, Main Menu

Controls

Key Action
Left / Right Move piece
Down Soft drop (+1 per cell)
Space Hard drop (+2 per cell)
Up / X Rotate clockwise
Z Rotate counter-clockwise
C Hold piece
Esc / P Pause (Forfeit in Versus)
Ctrl+C Force quit

Features

  • Super Rotation System (SRS) with full wall kick tables (toggleable)
  • 7-bag randomizer (or pure random)
  • Hold piece (toggleable)
  • Next queue preview (0-6 pieces, configurable)
  • Ghost piece (toggleable)
  • Lock delay (0-2s, configurable) with move/rotate reset (0-30 or unlimited)
  • DAS/ARR input handling
  • Line clear animation (toggleable)
  • Guideline scoring - T-Spin (Mini/Full), Back-to-Back, Combo, All Clear
  • Guideline gravity with level cap setting
  • BGM & SFX with polyphonic playback
  • Leaderboard - top 10 per mode, recorded only under default settings
  • LAN Versus - P2P TCP multiplayer with protocol handshake, garbage system, dual-board rendering, rematch support

Settings

Setting Modes Range Default Description
Level Marathon, Endless, Versus 1-20 1 Starting level
Goal Marathon 10-300 (step 10) 150 Lines to clear
Goal Sprint 10-100 (step 10) 40 Lines to clear
Time Ultra 30-300s (step 10) 120s Time limit
Cap Marathon, Endless 1-20 / INF 15 Maximum level
Next All 0-6 6 Next queue preview count
Lock All 0.0-2.0s (step 0.1) 0.5s Lock delay before piece locks
Reset All 0-30 / INF 15 Move reset limit during lock delay
Ghost All ON / OFF ON Ghost piece visibility
Anim All ON / OFF ON Line clear animation
Bag All ON / OFF ON 7-bag randomizer (OFF = pure random)
SRS All ON / OFF ON Super Rotation System with wall kicks
Hold All ON / OFF ON Hold piece
BGM All ON / OFF ON Background music
SFX All ON / OFF ON Sound effects

Troubleshooting

If the client gets TimedOut when joining, the host's firewall is likely blocking incoming TCP connections.

Linux

# firewalld
sudo firewall-cmd --add-port=21711/tcp
# ufw
sudo ufw allow 21711/tcp
# iptables
sudo iptables -I INPUT -p tcp --dport 21711 -j ACCEPT

Windows

Windows Firewall usually prompts automatically when hosting. If not:

netsh advfirewall firewall add rule name="Rustris" dir=in action=allow protocol=tcp localport=21711

Or go to Windows Defender Firewall > Advanced Settings > Inbound Rules > New Rule, select Port, enter TCP / 21711, and allow the connection.

macOS

macOS usually prompts automatically when hosting. If not:

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add "$(which rustris)"
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp "$(which rustris)"

Or go to System Settings > Network > Firewall > Options, click +, and add Rustris to the allowed list.

Replace 21711 with the actual port if you changed it.

Project Structure

src/
├── main.rs              Entry point, terminal init/cleanup
├── audio/
│   ├── mod.rs           Audio constants, module exports
│   ├── bgm.rs           BGM note/melody data, cycle assembly
│   ├── sfx.rs           Sfx enum, note sequences per sound effect
│   ├── synth.rs         Polyphonic synthesis (PolySource, SfxSource)
│   └── player.rs        MusicPlayer: BGM/SFX playback via rodio
├── game/
│   ├── mod.rs           Game struct definition
│   ├── board.rs         Construction, board queries, hold, ghost, timing
│   ├── movement.rs      Piece movement, rotation (SRS), gravity, drop
│   ├── scoring.rs       T-Spin detection, line clear, scoring
│   ├── animation.rs     Line clear animation, ARE, garbage rise animation
│   ├── types.rs         GameMode, LastMove, ClearAction, timing constants
│   ├── piece.rs         Piece/Bag structs, SRS data (rotation states, kick tables)
│   ├── settings.rs      Settings struct (shared by solo and versus)
│   ├── records.rs       Leaderboard persistence (JSON via serde)
│   ├── garbage.rs       Attack calculation, garbage queue, cancel logic
│   └── tests.rs         Unit tests (board, piece, garbage, scoring)
├── net/
│   ├── mod.rs           Network module exports
│   ├── protocol.rs      NetMessage enum, protocol version, BoardSnapshot, GarbageAttack
│   ├── transport.rs     Connection: frame encoding/decoding, non-blocking TCP I/O, timeout/length guard
│   ├── host.rs          LAN IP detection, TCP listener (non-blocking accept)
│   └── client.rs        TCP connect with timeout
├── render/
│   ├── mod.rs           Render module exports
│   ├── common.rs        Shared render utilities, title, piece preview
│   ├── board.rs         Single-player board rendering
│   ├── menus.rs         Menu/overlay rendering (pause, game over, settings, etc.)
│   └── versus.rs        Dual-board rendering, lobby/countdown/result screens
└── ui/
    ├── mod.rs           UI module exports
    ├── app.rs           Application loop, versus flow dispatch
    ├── input.rs         Key handling, DAS/ARR, gravity, lock delay, menu helpers
    ├── session.rs       Single-player game loop, pause, game over, records
    ├── versus.rs        Versus game loop, lobby, handshake, countdown, garbage, rematch
    └── menus/
        ├── mod.rs       Menu module exports
        ├── modes.rs     Mode select screen, records viewer
        ├── settings.rs  Settings menu (in-game and full)
        └── versus.rs    Versus Host/Join sub-menus with port/address input

Dependencies

  • crossterm - Terminal manipulation
  • rand - Bag shuffling and random generation
  • rodio - Audio playback
  • serde / serde_json - Record and network message serialization
  • dirs - Platform data directory resolution

License

GPL-3.0