Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 775fe23

Browse files
committedApr 4, 2025··
refactor(velocity): handle language player without player instance
This commit makes it possible to create a VelocityLanguagePlayer from a UUID only, without using Velocity's Player instance. This is necessary for plugins like LimboAPI that do not trigger LoginEvent while in the limbo phase. For that reason, when translating via PacketEvents, Triton would attempt to create instances of the VelocityLanguagePlayer but fail due to the missing player instance. This has now been fixed by removing the assumption that the Player instance is always available. Fixes #498
1 parent 56d992f commit 775fe23

File tree

7 files changed

+78
-46
lines changed

7 files changed

+78
-46
lines changed
 

‎core/src/main/java/com/rexcantor64/triton/packetinterceptor/PacketEventsListener.java

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.retrooper.packetevents.event.PacketListener;
44
import com.github.retrooper.packetevents.event.PacketSendEvent;
5+
import com.github.retrooper.packetevents.event.UserDisconnectEvent;
56
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
67
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
78
import com.rexcantor64.triton.Triton;
@@ -98,4 +99,13 @@ public void onPacketSend(PacketSendEvent event) {
9899
handler.accept(event, languagePlayer);
99100
}
100101
}
102+
103+
@Override
104+
public void onUserDisconnect(UserDisconnectEvent event) {
105+
// force language player to be unregistered
106+
val uuid = event.getUser().getUUID();
107+
if (uuid != null) {
108+
Triton.get().getPlayerManager().unregisterPlayer(uuid);
109+
}
110+
}
101111
}

‎triton-velocity/src/main/java/com/rexcantor64/triton/velocity/VelocityTriton.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class VelocityTriton extends Triton<VelocityLanguagePlayer, VelocityBridg
3131
private ScheduledTask configRefreshTask;
3232

3333
public VelocityTriton(PluginLoader loader) {
34-
super(new PlayerManager<>(VelocityLanguagePlayer::fromUUID), new VelocityBridgeManager());
34+
super(new PlayerManager<>(VelocityLanguagePlayer::new), new VelocityBridgeManager());
3535
super.loader = loader;
3636
}
3737

‎triton-velocity/src/main/java/com/rexcantor64/triton/velocity/bridge/VelocityBridgeManager.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,11 @@ public void onPluginMessage(PluginMessageEvent e) {
8080
}
8181

8282
public void sendPlayerLanguage(@NonNull VelocityLanguagePlayer lp) {
83-
Triton.get().getLogger().logTrace("Sending player %1 language to server", lp);
84-
val out = BridgeSerializer.buildPlayerLanguageData(lp);
85-
sendPluginMessage(lp.getParent(), out);
83+
lp.getPlatformPlayer().ifPresent(player -> {
84+
Triton.get().getLogger().logTrace("Sending player %1 language to server", lp);
85+
val out = BridgeSerializer.buildPlayerLanguageData(lp);
86+
sendPluginMessage(player, out);
87+
});
8688
}
8789

