• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyclopsMC / CyclopsCore / #479033859

06 Nov 2026 04:12PM UTC coverage: 23.271% (+0.2%) from 23.052%
#479033859

Pull #260

github

claude
Bound list and map sizes and avoid class init when decoding packets

The List, Map and Pair codecs read sizes and class names from the
packet. A small packet could make the decoder allocate very large lists,
and Class.forName initialized any class named in the packet.

Collection sizes are now bounded by the remaining packet bytes, the
number of (unencoded) null list elements is limited, list indices must
increase within the list size, and classes are loaded without
initialization and must have a codec action. Invalid input throws a
DecoderException instead of being logged.

Fixes GHSA-4jfp-83qj-mr8r

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UeSuG5REz2JxHTZADcGhh2
Pull Request #260: Bound list and map sizes and avoid class init when decoding packets

44 of 49 new or added lines in 1 file covered. (89.8%)

2470 of 10614 relevant lines covered (23.27%)

0.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.44
/src/main/java/org/cyclops/cyclopscore/network/PacketCodec.java
1
package org.cyclops.cyclopscore.network;
2

3
import com.google.common.collect.Lists;
4
import com.google.common.collect.Maps;
5
import io.netty.handler.codec.DecoderException;
6
import net.minecraft.core.BlockPos;
7
import net.minecraft.core.Direction;
8
import net.minecraft.core.Vec3i;
9
import net.minecraft.nbt.CompoundTag;
10
import net.minecraft.nbt.Tag;
11
import net.minecraft.network.FriendlyByteBuf;
12
import net.minecraft.resources.ResourceKey;
13
import net.minecraft.resources.ResourceLocation;
14
import net.minecraft.world.item.ItemStack;
15
import net.minecraft.world.phys.Vec3;
16
import net.minecraftforge.fluids.FluidStack;
17
import org.apache.commons.lang3.ClassUtils;
18
import org.apache.commons.lang3.tuple.Pair;
19
import org.cyclops.cyclopscore.datastructure.DimPos;
20
import org.cyclops.cyclopscore.datastructure.SingleCache;
21
import org.cyclops.cyclopscore.inventory.ItemLocation;
22
import org.joml.Vector3d;
23

24
import javax.annotation.Nullable;
25
import java.lang.reflect.Field;
26
import java.util.Arrays;
27
import java.util.Collections;
28
import java.util.Comparator;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.Objects;
32

33
/**
34
 * Packet with automatic coding and decoding of basic fields annotated with {@link CodecField}.
35
 * @author rubensworks
36
 *
37
 */
