Skip to content

Commit deff9eb

Browse files
committed
fix(spigot): motd translation error on MC 1.21.10
Related to dmulloy2/ProtocolLib#3570
1 parent 88347fb commit deff9eb

File tree

2 files changed

+155
-26
lines changed

2 files changed

+155
-26
lines changed

triton-spigot/src/main/java/com/rexcantor64/triton/spigot/packetinterceptor/MotdPacketHandler.java

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@
55
import com.comphenix.protocol.events.ListenerPriority;
66
import com.comphenix.protocol.events.PacketAdapter;
77
import com.comphenix.protocol.events.PacketEvent;
8+
import com.comphenix.protocol.reflect.accessors.Accessors;
9+
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
810
import com.comphenix.protocol.utility.MinecraftVersion;
911
import com.comphenix.protocol.wrappers.WrappedGameProfile;
12+
import com.comphenix.protocol.wrappers.WrappedServerPing;
13+
import com.comphenix.protocol.wrappers.ping.ServerPingImpl;
14+
import com.comphenix.protocol.wrappers.ping.ServerPingRecord;
1015
import com.rexcantor64.triton.Triton;
11-
import com.rexcantor64.triton.language.parser.AdventureParser;
1216
import com.rexcantor64.triton.language.parser.MessageParser;
1317
import com.rexcantor64.triton.spigot.SpigotTriton;
1418
import com.rexcantor64.triton.spigot.utils.WrappedComponentUtils;
19+
import com.rexcantor64.triton.spigot.wrappers.WrappedNameAndId;
1520
import lombok.val;
1621
import net.kyori.adventure.text.Component;
1722
import org.bukkit.entity.Player;
@@ -20,17 +25,22 @@
2025
import java.net.InetSocketAddress;
2126
import java.util.Arrays;
2227
import java.util.Collections;
23-
import java.util.Objects;
2428
import java.util.Optional;
2529
import java.util.UUID;
2630
import java.util.stream.Collectors;
2731
import java.util.stream.Stream;
2832

2933
public class MotdPacketHandler extends PacketAdapter {
3034

35+
private final FieldAccessor WRAPPED_SERVER_PING_IMPL;
36+
private final FieldAccessor SERVER_PING_RECORD_PLAYERS_SAMPLE;
37+
3138
public MotdPacketHandler() {
3239
super(SpigotTriton.asSpigot().getJavaPlugin(), ListenerPriority.HIGHEST,
3340
Collections.singleton(PacketType.Status.Server.SERVER_INFO), ListenerOptions.ASYNC);
41+
42+
WRAPPED_SERVER_PING_IMPL = Accessors.getFieldAccessor(WrappedServerPing.class, ServerPingImpl.class, true);
43+
SERVER_PING_RECORD_PLAYERS_SAMPLE = Accessors.getFieldAccessor(ServerPingRecord.class, ServerPingRecord.PlayerSample.class, true);
3444
}
3545

3646
/**
@@ -65,31 +75,70 @@ private void handleServerInfo(PacketEvent event) {
6575
val syntax = Triton.get().getConfig().getMotdSyntax();
6676

6777
val serverPing = event.getPacket().getServerPings().readSafely(0);
68-
serverPing.setPlayers(serverPing.getPlayers().stream().flatMap((gp) -> {
69-
if (gp.getName() == null) {
70-
return Stream.of(gp);
78+
if (WrappedNameAndId.getWrappedClass() == null) {
79+
// MC 1.21.8 and below
80+
serverPing.setPlayers(serverPing.getPlayers().stream().flatMap((gp) -> {
81+
if (gp.getName() == null) {
82+
return Stream.of(gp);
83+
}
84+
85+
return parser()
86+
.translateString(
87+
gp.getName(),
88+
lang,
89+
syntax
90+
)
91+
.mapToObj(
92+
(translatedName) -> {
93+
val translatedNameSplit = translatedName.split("\n", -1);
94+
if (translatedNameSplit.length > 1) {
95+
return Arrays.stream(translatedNameSplit).map(name -> new WrappedGameProfile(UUID.randomUUID(), name));
96+
} else {
97+
return Stream.of(gp.withName(translatedName));
98+
}
99+
},
100+
() -> Stream.of(gp),
101+
Stream::empty
102+
);
103+
104+
}).collect(Collectors.toList()));
105+
} else {
106+
// MC 1.21.9+
107+
108+
// I guess we're doing reflection on ProtocolLib again...
109+
ServerPingRecord impl = (ServerPingRecord) WRAPPED_SERVER_PING_IMPL.get(serverPing);
110+
ServerPingRecord.PlayerSample playerSample = (ServerPingRecord.PlayerSample) SERVER_PING_RECORD_PLAYERS_SAMPLE.get(impl);
111+
112+
if (playerSample.sample != null) {
113+
val sample = WrappedNameAndId.LIST_CONVERTER.getSpecific(playerSample.sample);
114+
val newList = sample.stream().flatMap((player) -> {
115+
if (player.getName() == null) {
116+
return Stream.of(player);
117+
}
118+
119+
return parser()
120+
.translateString(
121+
player.getName(),
122+
lang,
123+
syntax
124+
)
125+
.mapToObj(
126+
(translatedName) -> {
127+
val translatedNameSplit = translatedName.split("\n", -1);
128+
if (translatedNameSplit.length > 1) {
129+
return Arrays.stream(translatedNameSplit).map(name -> new WrappedNameAndId(UUID.randomUUID(), name));
130+
} else {
131+
return Stream.of(player.withName(translatedName));
132+
}
133+
},
134+
() -> Stream.of(player),
135+
Stream::empty
136+
);
137+
}).collect(Collectors.toList());
138+
139+
playerSample.sample = WrappedNameAndId.LIST_CONVERTER.getGeneric(newList);
71140
}
72-
73-
return parser()
74-
.translateString(
75-
gp.getName(),
76-
lang,
77-
syntax
78-
)
79-
.mapToObj(
80-
(translatedName) -> {
81-
val translatedNameSplit = translatedName.split("\n", -1);
82-
if (translatedNameSplit.length > 1) {
83-
return Arrays.stream(translatedNameSplit).map(name -> new WrappedGameProfile(UUID.randomUUID(), name));
84-
} else {
85-
return Stream.of(gp.withName(translatedName));
86-
}
87-
},
88-
() -> Stream.of(gp),
89-
Stream::empty
90-
);
91-
92-
}).collect(Collectors.toList()));
141+
}
93142

94143
parser().translateString(serverPing.getVersionName(), lang, syntax)
95144
.ifChanged(serverPing::setVersionName);
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.rexcantor64.triton.spigot.wrappers;
2+
3+
import com.comphenix.protocol.reflect.EquivalentConverter;
4+
import com.comphenix.protocol.reflect.FuzzyReflection;
5+
import com.comphenix.protocol.reflect.accessors.Accessors;
6+
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
7+
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
8+
import com.comphenix.protocol.utility.MinecraftReflection;
9+
import com.comphenix.protocol.wrappers.AbstractWrapper;
10+
import com.comphenix.protocol.wrappers.BukkitConverters;
11+
import com.comphenix.protocol.wrappers.Converters;
12+
import com.comphenix.protocol.wrappers.WrappedChatComponent;
13+
import org.jetbrains.annotations.Contract;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
16+
17+
import java.util.List;
18+
import java.util.Objects;
19+
import java.util.Optional;
20+
import java.util.UUID;
21+
22+
/**
23+
* Custom ProtocolLib Wrapper of NMS' NameAndId (added to NMS in 1.21.9)
24+
* Tested with 1.21.10
25+
*/
26+
public class WrappedNameAndId extends AbstractWrapper {
27+
28+
private static final @Nullable Class<?> NAME_AND_ID_CLASS;
29+
private static ConstructorAccessor CONSTRUCTOR;
30+
private static FieldAccessor UUID_FIELD;
31+
private static FieldAccessor NAME_FIELD;
32+
33+
public static final EquivalentConverter<WrappedNameAndId> CONVERTER = Converters.ignoreNull(Converters.handle(WrappedNameAndId::getHandle, WrappedNameAndId::fromHandle, WrappedNameAndId.class));
34+
public static final EquivalentConverter<List<WrappedNameAndId>> LIST_CONVERTER = BukkitConverters.getListConverter(CONVERTER);
35+
36+
static {
37+
NAME_AND_ID_CLASS = MinecraftReflection.getNullableNMS("server.players.NameAndId");
38+
if (NAME_AND_ID_CLASS != null) {
39+
CONSTRUCTOR = Accessors.getConstructorAccessor(NAME_AND_ID_CLASS, UUID.class, String.class);
40+
UUID_FIELD = Accessors.getFieldAccessor(NAME_AND_ID_CLASS, UUID.class, true);
41+
NAME_FIELD = Accessors.getFieldAccessor(NAME_AND_ID_CLASS, String.class, true);
42+
}
43+
}
44+
45+
private WrappedNameAndId(Object handle) {
46+
super(Objects.requireNonNull(getWrappedClass(), "NameAndId class does not exist on this version"));
47+
setHandle(handle);
48+
}
49+
50+
public WrappedNameAndId(UUID id, String name) {
51+
this(CONSTRUCTOR.invoke(id, name));
52+
}
53+
54+
public UUID getUniqueId() {
55+
return (UUID) UUID_FIELD.get(handle);
56+
}
57+
58+
public String getName() {
59+
return (String) NAME_FIELD.get(handle);
60+
}
61+
62+
@Contract("_ -> new")
63+
public WrappedNameAndId withUniqueId(UUID id) {
64+
return new WrappedNameAndId(id, this.getName());
65+
}
66+
67+
@Contract("_ -> new")
68+
public WrappedNameAndId withName(String name) {
69+
return new WrappedNameAndId(this.getUniqueId(), name);
70+
}
71+
72+
@Contract("_ -> new")
73+
public static @NotNull WrappedNameAndId fromHandle(Object handle) {
74+
return new WrappedNameAndId(handle);
75+
}
76+
77+
public static @Nullable Class<?> getWrappedClass() {
78+
return NAME_AND_ID_CLASS;
79+
}
80+
}

0 commit comments

Comments
 (0)