Skip to content

Commit 08edd1d

Browse files
committed
release: prepare 1.3.2
1 parent 213147a commit 08edd1d

7 files changed

Lines changed: 165 additions & 69 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
## 1.3.2 - 2026-06-18
4+
5+
Maintenance release for release-build formatting and clearer game room documentation.
6+
7+
中文更新日志:
8+
9+
- **发布构建修复**: 格式化 UI 测试文件,修复 Release 工作流在 Windows 平台执行 `spotlessKotlinCheck` 时失败的问题。
10+
- **棋牌室教程强化**: README 和中文 wiki 新增“先创建棋牌室,再创建牌桌”的开服推荐流程,补充魔棒选区、青色粒子范围确认、主厅创建和快速测试示例。
11+
12+
English Release Notes:
13+
14+
- **Release build fix**: Formatted UI test sources so the Windows Release build no longer fails in `spotlessKotlinCheck`.
15+
- **Game room documentation**: README and the Chinese wiki now emphasize creating a game room before placing tables, with wand selection, particle-outline confirmation, main-hall setup, and quick-test examples.
16+
317
## 1.3.1 - 2026-06-18
418

519
Hotfix and usability release for CI reliability, command help navigation, and game room selection confirmation.

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,31 @@ The current branch already supports playable Riichi Mahjong, GB Mahjong, and Sic
3131
- round history persistence through H2 by default, with optional MariaDB/MySQL
3232
- game room system: spatial containers for tables, with optional table creation restriction, enter/exit messages, and leave countdown for active matches
3333

34+
## Recommended Setup: Create a Game Room First
35+
36+
For normal server operation, create at least one game room before opening public tables. A game room is the allowed play area for Mahjong tables; when `gameRooms.restrictNewTables` is enabled, `/mahjong create` only works while the admin is standing inside a game room.
37+
38+
Quick tutorial:
39+
40+
1. Give yourself admin permission: `mahjongpaper.admin`.
41+
2. Run `/mahjong room wand` to get the selection wand.
42+
3. Left-click one corner of the room, then right-click the opposite corner.
43+
4. Check the cyan particle outline and make sure the entire play area is inside the cuboid.
44+
5. Run `/mahjong room create main-hall Main Hall`.
45+
6. Stand inside that room and run `/mahjong create` to place a Mahjong table.
46+
7. Use `/mahjong room list` and `/mahjong room info main-hall` to confirm the saved room.
47+
48+
If you only need a quick test room, stand at the room center and run `/mahjong room create quick-room`; without a wand selection, the plugin uses `gameRooms.defaultRadius` and `gameRooms.defaultHeight`.
49+
50+
For the longer operations guide, see [Game room system](./docs/wiki.zh-CN.md#棋牌室系统) in the Chinese wiki.
51+
3452
## Command Summary
3553

3654
- `/mahjong help`: show in-game command help
55+
- `/mahjong room wand`: get the game-room selection wand
56+
- `/mahjong room create <id> [name]`: create a game room from the current wand selection, or around your current position
57+
- `/mahjong room list`: list saved game rooms
58+
- `/mahjong room info <id>`: show a game room's world, bounds, size, and owner
3759
- `/mahjong create`: create a new empty table at your location
3860
- `/mahjong botmatch [MAJSOUL_HANCHAN|MAJSOUL_TONPUU|GB|SICHUAN]`: create a 4-bot test match and spectate it
3961
- `/mahjong mode <MAJSOUL_TONPUU|MAJSOUL_HANCHAN|GB|SICHUAN>`: apply a preset before the next start
@@ -67,6 +89,7 @@ Admin targeting details:
6789

6890
## Lobby Flow
6991

92+
- On production servers, create the game room first, then stand inside it before running `/mahjong create`.
7093
- `/mahjong create` creates an empty table only; it does not auto-seat the creator
7194
- the creator becomes the table owner and can manage rules, bots, refresh, start, and delete from `/mahjong table`
7295
- the table owner/admin can transfer ownership with `/mahjong table owner <player> [tableId]`
@@ -96,7 +119,7 @@ The plugin ships three independent rulesets. Switch between them before a hand s
96119

97120
Every mode uses the same play loop:
98121

99-
1. `/mahjong create`: create an empty table at your feet.
122+
1. Create a game room first on production servers; then stand inside it and run `/mahjong create` to create an empty table.
100123
2. Click one of the east/south/west/north floating seat labels to sit down, or use `/mahjong join <tableId>`.
101124
3. Fill empty seats with `/mahjong addbot` (bots count as ready).
102125
4. `/mahjong start` toggles ready; the hand auto-starts once all 4 seats are filled and ready.

README.zh-CN.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,31 @@
2929
- 默认使用 H2 持久化对局历史和段位,可选 MariaDB/MySQL
3030
- 棋牌室系统:牌桌的空间容器,支持牌桌创建限制、进出提醒和对局中离开倒计时
3131

32+
## 推荐开服流程:先创建棋牌室
33+
34+
正式开服时,建议先创建至少一个棋牌室,再在棋牌室内创建牌桌。棋牌室是牌桌的允许摆放区域;当 `gameRooms.restrictNewTables` 开启时,管理员只有站在棋牌室内才能使用 `/mahjong create` 创建新牌桌。
35+
36+
快速教程:
37+
38+
1. 确认你拥有管理员权限:`mahjongpaper.admin`
39+
2. 执行 `/mahjong room wand` 获取棋牌室选区魔棒。
40+
3. 左键点击房间的一个角,右键点击对角线另一角。
41+
4. 观察青色粒子边框,确认整个游玩区域都被框住。
42+
5. 执行 `/mahjong room create main-hall 主厅` 创建棋牌室。
43+
6. 站在这个棋牌室内,执行 `/mahjong create` 创建牌桌。
44+
7.`/mahjong room list``/mahjong room info main-hall` 检查保存结果。
45+
46+
如果只是快速测试,也可以站在房间中心直接执行 `/mahjong room create quick-room`;没有魔棒选区时,插件会按 `gameRooms.defaultRadius``gameRooms.defaultHeight` 自动生成一个区域。
47+
48+
更完整的操作说明见中文 wiki 的 [棋牌室系统](./docs/wiki.zh-CN.md#棋牌室系统)
49+
3250
## 指令概览
3351

3452
- `/mahjong help`:显示游戏内帮助
53+
- `/mahjong room wand`:获取棋牌室选区魔棒
54+
- `/mahjong room create <id> [名称]`:用当前选区创建棋牌室;没有选区时以当前位置为中心创建
55+
- `/mahjong room list`:列出已保存的棋牌室
56+
- `/mahjong room info <id>`:查看棋牌室世界、边界、大小和所有者
3557
- `/mahjong create`:在当前位置创建一个空牌桌
3658
- `/mahjong botmatch [MAJSOUL_HANCHAN|MAJSOUL_TONPUU|GB|SICHUAN]`:创建一桌 4 Bot 测试对局并进入观战
3759
- `/mahjong mode <MAJSOUL_TONPUU|MAJSOUL_HANCHAN|GB|SICHUAN>`:桌主/管理员在下一局开始前应用预设规则;默认玩法以 `MAJSOUL_HANCHAN` 为主
@@ -62,6 +84,7 @@
6284

6385
## 大厅与准备流程
6486

87+
- 正式服建议先建棋牌室,再站在棋牌室内使用 `/mahjong create` 创建牌桌。
6588
- `/mahjong create` 只会创建空桌,不会自动把创建者加入牌局
6689
- 创建者会成为桌主,可通过 `/mahjong table` 管理规则、Bot、刷新、开局和删除
6790
- 桌主/管理员可用 `/mahjong table owner <玩家名> [tableId]` 把桌主转让给已经入座的在线玩家
@@ -86,7 +109,7 @@
86109

87110
无论哪种模式,一局的操作流程都一样:
88111

89-
1. `/mahjong create`:在你脚下创建一张空牌桌
112+
1. 正式服先创建棋牌室;然后站在棋牌室内执行 `/mahjong create` 创建一张空牌桌
90113
2. 走到东/南/西/北四个悬浮座位牌前点击入座,或用 `/mahjong join <牌桌ID>`
91114
3. 缺人时由桌主/管理员用 `/mahjong table``/mahjong addbot` 补 Bot(Bot 默认视为已准备)。
92115
4. `/mahjong start` 切换准备状态;4 个座位都坐满且全部准备后自动开局。

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ plugins {
1212
}
1313

1414
group = "top.ellan"
15-
version = "1.3.1"
15+
version = "1.3.2"
1616

1717
val minimumPaperDevBundleVersion = "1.20.1-R0.1-SNAPSHOT"
1818
val paperDevBundleVersion =

docs/wiki.zh-CN.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@
1717
| 配置文件 | `plugins/MahjongPaper/config.yml` |
1818
| CraftEngine 导出目录 | `plugins/CraftEngine/resources/mahjongpaper` |
1919

20+
## 开服推荐路径:先建棋牌室
21+
22+
正式服建议把“创建棋牌室”作为开桌前的第一步。棋牌室是牌桌的空间容器,用来规定牌桌能放在哪里,并提供进出提醒和对局中离开倒计时。默认配置下,`gameRooms.enabled: true``gameRooms.restrictNewTables: true`,因此管理员需要站在棋牌室内才能创建新牌桌。
23+
24+
推荐流程:
25+
26+
1. 确认管理员拥有 `mahjongpaper.admin`
27+
2. 使用 `/mahjong room wand` 获取选区魔棒。
28+
3. 左键点击房间一个角,右键点击对角。
29+
4. 观察青色粒子边框,确认棋牌室范围覆盖整个游玩区域。
30+
5. 使用 `/mahjong room create main-hall 主厅` 保存棋牌室。
31+
6. 站在棋牌室内执行 `/mahjong create` 创建牌桌。
32+
7.`/mahjong room info main-hall` 检查世界、坐标和大小。
33+
34+
这个流程适合大厅、包间、活动区等固定场地。临时测试时,可以不使用魔棒,直接站在中心点执行 `/mahjong room create test-room`,插件会按 `gameRooms.defaultRadius``gameRooms.defaultHeight` 自动生成一个区域。
35+
2036
## 安装与首次启动
2137

2238
1. 准备 Paper 或 Folia 服务端,并确保运行环境是 Java 21。
@@ -30,7 +46,7 @@
3046

3147
一张牌桌从创建到结束大致是这个流程:
3248

33-
1. 管理员使用 `/mahjong create` 在当前位置创建空牌桌
49+
1. 管理员先创建棋牌室;如果启用了创建限制,则站在棋牌室内使用 `/mahjong create` 创建空牌桌
3450
2. 玩家点击东、南、西、北四个座位悬浮标签入座,也可以使用 `/mahjong join <table_id>` 加入。
3551
3. 开局前可使用 `/mahjong mode <mode>` 切换玩法,或用 `/mahjong rule <key> <value>` 调整部分规则。
3652
4. 缺人时可用 `/mahjong addbot` 补 Bot。Bot 默认视为已准备。
@@ -208,7 +224,7 @@
208224

209225
| 命令 | 用途 |
210226
| --- | --- |
211-
| `/mahjong create` | 在当前位置创建牌桌 |
227+
| `/mahjong create` | 在当前位置创建牌桌;开启棋牌室限制时必须站在棋牌室内 |
212228
| `/mahjong botmatch [hanchan|tonpuu]` | 创建 4 Bot 立直测试桌并进入观战 |
213229
| `/mahjong render` | 重新渲染当前牌桌 |
214230
| `/mahjong inspect` | 显示渲染锚点和方向诊断 |
@@ -314,7 +330,7 @@ MahjongPaper 使用 CraftEngine 处理以下内容:
314330
| `/mahjong leaderboard [mode]` | 玩家 | 查看分模式排行榜 |
315331
| `/mahjong addbot` | 桌主/管理员 | 开局前添加 Bot |
316332
| `/mahjong removebot` | 桌主/管理员 | 开局前移除 Bot |
317-
| `/mahjong create` | 管理员 | 创建牌桌 |
333+
| `/mahjong create` | 管理员 | 创建牌桌;开启棋牌室限制时必须站在棋牌室内 |
318334
| `/mahjong botmatch [MAJSOUL_HANCHAN|MAJSOUL_TONPUU|GB|SICHUAN]` | 管理员 | 创建 4 Bot 测试桌 |
319335
| `/mahjong list` | 管理员 | 列出活动牌桌 |
320336
| `/mahjong render` | 管理员 | 强制刷新牌桌展示 |
@@ -362,7 +378,7 @@ MahjongPaper 使用 CraftEngine 处理以下内容:
362378

363379
### 创建棋牌室
364380

365-
有两种方式创建棋牌室:使用游戏内魔棒工具(推荐)或手动编辑配置文件。
381+
有两种方式创建棋牌室:使用游戏内魔棒工具(推荐)或手动编辑配置文件。推荐优先使用魔棒,因为它能即时显示粒子边框,方便确认区域是否覆盖完整。
366382

367383
#### 方式一:魔棒工具(推荐)
368384

@@ -383,7 +399,14 @@ MahjongPaper 使用 CraftEngine 处理以下内容:
383399
| 左键点击方块 | 设置选区第一点(最小角) |
384400
| 右键点击方块 | 设置选区第二点(最大角) |
385401

386-
两点确定一个长方体(AABB)区域。设置成功后会收到提示消息,显示当前选区坐标。
402+
两点确定一个长方体(AABB)区域。设置成功后会收到提示消息,显示当前选区坐标;同时会出现青色粒子边框,用来确认棋牌室的实际范围。粒子边框只会显示给当前管理员,不会影响其他玩家。
403+
404+
选区建议:
405+
406+
- 第一、第二点可以任意先后点击,插件会自动计算最小/最大坐标。
407+
- 选区高度要覆盖玩家活动高度和牌桌展示高度,不要只框地面一层。
408+
- 如果棋牌室是大厅,建议比墙体内侧略大一圈,避免牌桌中心点贴边时被判定在区域外。
409+
- 如果右键后发现粒子边框不对,重新左键/右键选择即可,旧预览会被刷新。
387410

388411
**创建棋牌室**
389412

@@ -398,13 +421,23 @@ MahjongPaper 使用 CraftEngine 处理以下内容:
398421

399422
如果已有选区,会使用选区坐标创建;如果没有选区,则以玩家当前位置为中心,按配置中的 `defaultRadius``defaultHeight` 创建。
400423

401-
**示例**
424+
**完整示例:创建一个主厅棋牌室**
402425

403426
```
404427
/mahjong room wand # 获取魔棒
405-
# 左键点击房间一个角,右键点击对角
428+
# 左键点击主厅地面一角
429+
# 右键点击主厅对角的上方或地面方块
430+
# 确认青色粒子边框覆盖整个主厅
406431
/mahjong room create main-hall 主厅 # 用选区创建棋牌室
432+
/mahjong room info main-hall # 检查保存后的范围
433+
/mahjong create # 站在主厅内创建牌桌
434+
```
435+
436+
**快速测试示例**
437+
438+
```
407439
/mahjong room create quick-room # 无选区时以玩家位置为中心创建
440+
/mahjong create # 在 quick-room 内创建牌桌
408441
```
409442

410443
**其他棋牌室命令**

src/test/kotlin/top/ellan/mahjong/ui/SettlementUiIntegrationTest.kt

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package top.ellan.mahjong.ui
22

3-
import top.ellan.mahjong.table.core.TableRuntimeServices
3+
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
4+
import org.mockito.Mockito.mock
5+
import org.mockito.Mockito.`when`
46
import top.ellan.mahjong.compat.CraftEngineService
57
import top.ellan.mahjong.i18n.MessageService
8+
import top.ellan.mahjong.model.MahjongVariant
69
import top.ellan.mahjong.riichi.RoundResolution
710
import top.ellan.mahjong.riichi.model.DoubleYakuman
811
import top.ellan.mahjong.riichi.model.ExhaustiveDraw
@@ -13,10 +16,7 @@ import top.ellan.mahjong.riichi.model.SettlementPayment
1316
import top.ellan.mahjong.riichi.model.SettlementPaymentType
1417
import top.ellan.mahjong.riichi.model.YakuSettlement
1518
import top.ellan.mahjong.table.core.MahjongTableSession
16-
import top.ellan.mahjong.model.MahjongVariant
17-
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
18-
import org.mockito.Mockito.mock
19-
import org.mockito.Mockito.`when`
19+
import top.ellan.mahjong.table.core.TableRuntimeServices
2020
import java.util.Locale
2121
import java.util.UUID
2222
import kotlin.test.Test
@@ -28,35 +28,38 @@ class SettlementUiIntegrationTest {
2828
@Test
2929
fun `summary lore includes round dealer draw type and settled players`() {
3030
val session = mockSession()
31-
val resolution = RoundResolution(
32-
title = "Ron",
33-
yakuSettlements = listOf(
34-
YakuSettlement(
35-
displayName = "Alice",
36-
uuid = UUID.randomUUID().toString(),
37-
yakuList = listOf("reach"),
38-
yakumanList = emptyList(),
39-
doubleYakumanList = emptyList(),
40-
riichi = true,
41-
winningTile = MahjongTile.M1,
42-
hands = listOf(MahjongTile.M1, MahjongTile.M2, MahjongTile.M3),
43-
fuuroList = emptyList(),
44-
doraIndicators = emptyList(),
45-
uraDoraIndicators = emptyList(),
46-
fu = 30,
47-
han = 1,
48-
score = 1000
49-
)
50-
),
51-
scoreSettlement = ScoreSettlement(
52-
"Ron",
53-
listOf(
54-
ScoreItem("Alice", UUID.randomUUID().toString(), 25000, 12000),
55-
ScoreItem("Bob", UUID.randomUUID().toString(), 25000, -12000)
56-
)
57-
),
58-
draw = ExhaustiveDraw.NORMAL
59-
)
31+
val resolution =
32+
RoundResolution(
33+
title = "Ron",
34+
yakuSettlements =
35+
listOf(
36+
YakuSettlement(
37+
displayName = "Alice",
38+
uuid = UUID.randomUUID().toString(),
39+
yakuList = listOf("reach"),
40+
yakumanList = emptyList(),
41+
doubleYakumanList = emptyList(),
42+
riichi = true,
43+
winningTile = MahjongTile.M1,
44+
hands = listOf(MahjongTile.M1, MahjongTile.M2, MahjongTile.M3),
45+
fuuroList = emptyList(),
46+
doraIndicators = emptyList(),
47+
uraDoraIndicators = emptyList(),
48+
fu = 30,
49+
han = 1,
50+
score = 1000,
51+
),
52+
),
53+
scoreSettlement =
54+
ScoreSettlement(
55+
"Ron",
56+
listOf(
57+
ScoreItem("Alice", UUID.randomUUID().toString(), 25000, 12000),
58+
ScoreItem("Bob", UUID.randomUUID().toString(), 25000, -12000),
59+
),
60+
),
61+
draw = ExhaustiveDraw.NORMAL,
62+
)
6063

6164
val rendered = SettlementUi.summaryLore(session, resolution, Locale.ENGLISH).map(plain::serialize)
6265

@@ -71,29 +74,31 @@ class SettlementUiIntegrationTest {
7174
val session = mockSession()
7275
val ronPayer = UUID.randomUUID().toString()
7376
val paoPayer = UUID.randomUUID().toString()
74-
val settlement = YakuSettlement(
75-
displayName = "Alice",
76-
uuid = UUID.randomUUID().toString(),
77-
yakuList = listOf("reach"),
78-
yakumanList = listOf("kokushimuso"),
79-
doubleYakumanList = listOf(DoubleYakuman.SUANKO_TANKI),
80-
riichi = true,
81-
winningTile = MahjongTile.M1,
82-
hands = listOf(MahjongTile.M1, MahjongTile.M2, MahjongTile.M3),
83-
fuuroList = listOf(true to listOf(MahjongTile.P1, MahjongTile.P2, MahjongTile.P3)),
84-
doraIndicators = listOf(MahjongTile.S1),
85-
uraDoraIndicators = listOf(MahjongTile.S2),
86-
fu = 40,
87-
han = 3,
88-
score = 7700,
89-
paymentBreakdown = listOf(
90-
SettlementPayment(ronPayer, 3900, SettlementPaymentType.RON),
91-
SettlementPayment(paoPayer, 3900, SettlementPaymentType.PAO, "DAISANGEN"),
92-
SettlementPayment("", 1000, SettlementPaymentType.RIICHI_POOL)
93-
),
94-
redFiveCount = 1,
95-
nagashiMangan = true
96-
)
77+
val settlement =
78+
YakuSettlement(
79+
displayName = "Alice",
80+
uuid = UUID.randomUUID().toString(),
81+
yakuList = listOf("reach"),
82+
yakumanList = listOf("kokushimuso"),
83+
doubleYakumanList = listOf(DoubleYakuman.SUANKO_TANKI),
84+
riichi = true,
85+
winningTile = MahjongTile.M1,
86+
hands = listOf(MahjongTile.M1, MahjongTile.M2, MahjongTile.M3),
87+
fuuroList = listOf(true to listOf(MahjongTile.P1, MahjongTile.P2, MahjongTile.P3)),
88+
doraIndicators = listOf(MahjongTile.S1),
89+
uraDoraIndicators = listOf(MahjongTile.S2),
90+
fu = 40,
91+
han = 3,
92+
score = 7700,
93+
paymentBreakdown =
94+
listOf(
95+
SettlementPayment(ronPayer, 3900, SettlementPaymentType.RON),
96+
SettlementPayment(paoPayer, 3900, SettlementPaymentType.PAO, "DAISANGEN"),
97+
SettlementPayment("", 1000, SettlementPaymentType.RIICHI_POOL),
98+
),
99+
redFiveCount = 1,
100+
nagashiMangan = true,
101+
)
97102

98103
val rendered = SettlementUi.settlementLore(Locale.ENGLISH, session, settlement).map(plain::serialize)
99104

@@ -122,4 +127,3 @@ class SettlementUiIntegrationTest {
122127
return session
123128
}
124129
}
125-

src/test/kotlin/top/ellan/mahjong/ui/StaticUiSafetyTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import org.mockito.Mockito.lenient
77
import org.mockito.Mockito.mock
88
import top.ellan.mahjong.table.core.MahjongTableSession
99
import java.util.UUID
10-
import kotlin.test.assertEquals
1110
import kotlin.test.assertFalse
1211

1312
/**

0 commit comments

Comments
 (0)