MahjongMC — Real Japanese Riichi Mahjong, Right in Your Lobby
Why MahjongMC?
Most "mahjong" plugins are tile-matching solitaire. This is the real thing — authentic 4-player Japanese Riichi Mahjong with a proper shanten/scoring engine, yaku, dora, riichi sticks, and honest settlement. It runs as a drop-in lobby minigame: spawn a table anywhere, players right-click to sit, and any empty seats are filled by genuinely smart AI bots so a game always starts.
Built for server owners who care about polish: deeply configurable, fully translatable, database-ready, and zero residue when you remove it.
Core Features
Authentic Riichi Mahjong — real shanten calculation, yaku & dora scoring, riichi, furiten, and proper end-of-hand settlement. Powered by a battle-tested scoring engine, not hand-rolled guesswork.
Sit-and-play tables — admins place a table with one command; players join by right-clicking a seat. No commands for players to memorize.
4 levels of bot AI — empty seats fill instantly with bots ranging from clueless beginner to tournament-grade expert. A game never fails to start.
Beautiful in-world rendering — tables, tiles, melds, dora indicators, riichi sticks and scoreboards are drawn with display entities. Clean, modern, and readable.
Built for an international audience — full i18n with English, Japanese, Simplified & Traditional Chinese included, and optional per-player locale.
Flexible economy — bet tables backed by Vault, a built-in points system, or turn economy off entirely for casual free play.
Real database support — store player stats in YAML, SQLite, MySQL, or MongoDB. Pick what fits your network.
PlaceholderAPI ready — surface wins, win-rate, balance and live table counts anywhere.
Zero-residue & hot-reloadable — /mahjong reload applies config & language changes live; uninstalling leaves no leftover entities in your world.
Bots That Actually Play Mahjong
Empty seats are never a problem. MahjongMC ships four distinct difficulty tiers you choose per table — and the hard ones are hard:
| Difficulty | Playstyle |
|---|---|
| Easy | Plays like a true beginner — discards isolated tiles, never defends, doesn't call. Great for new players. |
| Normal | A solid casual player — efficient discards, plays safe tiles against riichi, calls for value. |
| Hard | Experienced — efficiency and hand value, defends using suji & walls, pushes or folds by hand worth and shanten. |
| Expert | Tournament-grade — full expected-value push/fold (reads opponents' likely hand value × deal-in risk vs. your own value × win rate), danger-ordered discards, reads the discard pond to fold pre-emptively, plays for safety when ahead and for points when behind. |
Game Modes
Pick how long a match runs when you create the table:
- Single — a single hand, quick and casual.
- East — East round only.
- Hanchan — full East + South, the classic competitive length.
Compatibility
| Minecraft | 1.21.4 → 26.1.2 (latest) |
| Server | Paper (and Paper forks) |
| Java | Java 25 required |
| Soft dependencies | Vault (optional), PlaceholderAPI (optional) |
Built to Be Customized
Customizability is the product. Everything below is config-driven, fully documented in config.yml, and applies live on /mahjong reload:
- Language — choose the server default, toggle per-player locale, and edit/add languages. Your edits are never overwritten on update; new keys are merged in automatically.
- Economy — vault, built-in points, or disabled.
- Storage — yaml, sqlite, mysql, or mongodb (drivers auto-downloaded; nothing to shade).
- Resource pack delivery:
- auto — the plugin runs a lightweight built-in HTTP server and serves the pack to players automatically (just set a port).
- manual — the plugin only exports resourcepack.zip to its data folder; you distribute or merge it yourself.
- Tables — default decision timer, betting countdown, "leave distance" (set -1 to disable the check), seated move speed, and sneak-to-overhead-view for an immersive top-down look at the board.
YAML:
# ═══════════════════════════════ MahjongMC ══════════════════════════════
# Lines starting with '#' are comments. After editing, run /mahjong reload
# (no restart needed). Every option's accepted values are listed above it.
# language: language for all messages and table displays.
# Accepted: en | zh_TW | zh_CN | ja (or any <lang>.yml in the lang/ folder)
language: en
# perPlayerLocale: also make CHAT follow each player's own Minecraft language.
# Accepted: true | false
# false (default) - everyone sees the single 'language' above (predictable)
# true - chat adapts per player's client; 'language' is the
# fallback. Use only on mixed-language servers.
perPlayerLocale: false
# ───────────────────────────────── Economy ─────────────────────────────
# Betting tables take a stake from each player on join and pay out at game end.
# Free tables (bet 0) never touch the economy.
# economy.mode: where balances come from. This is also the Vault on/off switch.
# Accepted: auto | vault | internal | none
# auto (default) - use Vault if an economy plugin is installed,
# otherwise use the built-in points system
# vault - require Vault (an economy plugin must be present;
# falls back to built-in points if none is found)
# internal - always use built-in points; ignore Vault even if present
# none - disable money entirely; every table becomes free
# economy.internal.startingBalance: balance a new player starts with.
# Accepted: any whole number >= 0
# economy.internal.symbol: currency unit shown next to amounts, e.g. "$", "pts".
economy:
mode: auto
internal:
startingBalance: 1000
symbol: pts
# ────────────────────────────────── Tables ─────────────────────────────
# table.defaultDecisionSeconds: seconds a human has to act before auto-discard.
# Overridable per table in /mahjong create. Accepted: whole number >= 1
# table.bettingCountdownSeconds: on a betting table, once the first player sits,
# wait this long for others, then start and fill empty seats with bots.
# (A table that reaches 4 players starts at once.) Accepted: whole number >= 1
# table.overheadViewOnSneak: let seated players hold sneak to see a top-down view.
# Accepted: true | false
# table.leaveDistance: how far (in blocks) a seated player may move from the table
# before being pulled back (in-game) or removed from the seat (before start).
# Accepted: a number > 0, or -1 to disable the distance check entirely
# table.seatedMoveSpeed: walk-speed multiplier for seated players (client-side only).
# Accepted: 0.1 - 1.0 (1.0 = normal speed, 0.5 = half)
table:
defaultDecisionSeconds: 20
bettingCountdownSeconds: 120
overheadViewOnSneak: true
leaveDistance: 10.0
seatedMoveSpeed: 0.5
# ─────────────────────────────── Resource pack ─────────────────────────
# Tile textures and sounds. A resourcepack.zip is ALWAYS written to this plugin's
# folder on startup, whichever mode you pick.
# resourcePack.mode: how the pack reaches players.
# Accepted: manual | auto
# manual (default) - only export resourcepack.zip; you host & apply it
# yourself (server.properties 'resource-pack', or merge
# it into your existing pack)
# auto - the plugin serves the pack from a built-in HTTP server
# and applies it to each player automatically on join
# resourcePack.port: [auto] port the HTTP server listens on (open/forward it).
# Accepted: a free port, 1-65535
# resourcePack.host: [auto] public address players download from (IP or domain).
# Leave empty to auto-detect; set it if players cannot download.
# resourcePack.required: [auto] true = players who decline the pack are kicked.
# Accepted: true | false
resourcePack:
mode: manual
port: 8765
host: ''
required: false
# ───────────────────────────────── Storage ─────────────────────────────
# Where player stats and built-in points are stored. (Table layouts and bet
# escrow always stay in local YAML - they are tied to this server's world.)
# DB drivers download automatically on first start when a database is selected.
# storage.type: storage backend.
# Accepted: yaml | sqlite | mysql | mongodb
# yaml (default) - flat files (data/stats.yml, data/economy.yml); zero setup
# sqlite - one local database file; good for larger single servers
# mysql - external MySQL/MariaDB; shareable across a network
# mongodb - external MongoDB; shareable across a network
# If a database fails to connect, the plugin falls back to yaml automatically.
# storage.sqlite.file: [sqlite] db file path, relative to this plugin folder.
# storage.mysql.*: [mysql] host / port / database / username / password.
# storage.mongodb.uri: [mongodb] standard MongoDB connection string.
# storage.mongodb.database: [mongodb] database name.
storage:
type: yaml
sqlite:
file: data/mahjong.db
mysql:
host: localhost
port: 3306
database: mahjong
username: root
password: ''
mongodb:
uri: mongodb://localhost:27017
database: mahjong
Commands & Permissions
All admin commands are under /mahjong and gated by
mahjongmc.admin.| Command | Description |
|---|---|
/mahjong create <bet> <type> <decisionSeconds> <difficulty> | Create a table where you stand (bet 0 = free table). |
/mahjong start <id> | Manually start a table. |
/mahjong remove <id> | Remove a table (bets refunded). |
/mahjong removeall | Remove every table (all bets refunded). |
/mahjong list | List all tables, their mode, bet, and bot difficulty. |
/mahjong reload | Reload config & languages live. |
PlaceholderAPI
| Placeholder | Returns |
|---|---|
%mahjongmc_tables% | Total tables |
%mahjongmc_active_tables% | Tables currently in a game |
%mahjongmc_players_playing% | Humans currently playing |
%mahjongmc_wins% | Player's total wins |
%mahjongmc_games% | Player's total games |
%mahjongmc_winrate% | Player's win rate (%) |
%mahjongmc_best_points% | Player's best score |
%mahjongmc_balance% | Player's economy balance |
%mahjongmc_in_game% | Is the player in a game? |
%mahjongmc_table% | Player's current table id (or -) |
%mahjongmc_seat% | Player's seat 1–4 (or -) |
Quick Start
- Drop MahjongMC.jar into /plugins (make sure you're on Java 25).
- Start the server — config.yml and language files generate automatically.
- Stand where you want a table and run
/mahjong create 0 HANCHAN 15 EXPERT. - Right-click a seat to sit. Empty seats fill with bots. Enjoy!
FAQ
Do players need to install anything?
No. The optional resource pack can be auto-delivered by the plugin or merged into your existing pack — players never download anything manually unless you choose manual mode.
Will it leave entities in my world if I remove the plugin?
No. All display entities are non-persistent and cleaned up on shutdown, and the plugin sweeps any orphans on startup. Removing the plugin leaves your world clean.
Do I have to use a database?
No — YAML works out of the box. SQLite/MySQL/MongoDB are there if you want them.
Is Vault required?
No. Use Vault, the built-in points system, or disable economy entirely.
Does it support older Minecraft versions?
The supported range is 1.21.4 → 26.1.2, all on a single jar. Java 25 is required.
🛟 Support
Discord: CLICK ME
If you enjoy MahjongMC, a
MahjongMC is an independent project and is not affiliated with any mahjong organization. "Minecraft" is a trademark of Mojang AB.
