Skip to content

Commit 71d678d

Browse files
committed
release: prepare 1.4.1
- Fix game room exit countdown to also remove the leaving player from the table without moving them. - Remove italic from chest UI item names and style lore text as aqua non-italic.
1 parent 3f7e94e commit 71d678d

8 files changed

Lines changed: 81 additions & 19 deletions

File tree

CHANGELOG.md

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

3+
## 1.4.1 - 2026-06-18
4+
5+
Hotfix for game room exit countdown behavior.
6+
7+
中文更新日志:
8+
9+
- **棋牌室离开倒计时结束时踢出玩家**: 修复玩家离开棋牌室后倒计时结束仅强制结束对局、但未将该玩家从牌桌移除的问题。现在倒计时到期时会同时把离开房间的玩家从牌桌无位移移除。
10+
11+
English Release Notes:
12+
13+
- **Game room exit countdown now removes the player**: Fixed an issue where the countdown only force-ended the match when a player left the game room, but left the player attached to the table. The countdown expiry now also removes the leaving player from the table without moving them.
14+
315
## 1.4.0 - 2026-06-18
416

517
Visual asset release for the MahjongPaper table furniture, seat chair, and dice resources.

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.4.0"
15+
version = "1.4.1"
1616

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

src/main/java/top/ellan/mahjong/gameroom/GameRoomManager.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -334,23 +334,24 @@ public void tick() {
334334
long deadline = entry.getValue();
335335

336336
if (now >= deadline) {
337-
// Countdown expired, force end the match
337+
// Countdown expired, force end the match and remove the player
338+
// who left the room from the table without moving them.
338339
String tableId = this.exitCountdownTableIds.get(playerId);
339340
if (tableId != null) {
340341
this.logDebug("Player " + playerId + " countdown expired, force-ending table " + tableId);
341342
// Schedule force-end on the table's region thread to avoid
342343
// cross-thread mutation of game state (round controller,
343344
// viewer presentation, bot task, etc.) on Folia.
344345
MahjongTableSession session = this.tableManager.resolveTableById(tableId);
345-
// Guard against redundant forceEnd scheduling: if the match has
346-
// already ended (e.g. natural round end between the countdown
347-
// expiry and this tick) skip the region scheduling entirely.
348-
// forceEndMatch is idempotent but skipping it saves a region
349-
// task dispatch + an unnecessary render pass on the table's
350-
// region thread. isStarted() reads the now-volatile
351-
// roundController field (T1) so this cross-thread read is safe.
352-
if (session != null && session.isStarted()) {
353-
this.scheduler.runRegion(session.center(), () -> this.tableManager.forceEndTable(tableId));
346+
// Guard against redundant scheduling: if the match has already
347+
// ended and the player is no longer at the table, skip the
348+
// region task dispatch. Otherwise force-end the match and
349+
// remove the leaving player from the table (without teleport).
350+
if (session != null && (session.isStarted() || session.contains(playerId))) {
351+
this.scheduler.runRegion(session.center(), () -> {
352+
this.tableManager.forceEndTable(tableId);
353+
this.tableManager.removePlayerFromTableWithoutMove(playerId);
354+
});
354355
}
355356
}
356357
iterator.remove();

src/main/java/top/ellan/mahjong/table/core/MahjongTableManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ public MahjongTableSession forceEndTable(String tableId) {
337337
return session;
338338
}
339339

340+
public void removePlayerFromTableWithoutMove(UUID playerId) {
341+
this.membershipCoordinator.removePlayerFromTableWithoutMove(playerId);
342+
}
343+
340344
public MahjongTableSession deleteTable(String tableId) {
341345
MahjongTableSession session = this.resolveTable(tableId);
342346
if (session == null) {

src/main/java/top/ellan/mahjong/table/core/TableMembershipCoordinator.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,42 @@ MahjongTableSession unspectate(UUID playerId) {
128128
return session;
129129
}
130130

131+
void removePlayerWithoutMove(UUID playerId, MahjongTableSession session) {
132+
if (playerId == null || session == null) {
133+
return;
134+
}
135+
SeatWind seatWind = session.seatOf(playerId);
136+
if (seatWind != null) {
137+
this.manager.seatCoordinatorRef().ejectSeatOccupant(playerId);
138+
if (!session.removePlayer(playerId)) {
139+
return;
140+
}
141+
this.manager.directoryRef().removePlayer(playerId);
142+
} else if (session.isSpectator(playerId)) {
143+
session.removeSpectator(playerId);
144+
this.manager.directoryRef().removeSpectator(playerId);
145+
} else {
146+
return;
147+
}
148+
this.manager.pluginRef().debug().log("table", "Player " + playerId + " removed from table " + session.id() + " without movement");
149+
this.handleSeatMembershipChanged(session, true);
150+
}
151+
152+
void removePlayerFromTableWithoutMove(UUID playerId) {
153+
if (playerId == null) {
154+
return;
155+
}
156+
MahjongTableSession session = this.manager.tableFor(playerId);
157+
if (session != null) {
158+
this.removePlayerWithoutMove(playerId, session);
159+
return;
160+
}
161+
MahjongTableSession spectatorSession = this.manager.sessionForViewer(playerId);
162+
if (spectatorSession != null) {
163+
this.unspectate(playerId);
164+
}
165+
}
166+
131167
void finalizeDeferredLeaves(MahjongTableSession session, Map<UUID, SeatWind> playerSeats) {
132168
if (session == null || playerSeats == null || playerSeats.isEmpty()) {
133169
return;

src/main/java/top/ellan/mahjong/ui/RuleSettingsUi.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.Locale;
1212
import net.kyori.adventure.text.Component;
1313
import net.kyori.adventure.text.format.NamedTextColor;
14+
import net.kyori.adventure.text.format.TextDecoration;
1415
import org.bukkit.Bukkit;
1516
import org.bukkit.Material;
1617
import org.bukkit.entity.Player;
@@ -255,9 +256,11 @@ private static String riichiProfileLabel(MahjongTableSession session, Locale loc
255256
private static ItemStack namedItem(Material material, Component name, List<Component> loreLines) {
256257
ItemStack item = new ItemStack(material);
257258
ItemMeta meta = item.getItemMeta();
258-
meta.displayName(name.colorIfAbsent(NamedTextColor.YELLOW));
259+
meta.displayName(name.colorIfAbsent(NamedTextColor.YELLOW).decoration(TextDecoration.ITALIC, false));
259260
if (!loreLines.isEmpty()) {
260-
meta.lore(loreLines);
261+
meta.lore(loreLines.stream()
262+
.map(line -> line.colorIfAbsent(NamedTextColor.AQUA).decoration(TextDecoration.ITALIC, false))
263+
.toList());
261264
}
262265
item.setItemMeta(meta);
263266
return item;

src/main/java/top/ellan/mahjong/ui/SettlementUi.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.UUID;
1717
import net.kyori.adventure.text.Component;
1818
import net.kyori.adventure.text.format.NamedTextColor;
19+
import net.kyori.adventure.text.format.TextDecoration;
1920
import org.bukkit.Bukkit;
2021
import org.bukkit.Material;
2122
import org.bukkit.NamespacedKey;
@@ -254,15 +255,15 @@ private static ItemStack tileItem(MahjongTableSession session, MahjongTile tile,
254255
ItemStack customItem = session.plugin().craftEngine().resolveTileItem(session.currentVariant(), tile, faceDown);
255256
if (customItem != null) {
256257
ItemMeta customMeta = customItem.getItemMeta();
257-
customMeta.displayName(name.colorIfAbsent(NamedTextColor.GOLD));
258+
customMeta.displayName(name.colorIfAbsent(NamedTextColor.GOLD).decoration(TextDecoration.ITALIC, false));
258259
customItem.setItemMeta(customMeta);
259260
return customItem;
260261
}
261262
String path = faceDown ? "mahjong_tile/back" : tile.itemModelPath();
262263
ItemStack itemStack = new ItemStack(Material.PAPER);
263264
ItemMeta meta = itemStack.getItemMeta();
264265
PaperCompatibility.applyItemModel(meta, new NamespacedKey("mahjongcraft", path));
265-
meta.displayName(name.colorIfAbsent(NamedTextColor.GOLD));
266+
meta.displayName(name.colorIfAbsent(NamedTextColor.GOLD).decoration(TextDecoration.ITALIC, false));
266267
itemStack.setItemMeta(meta);
267268
return itemStack;
268269
}
@@ -274,9 +275,11 @@ private static MahjongTile toDisplayTile(String name) {
274275
private static ItemStack namedItem(Material material, Component name, List<Component> loreLines) {
275276
ItemStack item = new ItemStack(material);
276277
ItemMeta meta = item.getItemMeta();
277-
meta.displayName(name.colorIfAbsent(NamedTextColor.YELLOW));
278+
meta.displayName(name.colorIfAbsent(NamedTextColor.YELLOW).decoration(TextDecoration.ITALIC, false));
278279
if (!loreLines.isEmpty()) {
279-
meta.lore(loreLines);
280+
meta.lore(loreLines.stream()
281+
.map(line -> line.colorIfAbsent(NamedTextColor.AQUA).decoration(TextDecoration.ITALIC, false))
282+
.toList());
280283
}
281284
item.setItemMeta(meta);
282285
return item;

src/main/java/top/ellan/mahjong/ui/TableControlUi.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.UUID;
1111
import net.kyori.adventure.text.Component;
1212
import net.kyori.adventure.text.format.NamedTextColor;
13+
import net.kyori.adventure.text.format.TextDecoration;
1314
import org.bukkit.Bukkit;
1415
import org.bukkit.Material;
1516
import org.bukkit.entity.Player;
@@ -225,9 +226,11 @@ private static ItemStack actionItem(MahjongTableSession session, Locale locale,
225226
private static ItemStack namedItem(Material material, Component name, List<Component> loreLines) {
226227
ItemStack item = new ItemStack(material);
227228
ItemMeta meta = item.getItemMeta();
228-
meta.displayName(name.colorIfAbsent(NamedTextColor.YELLOW));
229+
meta.displayName(name.colorIfAbsent(NamedTextColor.YELLOW).decoration(TextDecoration.ITALIC, false));
229230
if (!loreLines.isEmpty()) {
230-
meta.lore(loreLines);
231+
meta.lore(loreLines.stream()
232+
.map(line -> line.colorIfAbsent(NamedTextColor.AQUA).decoration(TextDecoration.ITALIC, false))
233+
.toList());
231234
}
232235
item.setItemMeta(meta);
233236
return item;

0 commit comments

Comments
 (0)