38
public abstract class PacketCodec extends PacketBase {
1 ✔
39

40
    public static final int READ_STRING_MAX_LENGTH = 32767;
41
    /**
42
     * The maximum number of null elements in a decoded list.
43
     * Null elements are not encoded, so they are limited separately from the packet size.
44
     */
45
    public static final int MAX_LIST_NULL_ELEMENTS = 1024;
46

47
    private static Map<Class<?>, ICodecAction> codecActions = Maps.newHashMap();
1 ✔
48
    static {
49
        codecActions.put(String.class, new ICodecAction() {
1 ✔
50

51
            @Override
52
            public void encode(Object object, FriendlyByteBuf output) {
53
                output.writeUtf((String) object);
1 ✔
54
            }
1 ✔
55

56
            @Override
57
            public Object decode(FriendlyByteBuf input) {
58
                return input.readUtf(32767); // The arg-less version is client-side only, so we copy its implementation.
1 ✔
59
            }
60
        });
61

62
        codecActions.put(double.class, new ICodecAction() {
1 ✔
63

64
            @Override
65
            public void encode(Object object, FriendlyByteBuf output) {
66
                output.writeDouble((Double) object);
1 ✔
67
            }
1 ✔
68

69
            @Override
70
            public Object decode(FriendlyByteBuf input) {
71
                return input.readDouble();
1 ✔
72
            }
73
        });
74

75
        codecActions.put(int.class, new ICodecAction() {
1 ✔
76

77
            @Override
78
            public void encode(Object object, FriendlyByteBuf output) {
79
                output.writeInt((int) object);
1 ✔
80
            }
1 ✔
81

82
            @Override
83
            public Object decode(FriendlyByteBuf input) {
84
                return input.readInt();
1 ✔
85
            }
86
        });
87

88
        codecActions.put(long.class, new ICodecAction() {
1 ✔
89

90
            @Override
91
            public void encode(Object object, FriendlyByteBuf output) {
92
                output.writeLong((long) object);
1 ✔
93
            }
1 ✔
94

95
            @Override
96
            public Object decode(FriendlyByteBuf input) {
97
                return input.readLong();
1 ✔
98
            }
99
        });
100

101
        codecActions.put(short.class, new ICodecAction() {
1 ✔
102

103
            @Override
104
            public void encode(Object object, FriendlyByteBuf output) {
105
                output.writeShort((Short) object);
1 ✔
106
            }
1 ✔
107

108
            @Override
109
            public Object decode(FriendlyByteBuf input) {
110
                return input.readShort();
1 ✔
111
            }
112
        });
113

114
        codecActions.put(boolean.class, new ICodecAction() {
1 ✔
115

116
            @Override
117
            public void encode(Object object, FriendlyByteBuf output) {
118
                output.writeBoolean((Boolean) object);
1 ✔
119
            }
1 ✔
120

121
            @Override
122
            public Object decode(FriendlyByteBuf input) {
123
                return input.readBoolean();
1 ✔
124
            }
125
        });
126

127
        codecActions.put(float.class, new ICodecAction() {
1 ✔
128

129
            @Override
130
            public void encode(Object object, FriendlyByteBuf output) {
131
                output.writeFloat((Float) object);
1 ✔
132
            }
1 ✔
133

134
            @Override
135
            public Object decode(FriendlyByteBuf input) {
136
                return input.readFloat();
1 ✔
137
            }
138
        });
139

140
        codecActions.put(Vector3d.class, new ICodecAction() {
1 ✔
141
            @Override
142
            public void encode(Object object, FriendlyByteBuf output) {
143
                Vector3d v = (Vector3d)object;
1 ✔
144
                output.writeDouble(v.x);
1 ✔
145
                output.writeDouble(v.y);
1 ✔
146
                output.writeDouble(v.z);
1 ✔
147
            }
1 ✔
148

149
            @Override
150
            public Object decode(FriendlyByteBuf input) {
151
                double x = input.readDouble();
1 ✔
152
                double y = input.readDouble();
1 ✔
153
                double z = input.readDouble();
1 ✔
154
                return new Vector3d(x, y, z);
1 ✔
155
            }
156
        });
157

158
        codecActions.put(Vec3i.class, new ICodecAction() {
1 ✔
159
            @Override
160
            public void encode(Object object, FriendlyByteBuf output) {
161
                Vec3i v = (Vec3i)object;
×
162
                output.writeInt(v.getX());
×
163
                output.writeInt(v.getY());
×
164
                output.writeInt(v.getZ());
×
165
            }
×
166

167
            @Override
168
            public Object decode(FriendlyByteBuf input) {
169
                int x = input.readInt();
×
170
                int y = input.readInt();
×
171
                int z = input.readInt();
×
172
                return new Vec3i(x, y, z);
×
173
            }
174
        });
175

176
        codecActions.put(Vec3.class, new ICodecAction() {
1 ✔
177
            @Override
178
            public void encode(Object object, FriendlyByteBuf output) {
179
                Vec3 v = (Vec3)object;
×
180
                output.writeDouble(v.x);
×
181
                output.writeDouble(v.y);
×
182
                output.writeDouble(v.z);
×
183
            }
×
184

185
            @Override
186
            public Object decode(FriendlyByteBuf input) {
187
                double x = input.readDouble();
×
188
                double y = input.readDouble();
×
189
                double z = input.readDouble();
×
190
                return new Vec3(x, y, z);
×
191
            }
192
        });
193

194
        codecActions.put(Map.class, new ICodecAction() {
1 ✔
195

196
            // Packet structure:
197
            // Map length (int)
198
            // --- end if length == 0
199
            // Key class name (UTF)
200
            // Value class name (UTF)
201
            // for length
202
            //   key
203
            //   value
204

205
            @SuppressWarnings({ "rawtypes", "unchecked" })
206
            @Override
207
            public void encode(Object object, FriendlyByteBuf output) {
208
                Map map = (Map) object;
1 ✔
209
                List<Map.Entry> entries = Lists.newArrayList(map.entrySet());
1 ✔
210
                output.writeInt(entries.size());
1 ✔
211
                ICodecAction keyAction = null;
1 ✔
212
                ICodecAction valueAction = null;
1 ✔
213
                for(Map.Entry entry : entries) {
1 ✔
214
                    if(keyAction == null) {
1 ✔
215
                        keyAction = getAction(entry.getKey().getClass());
1 ✔
216
                        output.writeUtf(entry.getKey().getClass().getName());
1 ✔
217
                    }
218
                    if(valueAction == null) {
1 ✔
219
                        valueAction = getAction(entry.getValue().getClass());
1 ✔
220
                        output.writeUtf(entry.getValue().getClass().getName());
1 ✔
221
                    }
222
                    keyAction.encode(entry.getKey(), output);
1 ✔
223
                    valueAction.encode(entry.getValue(), output);
1 ✔
224
                }
1 ✔
225
            }
1 ✔
226

227
            @SuppressWarnings({ "rawtypes", "unchecked" })
228
            @Override
229
            public Object decode(FriendlyByteBuf input) {
230
                Map map = Maps.newHashMap();
1 ✔
231
                int size = input.readInt();
1 ✔
232
                if(size == 0) {
1 ✔
233
                    return map;
1 ✔
234
                }
235
                // Each entry takes at least one byte
236
                if(size < 0 || size > input.readableBytes()) {
1 ✔
237
                    throw new DecoderException("Invalid map size: " + size);
1 ✔
238
                }
239
                ICodecAction keyAction = getActionForClassName(input.readUtf(READ_STRING_MAX_LENGTH));
1 ✔
240
                ICodecAction valueAction = getActionForClassName(input.readUtf(READ_STRING_MAX_LENGTH));
1 ✔
241
                for(int i = 0; i < size; i++) {
1 ✔
242
                    Object key = keyAction.decode(input);
1 ✔
243
                    Object value = valueAction.decode(input);
1 ✔
244
                    map.put(key, value);
1 ✔
245
                }
246
                return map;
1 ✔
247
            }
248
        });
249

250
        codecActions.put(CompoundTag.class, new ICodecAction() {
1 ✔
251

252
            @Override
253
            public void encode(Object object, FriendlyByteBuf output) {
254
                output.writeNbt((CompoundTag) object);
1 ✔
255
            }
1 ✔
256

257
            @Override
258
            public Object decode(FriendlyByteBuf input) {
259
                return input.readNbt();
1 ✔
260
            }
261
        });
262

263
        codecActions.put(Tag.class, new ICodecAction() {
1 ✔
264

265
            @Override
266
            public void encode(Object object, FriendlyByteBuf output) {
267
                CompoundTag tag = new CompoundTag();
×
268
                tag.put("v", (Tag) object);
×
269
                output.writeNbt(tag);
×
270
            }
×
271

272
            @Override
273
            public Object decode(FriendlyByteBuf input) {
274
                CompoundTag tag = input.readNbt();
×
275
                return tag.get("v");
×
276
            }
277
        });
278

279
        codecActions.put(ItemStack.class, new ICodecAction() {
1 ✔
280

281
            @Override
282
            public void encode(Object object, FriendlyByteBuf output) {
283
                output.writeItem((ItemStack) object);
×
284
            }
×
285

286
            @Override
287
            public Object decode(FriendlyByteBuf input) {
288
                return input.readItem();
×
289
            }
290
        });
291

292
        codecActions.put(FluidStack.class, new ICodecAction() {
1 ✔
293

294
            @Override
295
            public void encode(Object object, FriendlyByteBuf output) {
296
                output.writeNbt(((FluidStack) object).writeToNBT(new CompoundTag()));
×
297
            }
×
298

299
            @Override
300
            public Object decode(FriendlyByteBuf input) {
301
                return FluidStack.loadFluidStackFromNBT(input.readNbt());
×
302
            }
303
        });
304

305
        codecActions.put(Direction.class, new ICodecAction() {
1 ✔
306

307
            @Override
308
            public void encode(Object object, FriendlyByteBuf output) {
309
                output.writeInt(((Direction) object).ordinal());
1 ✔
310
            }
1 ✔
311

312
            @Override
313
            public Object decode(FriendlyByteBuf input) {
314
                return Direction.values()[input.readInt()];
1 ✔
315
            }
316
        });
317

318
        codecActions.put(BlockPos.class, new ICodecAction() {
1 ✔
319

320
            @Override
321
            public void encode(Object object, FriendlyByteBuf output) {
322
                output.writeBlockPos((BlockPos) object);
1 ✔
323
            }
1 ✔
324

325
            @Override
326
            public Object decode(FriendlyByteBuf input) {
327
                return input.readBlockPos();
1 ✔
328
            }
329
        });
330

331
        codecActions.put(ResourceKey.class, new ICodecAction() {
1 ✔
332

333
            @Override
334
            public void encode(Object object, FriendlyByteBuf output) {
335
                output.writeUtf(((ResourceKey) object).registry().toString());
×
336
                output.writeUtf(((ResourceKey) object).location().toString());
×
337
            }
×
338

339
            @Override
340
            public Object decode(FriendlyByteBuf input) {
341
                return ResourceKey.create(
×
342
                        ResourceKey.createRegistryKey(new ResourceLocation(input.readUtf(READ_STRING_MAX_LENGTH))),
×
343
                        new ResourceLocation(input.readUtf(READ_STRING_MAX_LENGTH)));
×
344
            }
345
        });
346

347
        codecActions.put(DimPos.class, new ICodecAction() {
1 ✔
348

349
            @Override
350
            public void encode(Object object, FriendlyByteBuf output) {
351
                write(output, ((DimPos) object).getLevelKey());
×
352
                write(output, ((DimPos) object).getBlockPos());
×
353
            }
×
354

355
            @Override
356
            public Object decode(FriendlyByteBuf input) {
357
                ResourceKey dimensionType = read(input, ResourceKey.class);
×
358
                BlockPos blockPos = read(input, BlockPos.class);
×
359
                return DimPos.of(dimensionType, blockPos);
×
360
            }
361
        });
362

363
        codecActions.put(List.class, new ICodecAction() {
1 ✔
364

365
            // Packet structure:
366
            // list length (int)
367
            // --- end if length == 0
368
            // Value class name (UTF)
369
            //         id + value
370
            // -1
371

372
            @Override
373
            public void encode(Object object, FriendlyByteBuf output) {
374
                List<?> list = (List<?>) object;
1 ✔
375
                output.writeInt(list.size());
1 ✔
376
                if(list.size() == 0) return;
1 ✔
377
                ICodecAction valueAction = null;
1 ✔
378
                for(int i = 0; i < list.size(); i++) {
1 ✔
379
                    Object value = list.get(i);
1 ✔
380
                    if(value != null) {
1 ✔
381
                        if (valueAction == null) {
1 ✔
382
                            valueAction = getAction(value.getClass());
1 ✔
383
                            output.writeUtf(value.getClass().getName());
1 ✔
384
                        }
385
                        output.writeInt(i);
1 ✔
386
                        valueAction.encode(value, output);
1 ✔
387
                    }
388
                }
389
                if(valueAction == null) {
1 ✔
390
                    output.writeUtf("__noclass");
1 ✔
391
                } else {
392
                    output.writeInt(-1);
1 ✔
393
                }
394
            }
1 ✔
395

396
            @SuppressWarnings({ "rawtypes", "unchecked" })
397
            @Override
398
            public Object decode(FriendlyByteBuf input) {
399
                int size = input.readInt();
1 ✔
400
                if(size == 0) {
1 ✔
401
                    return Collections.emptyList();
1 ✔
402
                }
403
                // Each non-null element takes at least one byte
404
                if(size < 0 || size > input.readableBytes() + MAX_LIST_NULL_ELEMENTS) {
1 ✔
405
                    throw new DecoderException("Invalid list size: " + size);
1 ✔
406
                }
407
                List list = Lists.newArrayListWithExpectedSize(Math.min(size, input.readableBytes()));
1 ✔
408
                String className = input.readUtf(READ_STRING_MAX_LENGTH);
1 ✔
409
                int nullElements;
410
                if(!className.equals("__noclass")) {
1 ✔
411
                    ICodecAction valueAction = getActionForClassName(className);
1 ✔
412
                    int i, currentLength = 0;
1 ✔
413
                    nullElements = 0;
1 ✔
414
                    while((i = input.readInt()) >= 0) {
1 ✔
415
                        if(i < currentLength || i >= size) {
1 ✔
416
                            throw new DecoderException("Invalid list index: " + i);
1 ✔
417
                        }
418
                        nullElements = checkListNullElements(nullElements + (i - currentLength));
1 ✔
419
                        while(currentLength < i) {
1 ✔
420
                            list.add(null);
1 ✔
421
                            currentLength++;
1 ✔
422
                        }
423
                        Object value = valueAction.decode(input);
1 ✔
424
                        list.add(value);
1 ✔
425
                        currentLength++;
1 ✔
426
                    }
1 ✔
427
                    checkListNullElements(nullElements + (size - currentLength));
1 ✔
428
                    while(currentLength < size) {
1 ✔
429
                        list.add(null);
1 ✔
430
                        currentLength++;
1 ✔
431
                    }
432
                } else {
1 ✔
433
                    checkListNullElements(size);
1 ✔
434
                    for (int i = 0; i < size; i++) {
1 ✔
435
                        list.add(null);
1 ✔
436
                    }
437
                }
438
                return list;
1 ✔
439
            }
440
        });
441

442
        codecActions.put(Pair.class, new ICodecAction() {
1 ✔
443

444
            @Override
445
            public void encode(Object object, FriendlyByteBuf output) {
446
                output.writeUtf(((Pair) object).getLeft().getClass().getName());
×
447
                output.writeUtf(((Pair) object).getRight().getClass().getName());
×
448
                write(output, ((Pair) object).getLeft());
×
449
                write(output, ((Pair) object).getRight());
×
450
            }
×
451

452
            @Override
453
            public Object decode(FriendlyByteBuf input) {
NEW
454
                ICodecAction keyAction = getActionForClassName(input.readUtf(READ_STRING_MAX_LENGTH));
×
NEW
455
                ICodecAction valueAction = getActionForClassName(input.readUtf(READ_STRING_MAX_LENGTH));
×
NEW
456
                Object key = keyAction.decode(input);
×
NEW
457
                Object value = valueAction.decode(input);
×
NEW
458
                return Pair.of(key, value);
×
459
            }
460
        });
461

462
        codecActions.put(ItemLocation.class, new ICodecAction() {
1 ✔
463

464
            @Override
465
            public void encode(Object object, FriendlyByteBuf output) {
466
                ItemLocation.writeToPacketBuffer(output, (ItemLocation) object);
×
467
            }
×
468

469
            @Override
470
            public Object decode(FriendlyByteBuf input) {
471
                return ItemLocation.readFromPacketBuffer(input);
×
472
            }
473
        });
474
    }
1 ✔
475

476
    /**
477
     * Register a new coded action.
478
     * @param clazz A class type.
479
     * @param action A codec action for the given type.
480
     */
481
    public static void addCodedAction(Class<?> clazz, ICodecAction action) {
482
        codecActions.put(clazz, action);
×
483
    }
×
484

485
    protected SingleCache<Void, List<Field>> fieldCache = new SingleCache<Void, List<Field>>(
1 ✔
486
            new SingleCache.ICacheUpdater<Void, List<Field>>() {
1 ✔
487

488
        @Override
489
        public List<Field> getNewValue(Void key) {
490
            List<Field> fieldList = Lists.newLinkedList();
1 ✔
491

492
            Class<?> clazz = PacketCodec.this.getClass();
1 ✔
493
            for (; clazz != PacketCodec.class && clazz != null; clazz = clazz.getSuperclass()) {
1 ✔
494
                Field[] fields = clazz.getDeclaredFields();
1 ✔
495

496
                // Sort this because the Java API tells us that getDeclaredFields()
497
                // does not deterministically define the order of the fields in the array.
498
                // Otherwise we might get nasty class cast exceptions when running in SMP.
499
                Arrays.sort(fields, new Comparator<Field>() {
1 ✔
500

501
                    @Override
502
                    public int compare(Field o1, Field o2) {
503
                        return o1.getName().compareTo(o2.getName());
1 ✔
504
                    }
505

506
                });
507

508
                for (final Field field : fields) {
1 ✔
509
                    if (field.isAnnotationPresent(CodecField.class)) {
1 ✔
510
                        fieldList.add(field);
1 ✔
511
                    }
512
                }
513
            }
514

515
            return fieldList;
1 ✔
516
        }
517

518
        @Override
519
        public boolean isKeyEqual(Void cacheKey, Void newKey) {
520
            return true;
×
521
        }
522

523
    });
524

525
    @Nullable
526
    protected static ICodecAction getActionSuper(Class<?> clazz) {
527
        if(ClassUtils.isPrimitiveWrapper(clazz)) {
1 ✔
528
            clazz = ClassUtils.wrapperToPrimitive(clazz);
1 ✔
529
        }
530
        ICodecAction action = codecActions.get(clazz);
1 ✔
531
        if(action == null) {
1 ✔
532
            for (Class<?> iface : clazz.getInterfaces()) {
1 ✔
533
                action = codecActions.get(iface);
1 ✔
534
                if (action != null) {
1 ✔
535
                    return action;
1 ✔
536
                }
537
            }
538
            Class<?> superClass = clazz.getSuperclass();
1 ✔
539
            if (superClass != null) {
1 ✔
540
                return getActionSuper(superClass);
1 ✔
541
            } else {
542
                return null;
1 ✔
543
            }
544
        }
545
        return action;
1 ✔
546
    }
547

548
    public static ICodecAction getAction(Class<?> clazz) {
549
        ICodecAction action = getActionSuper(clazz);
1 ✔
550
        if(action == null) {
1 ✔
551
            System.err.println("No ICodecAction was found for " + clazz
×
552
                    + ". You should add one in PacketCodec.");
553
        }
554
        return action;
1 ✔
555
    }
556

557
    /**
558
     * Get the codec action for a class name that was read from a packet.
559
     * The class is not initialized, and it must have a codec action.
560
     * @param className A class name.
561
     * @return The codec action.
562
     * @throws DecoderException If the class does not exist or has no codec action.
563
     */
564
    protected static ICodecAction getActionForClassName(String className) {
565
        Class<?> clazz;
566
        try {
567
            clazz = Class.forName(className, false, PacketCodec.class.getClassLoader());
1 ✔
568
        } catch (ClassNotFoundException | LinkageError e) {
1 ✔
569
            throw new DecoderException("Unknown class in packet: " + className, e);
1 ✔
570
        }
1 ✔
571
        ICodecAction action = getActionSuper(clazz);
1 ✔
572
        if(action == null) {
1 ✔
573
            throw new DecoderException("No ICodecAction was found for class in packet: " + className);
1 ✔
574
        }
575
        return action;
1 ✔
576
    }
577

578
    protected static int checkListNullElements(int nullElements) {
579
        if(nullElements > MAX_LIST_NULL_ELEMENTS) {
1 ✔
580
            throw new DecoderException("Too many null elements in list: " + nullElements);
1 ✔
581
        }
582
        return nullElements;
1 ✔
583
    }
584

585
    private void loopCodecFields(ICodecRunnable runnable) {
586
        for (Field field : fieldCache.get(null)) {
1 ✔
587
            Class<?> clazz = field.getType();
1 ✔
588
            ICodecAction action = getAction(clazz);
1 ✔
589

590
            // Make private fields temporarily accessible.
591
            boolean accessible = field.isAccessible();
1 ✔
592
            if (!accessible) {
1 ✔
593
                field.setAccessible(true);
1 ✔
594
            }
595
            runnable.run(field, action);
1 ✔
596
        }
1 ✔
597
    }
1 ✔
598

599
    @Override
600
    public void encode(final FriendlyByteBuf output) {
601
        loopCodecFields((field, action) -> {
1 ✔
602
            Object object = null;
1 ✔
603
            try {
604
                object = field.get(PacketCodec.this);
1 ✔
605
            } catch (IllegalArgumentException | IllegalAccessException e) {
×
606
                e.printStackTrace();
×
607
            }
1 ✔
608
            action.encode(object, output);
1 ✔
609
        });
1 ✔
610
    }
1 ✔
611

612
    @Override
613
    public void decode(final FriendlyByteBuf input) {
614
        loopCodecFields((field, action) -> {
1 ✔
615
            Object object = action.decode(input);
1 ✔
616
            try {
617
                field.set(PacketCodec.this, object);
1 ✔
618
            } catch (IllegalArgumentException | IllegalAccessException e) {
×
619
                e.printStackTrace();
×
620
            }
1 ✔
621
        });
1 ✔
622
    }
1 ✔
623

624
    /**
625
     * Write the given object into the packet buffer.
626
     * @param packetBuffer A packet buffer.
627
     * @param object An object.
628
     */
629
    public static void write(FriendlyByteBuf packetBuffer, Object object) {
630
        ICodecAction action = Objects.requireNonNull(getActionSuper(object.getClass()),
×
631
                "No codec action was registered for " + object.getClass().getName());
×
632
        action.encode(object, packetBuffer);
×
633
    }
×
634

635
    /**
636
     * Read the an object of the given type from the packet buffer.
637
     * @param packetBuffer A packet buffer.
638
     * @param clazz The class type to read.
639
     * @param <T> The type of object.
640
     * @return The read object.
641
     */
642
    public static <T> T read(FriendlyByteBuf packetBuffer, Class<T> clazz) {
643
        ICodecAction action = Objects.requireNonNull(getActionSuper(clazz));
×
644
        return (T) action.decode(packetBuffer);
×
645
    }
646

647
    public static interface ICodecAction {
648

649
        /**
650
         * Encode the given object.
651
         * @param object The object to encode into the output.
652
         * @param output The byte array to encode to.
653
         */
654
        public void encode(Object object, FriendlyByteBuf output);
655

656
        /**
657
         * Decode from the input.
658
         * @param input The byte array to decode from.
659
         * @return The object to return after reading it from the input.
660
         */
661
        public Object decode(FriendlyByteBuf input);
662

663
    }
664

665
    public static interface ICodecRunnable {
666

667
        /**
668
         * Run a type of codec.
669
         * @param field The field annotated with {@link CodecField}.
670
         * @param action The action that must be applied to the field.
671
         */
672
        public void run(Field field, ICodecAction action);
673

674
    }
675

676
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc