Skip to content

Commit 36eecd9

Browse files
committed
add: aliases for PacketConstructorRegistry (and thus for SecExprNewPacket and ExprPacketField)
fix: description for ExprPacketField and SecExprNewPacket to show the available fields
1 parent f96ed1e commit 36eecd9

4 files changed

Lines changed: 201 additions & 66 deletions

File tree

src/main/java/dev/threeadd/packeteventssk/api/general/PacketConstructorRegistry.java

Lines changed: 154 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
44
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
55
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
6-
import com.github.retrooper.packetevents.util.Vector3d;
76
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
87
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientSelectBundleItem;
98
import com.github.retrooper.packetevents.wrapper.play.server.*;
@@ -13,6 +12,7 @@
1312
import org.bukkit.block.data.BlockData;
1413
import org.bukkit.block.sign.Side;
1514
import org.bukkit.util.Vector;
15+
import org.jspecify.annotations.NonNull;
1616

1717
import java.util.*;
1818
import java.util.function.BiConsumer;
@@ -24,65 +24,72 @@ public class PacketConstructorRegistry {
2424

2525
static {
2626
builder(PacketType.Play.Server.ENTITY_VELOCITY, WrapperPlayServerEntityVelocity.class)
27-
.requiredField("entity id", Number.class, WrapperPlayServerEntityVelocity::getEntityId,
28-
(w, id) -> w.setEntityId(id.intValue()))
29-
.requiredField("vector", Vector.class, w -> ConversionUtil.toBukkitVector(w.getVelocity()),
30-
(w, vector) -> w.setVelocity(ConversionUtil.toPeVectorD(vector)))
31-
.constructor(values -> {
32-
Vector vec = values.get("vector", Vector.class);
33-
return new WrapperPlayServerEntityVelocity(
34-
values.get("entity id", Number.class).intValue(),
35-
new Vector3d(vec.getX(), vec.getY(), vec.getZ())
36-
);
37-
})
27+
.requiredField(Number.class, WrapperPlayServerEntityVelocity::getEntityId,
28+
(w, id) -> w.setEntityId(id.intValue()),
29+
"entity id", "id")
30+
.requiredField(Vector.class, w -> ConversionUtil.toBukkitVector(w.getVelocity()),
31+
(w, vector) -> w.setVelocity(ConversionUtil.toPeVectorD(vector)),
32+
"velocity vector", "vector", "velocity")
33+
.constructor(values -> new WrapperPlayServerEntityVelocity(
34+
values.get("entity id", Number.class).intValue(),
35+
ConversionUtil.toPeVectorD(values.get("velocity vector", Vector.class)))
36+
)
3837
.build();
3938

4039
builder(PacketType.Play.Server.GAME_TEST_HIGHLIGHT_POS, WrapperPlayServerGameTestHighlightPos.class)
41-
.requiredField("position", Vector.class, w -> ConversionUtil.toBukkitVector(w.getAbsolutePos()),
42-
(w, vector) -> w.setAbsolutePos(ConversionUtil.toPeVectorI(vector)))
43-
.optionalField("relative position", Vector.class, w -> ConversionUtil.toBukkitVector(w.getRelativePos()),
44-
(w, vector) -> w.setRelativePos(ConversionUtil.toPeVectorI(vector)))
40+
.requiredField(Vector.class, w -> ConversionUtil.toBukkitVector(w.getAbsolutePos()),
41+
(w, vector) -> w.setAbsolutePos(ConversionUtil.toPeVectorI(vector)),
42+
"absolute position", "absolute pos", "abs position", "abs pos", "position", "pos")
43+
.optionalField(Vector.class, w -> ConversionUtil.toBukkitVector(w.getRelativePos()),
44+
(w, vector) -> w.setRelativePos(ConversionUtil.toPeVectorI(vector)),
45+
"relative position", "relative pos", "rel position", "rel pos")
4546
.constructor(values -> new WrapperPlayServerGameTestHighlightPos(
46-
ConversionUtil.toPeVectorI(values.get("position", Vector.class)),
47+
ConversionUtil.toPeVectorI(values.get("absolute position", Vector.class)),
4748
ConversionUtil.toPeVectorI(values.get("relative position", Vector.class)))
4849
)
4950
.build();
5051

5152
builder(PacketType.Play.Server.ENTITY_METADATA, WrapperPlayServerEntityMetadata.class)
52-
.requiredField("entity id", Number.class, WrapperPlayServerEntityMetadata::getEntityId,
53-
(w, id) -> w.setEntityId(id.intValue()))
54-
.requiredField("entity meta", EntityMeta.class, w -> {
55-
EntityType type = EntityTracker.getType(w.getEntityId());
56-
if (type == null) {
57-
throw new IllegalStateException("Failed to find entity type of entity with id " + w.getEntityId());
58-
}
59-
60-
EntityMeta meta = EntityMeta.createMeta(w.getEntityId(), type);
61-
meta.getMetadata().setMetaFromPacket(w);
62-
return meta;
63-
}, WrapperPlayServerEntityMetadata::setEntityMetadata)
53+
.requiredField(Number.class, WrapperPlayServerEntityMetadata::getEntityId,
54+
(w, id) -> w.setEntityId(id.intValue()),
55+
"entity id", "id")
56+
.requiredField(EntityMeta.class, w -> {
57+
EntityType type = EntityTracker.getType(w.getEntityId());
58+
if (type == null) {
59+
throw new IllegalStateException("Failed to find entity type of entity with id " + w.getEntityId());
60+
}
61+
62+
EntityMeta meta = EntityMeta.createMeta(w.getEntityId(), type);
63+
meta.getMetadata().setMetaFromPacket(w);
64+
return meta;
65+
}, WrapperPlayServerEntityMetadata::setEntityMetadata,
66+
"entity metadata", "entity meta", "metadata", "meta")
6467
.constructor(values -> new WrapperPlayServerEntityMetadata(
6568
values.get("entity id", Number.class).intValue(),
66-
values.get("entity meta", EntityMeta.class))
69+
values.get("entity metadata", EntityMeta.class))
6770
)
6871
.build();
6972

7073
builder(PacketType.Play.Server.BLOCK_CHANGE, WrapperPlayServerBlockChange.class)
71-
.requiredField("block position", Vector.class, w -> ConversionUtil.toBukkitVector(w.getBlockPosition()),
72-
(w, vector) -> w.setBlockPosition(ConversionUtil.toPeVectorI(vector)))
73-
.requiredField("block state", BlockData.class, w -> SpigotConversionUtil.toBukkitBlockData(w.getBlockState()),
74-
(w, blockData) -> w.setBlockState(SpigotConversionUtil.fromBukkitBlockData(blockData)))
74+
.requiredField(Vector.class, w -> ConversionUtil.toBukkitVector(w.getBlockPosition()),
75+
(w, vector) -> w.setBlockPosition(ConversionUtil.toPeVectorI(vector)),
76+
"block position", "block pos", "position", "pos")
77+
.requiredField(BlockData.class, w -> SpigotConversionUtil.toBukkitBlockData(w.getBlockState()),
78+
(w, blockData) -> w.setBlockState(SpigotConversionUtil.fromBukkitBlockData(blockData)),
79+
"block state", "state", "block data", "data")
7580
.constructor(values -> new WrapperPlayServerBlockChange(
7681
ConversionUtil.toPeVectorI(values.get("block position", Vector.class)),
7782
SpigotConversionUtil.fromBukkitBlockData(values.get("block state", BlockData.class))
7883
))
7984
.build();
8085

8186
builder(PacketType.Play.Server.OPEN_SIGN_EDITOR, WrapperPlayServerOpenSignEditor.class)
82-
.requiredField("block position", Vector.class, w -> ConversionUtil.toBukkitVector(w.getPosition()),
83-
(w, vector) -> w.setPosition(ConversionUtil.toPeVectorI(vector)))
84-
.requiredField("sign side", Side.class, w -> w.isFrontText() ? Side.FRONT : Side.BACK
85-
, (w, side) -> w.setFrontText(side == Side.FRONT))
87+
.requiredField(Vector.class, w -> ConversionUtil.toBukkitVector(w.getPosition()),
88+
(w, vector) -> w.setPosition(ConversionUtil.toPeVectorI(vector)),
89+
"block position", "block pos", "position", "pos")
90+
.requiredField(Side.class, w -> w.isFrontText() ? Side.FRONT : Side.BACK,
91+
(w, side) -> w.setFrontText(side == Side.FRONT),
92+
"sign side", "side")
8693
.constructor(values -> new WrapperPlayServerOpenSignEditor(
8794
ConversionUtil.toPeVectorI(values.get("block position", Vector.class)),
8895
values.get("sign side", Side.class) == Side.FRONT
@@ -94,27 +101,29 @@ public class PacketConstructorRegistry {
94101
.build();
95102

96103
builder(PacketType.Play.Server.DESTROY_ENTITIES, WrapperPlayServerDestroyEntities.class)
97-
.requiredField("entity ids", Number[].class,
104+
.requiredField(Number[].class,
98105
w -> Arrays.stream(w.getEntityIds()).boxed().toArray(Number[]::new),
99-
(w, ids) -> w.setEntityIds(Arrays.stream(ids).mapToInt(Number::intValue).toArray()))
106+
(w, ids) -> w.setEntityIds(Arrays.stream(ids).mapToInt(Number::intValue).toArray()),
107+
"entity ids", "ids")
100108
.constructor(values -> new WrapperPlayServerDestroyEntities(
101109
Arrays.stream(values.get("entity ids", Number[].class)).mapToInt(Number::intValue).toArray()
102110
))
103111
.build();
104112

105113
builder(PacketType.Play.Client.SELECT_BUNDLE_ITEM, WrapperPlayClientSelectBundleItem.class)
106-
.requiredField("slot id", Number.class, WrapperPlayClientSelectBundleItem::getSlotId,
107-
(w, id) -> w.setSlotId(id.intValue()))
108-
.requiredField("selected item index", Number.class, WrapperPlayClientSelectBundleItem::getSelectedItemIndex,
109-
(w, index) -> w.setSelectedItemIndex(index.intValue()))
114+
.requiredField(Number.class, WrapperPlayClientSelectBundleItem::getSlotId,
115+
(w, id) -> w.setSlotId(id.intValue()),
116+
"slot id", "id")
117+
.requiredField(Number.class, WrapperPlayClientSelectBundleItem::getSelectedItemIndex,
118+
(w, index) -> w.setSelectedItemIndex(index.intValue()),
119+
"selected item index", "selected index", "item index", "index")
110120
.constructor(values -> new WrapperPlayClientSelectBundleItem(
111121
values.get("slot id", Number.class).intValue(),
112122
values.get("selected item index", Number.class).intValue()
113123
))
114124
.build();
115125
}
116126

117-
// wrapperClass is an essential argument for the lambda methods
118127
public static <W extends PacketWrapper<?>> PacketBuilder<W> builder(PacketTypeCommon type, Class<W> wrapperClass) {
119128
return new PacketBuilder<>(type);
120129
}
@@ -127,17 +136,43 @@ public static Collection<PacketDefinition> getAllDefinitions() {
127136
return REGISTRY.values();
128137
}
129138

130-
public record PacketField<T>(String name, Class<T> expectedType, boolean isOptional,
139+
public record PacketField<T>(String name, String[] aliases, Class<T> expectedType, boolean isOptional,
131140
Function<PacketWrapper<?>, T> getter, BiConsumer<PacketWrapper<?>, T> setter) {
141+
public boolean matches(String input) {
142+
if (name.equalsIgnoreCase(input)) return true;
143+
for (String alias : aliases) {
144+
if (alias.equalsIgnoreCase(input)) return true;
145+
}
146+
return false;
147+
}
132148
}
133149

134-
public record PacketDefinition(List<PacketField<?>> fields, Function<PacketValues, PacketWrapper<?>> constructor) {
150+
public record PacketDefinition(PacketTypeCommon type, List<PacketField<?>> fields, Function<PacketValues, PacketWrapper<?>> constructor) {
135151
public PacketField<?> getField(String name) {
136152
for (PacketField<?> field : fields) {
137-
if (field.name().equalsIgnoreCase(name)) return field;
153+
if (field.matches(name)) {
154+
return field;
155+
}
138156
}
139157
return null;
140158
}
159+
160+
public String getReadableFields() {
161+
List<String> fieldStrings = new ArrayList<>();
162+
for (PacketField<?> field : fields) {
163+
if (field.aliases().length > 0) {
164+
fieldStrings.add(field.name() + " (or: " + String.join(", ", field.aliases()) + ")");
165+
} else {
166+
fieldStrings.add(field.name());
167+
}
168+
}
169+
return String.join(" | ", fieldStrings);
170+
}
171+
172+
@Override
173+
public @NonNull String toString() {
174+
return type.getName().replace("_", " ").toLowerCase(Locale.ENGLISH);
175+
}
141176
}
142177

143178
public static class PacketBuilder<W extends PacketWrapper<?>> {
@@ -150,14 +185,26 @@ public PacketBuilder(PacketTypeCommon type) {
150185
}
151186

152187
@SuppressWarnings("unchecked")
153-
public <T> PacketBuilder<W> requiredField(String name, Class<T> type, Function<W, T> getter, BiConsumer<W, T> setter) {
154-
this.fields.add(new PacketField<>(name, type, false, (Function<PacketWrapper<?>, T>) getter, (BiConsumer<PacketWrapper<?>, T>) setter));
188+
public <T> PacketBuilder<W> requiredField(Class<T> type, Function<W, T> getter, BiConsumer<W, T> setter, String... names) {
189+
if (names == null || names.length == 0) {
190+
throw new IllegalArgumentException("Field registration must specify at least a primary name.");
191+
}
192+
String primaryName = names[0];
193+
String[] aliases = Arrays.copyOfRange(names, 1, names.length);
194+
195+
this.fields.add(new PacketField<>(primaryName, aliases, type, false, (Function<PacketWrapper<?>, T>) getter, (BiConsumer<PacketWrapper<?>, T>) setter));
155196
return this;
156197
}
157198

158199
@SuppressWarnings("unchecked")
159-
public <T> PacketBuilder<W> optionalField(String name, Class<T> type, Function<W, T> getter, BiConsumer<W, T> setter) {
160-
this.fields.add(new PacketField<>(name, type, true, (Function<PacketWrapper<?>, T>) getter, (BiConsumer<PacketWrapper<?>, T>) setter));
200+
public <T> PacketBuilder<W> optionalField(Class<T> type, Function<W, T> getter, BiConsumer<W, T> setter, String... names) {
201+
if (names == null || names.length == 0) {
202+
throw new IllegalArgumentException("Field registration must specify at least a primary name.");
203+
}
204+
String primaryName = names[0];
205+
String[] aliases = Arrays.copyOfRange(names, 1, names.length);
206+
207+
this.fields.add(new PacketField<>(primaryName, aliases, type, true, (Function<PacketWrapper<?>, T>) getter, (BiConsumer<PacketWrapper<?>, T>) setter));
161208
return this;
162209
}
163210

@@ -172,28 +219,77 @@ public void build() {
172219
throw new IllegalStateException("Cannot build packet definition for '" + type.getName() + "' because no constructor was provided.");
173220
}
174221

175-
REGISTRY.put(this.type, new PacketDefinition(Collections.unmodifiableList(this.fields), (Function) this.packetConstructor));
222+
REGISTRY.put(this.type, new PacketDefinition(this.type, Collections.unmodifiableList(this.fields), (Function) this.packetConstructor));
176223
}
177224
}
178225

179226
public static class PacketValues {
227+
private final List<PacketField<?>> registeredFields;
180228
private final Map<String, Object> values;
181229

182-
public PacketValues(Map<String, Object> values) {
230+
public PacketValues(List<PacketField<?>> registeredFields, Map<String, Object> values) {
231+
this.registeredFields = registeredFields;
183232
this.values = values;
184233
}
185234

235+
private void assertKeyRegistered(String key) {
236+
for (PacketField<?> field : registeredFields) {
237+
if (field.matches(key)) return;
238+
}
239+
throw new IllegalArgumentException("Attempted to look up field '" + key + "', but it is not registered in this packet definition's fields.");
240+
}
241+
186242
public <T> T get(String key, Class<T> clazz) {
187-
return clazz.cast(values.get(key));
243+
assertKeyRegistered(key);
244+
245+
PacketField<?> targetField = null;
246+
for (PacketField<?> field : registeredFields) {
247+
if (field.matches(key)) {
248+
targetField = field;
249+
break;
250+
}
251+
}
252+
253+
if (targetField != null) {
254+
if (values.containsKey(targetField.name())) return clazz.cast(values.get(targetField.name()));
255+
for (String alias : targetField.aliases()) {
256+
if (values.containsKey(alias)) return clazz.cast(values.get(alias));
257+
}
258+
259+
for (Map.Entry<String, Object> entry : values.entrySet()) {
260+
if (targetField.matches(entry.getKey())) return clazz.cast(entry.getValue());
261+
}
262+
}
263+
264+
return null;
188265
}
189266

190267
public <T> T getOrElse(String key, Class<T> clazz, T defaultValue) {
191-
Object val = values.get(key);
192-
return clazz.isInstance(val) ? clazz.cast(val) : defaultValue;
268+
T val = get(key, clazz);
269+
return val != null ? val : defaultValue;
193270
}
194271

195272
public boolean has(String key) {
196-
return values.containsKey(key);
273+
assertKeyRegistered(key);
274+
275+
PacketField<?> targetField = null;
276+
for (PacketField<?> field : registeredFields) {
277+
if (field.matches(key)) {
278+
targetField = field;
279+
break;
280+
}
281+
}
282+
283+
if (targetField != null) {
284+
if (values.containsKey(targetField.name())) return true;
285+
for (String alias : targetField.aliases()) {
286+
if (values.containsKey(alias)) return true;
287+
}
288+
for (String k : values.keySet()) {
289+
if (targetField.matches(k)) return true;
290+
}
291+
}
292+
return false;
197293
}
198294
}
199295
}

0 commit comments

Comments
 (0)