8890
public void sendExecutableCommand(String command, @NonNull RegisteredServer server) {

‎triton-velocity/src/main/java/com/rexcantor64/triton/velocity/listeners/VelocityListener.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ public void afterServerConnect(ServerPostConnectEvent e) {
6969
@Subscribe(order = PostOrder.FIRST)
7070
public void onPlayerLogin(LoginEvent e) {
7171
val player = e.getPlayer();
72-
val lp = new VelocityLanguagePlayer(player);
73-
VelocityTriton.asVelocity().getPlayerManager().registerPlayer(lp);
72+
val lp = VelocityTriton.asVelocity().getPlayerManager().get(player.getUniqueId());
73+
lp.setParent(player);
7474
lp.injectNettyPipeline();
7575
}
7676

‎triton-velocity/src/main/java/com/rexcantor64/triton/velocity/packetinterceptor/packets/DisconnectHandler.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ private FeatureSyntax getKickSyntax() {
4444
.map(result -> {
4545
// During the Login phase, this packet is supposed to send JSON text even on 1.20.3+ (instead of NBT data)
4646
// https://github.com/PaperMC/Velocity/blob/be678840de9c927c9e17bcea06ed7aedcea77d1e/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DisconnectPacket.java#L81-L82
47-
val connectedPlayer = (ConnectedPlayer) player.getParent();
48-
val isLoginPhase = connectedPlayer.getConnection().getState() == StateRegistry.LOGIN;
47+
val connectedPlayer = player.getPlatformPlayer().map(p -> (ConnectedPlayer) p);
48+
val isLoginPhase = connectedPlayer
49+
.map(p -> p.getConnection().getState() == StateRegistry.LOGIN)
50+
.orElse(true);
4951
return new ComponentHolder(
5052
isLoginPhase ? ProtocolVersion.MINECRAFT_1_20_2 : player.getProtocolVersion(),
5153
result

‎triton-velocity/src/main/java/com/rexcantor64/triton/velocity/player/RefreshFeatures.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ private void refreshPlayerListItems() {
8181
}
8282

8383
private void sendPacket(MinecraftPacket packet) {
84-
((ConnectedPlayer) player.getParent()).getConnection().write(packet);
84+
player.getPlatformPlayer()
85+
.ifPresent(parent -> ((ConnectedPlayer) parent).getConnection().write(packet));
8586
}
8687

8788
}

‎triton-velocity/src/main/java/com/rexcantor64/triton/velocity/player/VelocityLanguagePlayer.java

+54-37
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,22 @@
1919
import net.kyori.adventure.text.Component;
2020
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
2121
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.annotations.Nullable;
2223

2324
import java.util.Collections;
2425
import java.util.HashMap;
2526
import java.util.Map;
27+
import java.util.Objects;
2628
import java.util.Optional;
2729
import java.util.UUID;
2830
import java.util.concurrent.ConcurrentHashMap;
2931

3032
public class VelocityLanguagePlayer extends TritonLanguagePlayer<Player> {
31-
@Getter
32-
private final Player parent;
33+
@NotNull
34+
private final UUID uuid;
35+
@Nullable
36+
@Setter
37+
private Player parent;
3338

3439
private Language language;
3540

@@ -45,21 +50,21 @@ public class VelocityLanguagePlayer extends TritonLanguagePlayer<Player> {
4550
private String clientLocale;
4651
private final RefreshFeatures refresher;
4752

48-
public VelocityLanguagePlayer(@NotNull Player parent) {
53+
public VelocityLanguagePlayer(@NotNull UUID uuid) {
4954
super();
50-
this.parent = parent;
55+
Objects.requireNonNull(uuid, "cannot build VelocityLanguagePlayer from null UUID");
56+
this.uuid = uuid;
5157
this.refresher = new RefreshFeatures(this);
5258
Triton.get().runAsync(this::load);
5359
}
5460

55-
public static VelocityLanguagePlayer fromUUID(UUID uuid) {
56-
val player = VelocityTriton.asVelocity().getLoader().getServer().getPlayer(uuid);
57-
return player.map(VelocityLanguagePlayer::new).orElse(null);
58-
}
59-
6061
@Override
6162
public @NotNull Optional<Player> getPlatformPlayer() {
62-
return Optional.of(this.parent);
63+
if (this.parent == null) {
64+
return VelocityTriton.asVelocity().getLoader().getServer().getPlayer(this.uuid);
65+
} else {
66+
return Optional.of(this.parent);
67+
}
6368
}
6469

6570
public void setBossbar(UUID uuid, Component lastBossBar) {
@@ -119,14 +124,18 @@ public void setLang(Language language) {
119124

120125
public void setLang(Language language, boolean sendToSpigot) {
121126
// TODO fire Triton's API change language event
122-
if (this.waitingForClientLocale && getParent() != null)
123-
parent.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Triton.get().getMessagesConfig()
124-
.getMessage("success.detected-language", language.getDisplayName())));
127+
val player = getPlatformPlayer();
128+
if (this.waitingForClientLocale) {
129+
player.ifPresent(parent -> parent.sendMessage(
130+
LegacyComponentSerializer.legacyAmpersand().deserialize(Triton.get().getMessagesConfig()
131+
.getMessage("success.detected-language", language.getDisplayName()))));
132+
133+
}
125134
this.language = language;
126135
this.waitingForClientLocale = false;
127136

128-
if (sendToSpigot && getParent() != null) {
129-
VelocityTriton.asVelocity().getBridgeManager().sendPlayerLanguage(this);
137+
if (sendToSpigot) {
138+
player.ifPresent(p -> VelocityTriton.asVelocity().getBridgeManager().sendPlayerLanguage(this));
130139
}
131140

132141
save();
@@ -146,50 +155,59 @@ public void injectNettyPipeline() {
146155
Triton.get().getLogger().logDebug("Skipped injecting into netty pipeline for player %1 because PacketEvents is in use", getUUID());
147156
return;
148157
}
149-
ConnectedPlayer connectedPlayer = (ConnectedPlayer) this.parent;
150-
connectedPlayer.getConnection().getChannel().pipeline()
151-
.addAfter(Connections.MINECRAFT_ENCODER, "triton-custom-encoder", new VelocityNettyEncoder(this));
158+
val player = this.getPlatformPlayer();
159+
player.ifPresent(parent -> {
160+
ConnectedPlayer connectedPlayer = (ConnectedPlayer) parent;
161+
connectedPlayer.getConnection().getChannel().pipeline()
162+
.addAfter(Connections.MINECRAFT_ENCODER, "triton-custom-encoder", new VelocityNettyEncoder(this));
163+
});
152164
}
153165

154166
@Override
155167
public UUID getUUID() {
156-
return this.parent.getUniqueId();
168+
return this.uuid;
157169
}
158170

159171
public @NotNull ProtocolVersion getProtocolVersion() {
160-
return this.getParent().getProtocolVersion();
172+
return this.getPlatformPlayer().map(Player::getProtocolVersion).orElse(ProtocolVersion.UNKNOWN);
161173
}
162174

163175
private void load() {
176+
val player = getPlatformPlayer();
164177
this.language = Triton.get().getStorage().getLanguage(this);
165178
if (this.clientLocale != null && this.isWaitingForClientLocale()) {
166179
this.waitingForClientLocale = false;
167180
this.language = Triton.get().getLanguageManager().getLanguageByLocaleOrDefault(this.clientLocale);
168-
if (getParent() != null) {
169-
getParent().sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Triton.get().getMessagesConfig()
170-
.getMessage("success.detected-language", language.getDisplayName())));
171-
}
172-
}
173-
if (getParent() != null) {
174-
Triton.get().getStorage()
175-
.setLanguage(null, SocketUtils.getIpAddress(getParent().getRemoteAddress()), language);
181+
player.ifPresent(parent -> parent.sendMessage(
182+
LegacyComponentSerializer.legacyAmpersand().deserialize(Triton.get().getMessagesConfig()
183+
.getMessage("success.detected-language", language.getDisplayName()))));
176184
}
185+
player.ifPresent(parent -> Triton.get().getStorage()
186+
.setLanguage(null, SocketUtils.getIpAddress(parent.getRemoteAddress()), language));
177187
}
178188

179189
private void save() {
180190
Triton.get().runAsync(() -> {
181-
val ip = SocketUtils.getIpAddress(getParent().getRemoteAddress());
182-
Triton.get().getStorage().setLanguage(getParent().getUniqueId(), ip, language);
191+
val ip = getPlatformPlayer()
192+
.map(player -> SocketUtils.getIpAddress(player.getRemoteAddress()))
193+
.orElse(null);
194+
Triton.get().getStorage().setLanguage(getUUID(), ip, language);
183195
});
184196
}
185197

186198
public void executeCommands(RegisteredServer overrideServer) {
187-
val currentServer = getParent().getCurrentServer();
199+
val playerOpt = getPlatformPlayer();
200+
if (!playerOpt.isPresent()) {
201+
return;
202+
}
203+
val player = playerOpt.get();
204+
205+
val currentServer = player.getCurrentServer();
188206
if (overrideServer == null && !currentServer.isPresent()) return;
189207
val server = overrideServer == null ? currentServer.get().getServer() : overrideServer;
190208
for (val cmd : ((com.rexcantor64.triton.language.Language) language).getCmds()) {
191-
val cmdText = cmd.getCmd().replace("%player%", getParent().getUsername())
192-
.replace("%uuid%", getParent().getUniqueId().toString());
209+
val cmdText = cmd.getCmd().replace("%player%", player.getUsername())
210+
.replace("%uuid%", player.getUniqueId().toString());
193211

194212
if (!cmd.isUniversal() && !cmd.getServers().contains(server.getServerInfo().getName())) {
195213
continue;
@@ -200,20 +218,19 @@ public void executeCommands(RegisteredServer overrideServer) {
200218
if (cmd.getType() == ExecutableCommand.Type.SERVER) {
201219
VelocityTriton.asVelocity().getBridgeManager().sendExecutableCommand(cmdText, server);
202220
} else if (cmd.getType() == ExecutableCommand.Type.PLAYER) {
203-
getParent().spoofChatInput("/" + cmdText);
221+
player.spoofChatInput("/" + cmdText);
204222
} else if (cmd.getType() == ExecutableCommand.Type.BUNGEE) {
205223
velocity.getCommandManager().executeAsync(velocity.getConsoleCommandSource(), cmdText);
206224
} else if (cmd.getType() == ExecutableCommand.Type.BUNGEE_PLAYER) {
207-
velocity.getCommandManager().executeAsync(getParent(), cmdText);
225+
velocity.getCommandManager().executeAsync(player, cmdText);
208226
}
209227
}
210228
}
211229

212230
@Override
213231
public String toString() {
214232
return "VelocityLanguagePlayer{" +
215-
"username=" + parent.getUsername() +
216-
", uuid=" + parent.getUniqueId() +
233+
"uuid=" + this.getUUID() +
217234
", language=" + Optional.ofNullable(language).map(Language::getName).orElse("null") +
218235
'}';
219236
}

0 commit comments

Comments
 (0)
Please sign in to comment.