Fixed compatibility with synatra connector

This commit is contained in:
Sollace 2024-10-31 21:09:39 +00:00
parent b3a46b0035
commit 253ff329c7
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
17 changed files with 88 additions and 96 deletions

View file

@ -42,8 +42,8 @@ public record Race (
boolean canInteractWithClouds
) implements Affine {
public static final String DEFAULT_ID = "unicopia:unset";
public static final Registry<Race> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID);
public static final Registry<Race> COMMAND_REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race/grantable"), DEFAULT_ID);
public static final Registry<Race> REGISTRY = RegistryUtils.createSynced(Unicopia.id("race"), DEFAULT_ID);
public static final Registry<Race> COMMAND_REGISTRY = RegistryUtils.createSynced(Unicopia.id("race/grantable"), DEFAULT_ID);
public static final RegistryKey<? extends Registry<Race>> REGISTRY_KEY = REGISTRY.getKey();
private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("commands.race.fail", id));
private static final Function<Race, Composite> COMPOSITES = Util.memoize(race -> new Composite(race, null, null));

View file

@ -18,7 +18,7 @@ import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.Registry;
public interface Abilities {
Registry<Ability<?>> REGISTRY = RegistryUtils.createSimple(Unicopia.id("abilities"));
Registry<Ability<?>> REGISTRY = RegistryUtils.createSynced(Unicopia.id("abilities"));
PacketCodec<RegistryByteBuf, Ability<?>> PACKET_CODEC = PacketCodecs.registryValue(REGISTRY.getKey());
Map<AbilitySlot, Set<Ability<?>>> BY_SLOT = new EnumMap<>(AbilitySlot.class);
BiFunction<AbilitySlot, Race.Composite, List<Ability<?>>> BY_SLOT_AND_COMPOSITE_RACE = Util.memoize((slot, race) -> {

View file

@ -149,16 +149,14 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable {
}
@Override
public void read(ByteBuf buffer, WrapperLookup lookup) {
public void read(RegistryByteBuf buffer, WrapperLookup lookup) {
byte contentType = buffer.readByte();
if (contentType == 1) {
readTrackedNbt(PacketCodecs.NBT_COMPOUND.decode(buffer), lookup);
} else {
T spell = this.spell.get();
if (spell != null) {
spell.getDataTracker().load(MsgTrackedValues.TrackerEntries.PACKET_CODEC.decode(
buffer instanceof RegistryByteBuf r ? r : new RegistryByteBuf(buffer, owner.asEntity().getRegistryManager())
), lookup);
spell.getDataTracker().load(MsgTrackedValues.TrackerEntries.PACKET_CODEC.decode(buffer), buffer.getRegistryManager());
}
}
}

View file

@ -44,13 +44,14 @@ import net.minecraft.server.command.ServerCommandSource;
public final class SpellType<T extends Spell> implements Affine, SpellPredicate<T> {
public static final Identifier EMPTY_ID = Unicopia.id("none");
public static final SpellType<?> EMPTY_KEY = builder(t -> null).affinity(Affinity.NEUTRAL).color(0xFFFFFF).unobtainable().build(EMPTY_ID);
public static final Registry<SpellType<?>> REGISTRY = RegistryUtils.createSimple(Unicopia.id("spells"));
public static final Registry<SpellType<?>> REGISTRY = RegistryUtils.createSynced(Unicopia.id("spells"), EMPTY_ID.toString());
public static final RegistryKey<? extends Registry<SpellType<?>>> REGISTRY_KEY = REGISTRY.getKey();
public static final Codec<SpellType<?>> CODEC = REGISTRY.getCodec();
public static final PacketCodec<RegistryByteBuf, SpellType<?>> PACKET_CODEC = PacketCodecs.registryValue(REGISTRY_KEY);
public static final SpellType<?> EMPTY_KEY = register(EMPTY_ID.getPath(), builder(t -> null).affinity(Affinity.NEUTRAL).color(0xFFFFFF).unobtainable());
private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id));
public static final SpellType<PlacementControlSpell> PLACE_CONTROL_SPELL = register("place_controller", SpellType.<PlacementControlSpell>builder(PlacementControlSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT));

View file

@ -15,7 +15,7 @@ import net.minecraft.util.Util;
public record AfflictionType<T extends Affliction>(Identifier id, MapCodec<T> codec, PacketCodec<? super RegistryByteBuf, T> packetCodec) {
public static final String DEFAULT_ID = "unicopia:apply_status_effect";
public static final Registry<AfflictionType<?>> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("affliction_type"), DEFAULT_ID);
public static final Registry<AfflictionType<?>> REGISTRY = RegistryUtils.createSynced(Unicopia.id("affliction_type"), DEFAULT_ID);
public static final Codec<Affliction> CODEC = CodecUtils.apply(REGISTRY.getCodec()
.dispatch("type", Affliction::getType, AfflictionType::codec), elementCodec -> Codec
.withAlternative(elementCodec, Codec.list(elementCodec), afflictions -> {

View file

@ -10,7 +10,7 @@ import net.minecraft.fluid.Fluids;
import net.minecraft.registry.tag.FluidTags;
@Pseudo
@Mixin(targets = "net.minecraftforge.common.extensions.IForgeBoat")
@Mixin(targets = "net.neoforged.neoforge.common.extensions.IAbstractBoatExtension")
interface MixinIForgeBoat {
@ModifyVariable(
method = "canBoatInFluid(Lnet/minecraft/fluid/FluidState;)Z",

View file

@ -0,0 +1,15 @@
package com.minelittlepony.unicopia.mixin.forgified;
import java.util.function.Consumer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import net.minecraft.network.listener.ClientCommonPacketListener;
import net.minecraft.network.packet.Packet;
@Pseudo
@Mixin(targets = "net.neoforged.neoforge.network.bundle.PacketAndPayloadAcceptor")
interface MixinPacketAndPayloadAcceptor<L extends ClientCommonPacketListener> extends Consumer<Packet<? extends L>> {
}

View file

@ -6,13 +6,13 @@ import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Coerce;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.network.track.Trackable;
import net.minecraft.entity.Entity;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.server.network.EntityTrackerEntry;
import net.minecraft.server.network.ServerPlayerEntity;
@ -29,8 +29,12 @@ abstract class MixinEntityTrackerEntry {
Trackable.of(entity).getDataTrackers().tick(this::sendSyncPacket);
}
@Inject(method = "sendPackets", at = @At("RETURN"))
private void unicopia_onSendPackets(ServerPlayerEntity player, Consumer<Packet<ClientPlayPacketListener>> sender, CallbackInfo info) {
Trackable.of(entity).getDataTrackers().sendInitial(player, sender);
@SuppressWarnings("unchecked")
@Inject(method = "sendPackets", at = @At("RETURN"), require = 0)
private void unicopia_onSendPackets(ServerPlayerEntity player, @Coerce Object sender, CallbackInfo info) {
// NEO: Consumer<Packet<ClientPlayPacketListener>> replaced with PacketAndPayloadAcceptor<ClientPlayPacketListener>
if (sender instanceof Consumer s) {
Trackable.of(entity).getDataTrackers().sendInitial(player, s);
}
}
}

View file

@ -42,10 +42,14 @@ abstract class MixinServerPlayerEntity extends PlayerEntity implements ScreenHan
get().copyFrom(((Equine.Container<Pony>)oldPlayer).get(), alive);
}
@Inject(method = "trySleep(Lnet/minecraft/util/math/BlockPos;)Lcom/mojang/datafixers/util/Either;", at = @At(
@Inject(method = {
"trySleep(Lnet/minecraft/util/math/BlockPos;)Lcom/mojang/datafixers/util/Either;",
// NEO: https://github.com/neoforged/NeoForge/blob/62eb61a8e0c726faa30eaa3f4998ba1101368ee4/patches/net/minecraft/server/level/ServerPlayer.java.patch#L77
"lambda$0()Lcom/mojang/datafixers/util/Either;"
}, at = @At(
value = "FIELD",
target = "net/minecraft/entity/player/PlayerEntity$SleepFailureReason.NOT_POSSIBLE_NOW:Lnet/minecraft/entity/player/PlayerEntity$SleepFailureReason;"
), cancellable = true)
), cancellable = true, require = 0)
private void onTrySleep(BlockPos pos, CallbackInfoReturnable<Either<PlayerEntity.SleepFailureReason, Unit>> info) {
if (get().getSpecies().isNocturnal() && get().asWorld().getGameRules().getBoolean(UGameRules.DO_NOCTURNAL_BAT_PONIES)) {
((PlayerEntity)this).sendMessage(Text.translatable("block.unicopia.bed.no_sleep.nocturnal"), true);

View file

@ -9,13 +9,14 @@ import java.util.Optional;
import com.minelittlepony.unicopia.util.Untyped;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public class DataTracker {
@ -86,7 +87,7 @@ public class DataTracker {
return getInitialPairs(lookup);
}
Map<Integer, ByteBuf> updates = writePersistentObjects(lookup, false);
Map<Integer, byte[]> updates = writePersistentObjects(lookup, false);
if (dirtyIndices.isEmpty() && updates.isEmpty()) {
return Optional.empty();
@ -98,21 +99,21 @@ public class DataTracker {
for (int i : toSend) {
pairs.add(codecs.get(i));
}
return Optional.of(new MsgTrackedValues.TrackerEntries(id, false, pairs, Untyped.cast(updates)));
return Optional.of(new MsgTrackedValues.TrackerEntries(id, false, pairs, updates));
}
private Map<Integer, ByteBuf> writePersistentObjects(WrapperLookup lookup, boolean initial) {
Map<Integer, ByteBuf> updates = new HashMap<>();
private Map<Integer, byte[]> writePersistentObjects(WrapperLookup lookup, boolean initial) {
Map<Integer, byte[]> updates = new HashMap<>();
for (int i = 0; i < persistentObjects.size(); i++) {
TrackableObject<?> o = persistentObjects.get(i);
TrackableObject.Status status = initial ? TrackableObject.Status.NEW : o.getStatus();
int id = i;
o.write(status, lookup).ifPresent(data -> updates.put(id, data));
o.write(status, lookup).ifPresent(data -> updates.put(id, data.copy().array()));
}
return updates;
}
public synchronized void load(MsgTrackedValues.TrackerEntries values, WrapperLookup lookup) {
public synchronized void load(MsgTrackedValues.TrackerEntries values, DynamicRegistryManager lookup) {
if (values.wipe()) {
codecs.clear();
codecs.addAll(values.values());
@ -129,7 +130,7 @@ public class DataTracker {
for (var entry : values.objects().entrySet()) {
TrackableObject<?> o = persistentObjects.get(entry.getKey());
if (o != null) {
o.read(entry.getValue(), lookup);
o.read(new RegistryByteBuf(Unpooled.wrappedBuffer(entry.getValue()), lookup), lookup);
}
}
}

View file

@ -6,16 +6,17 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import com.minelittlepony.unicopia.network.Channel;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.entity.Entity;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.server.network.ServerPlayerEntity;
public class DataTrackerManager {
private final Entity entity;
private final WrapperLookup lookup;
private final DynamicRegistryManager lookup;
final boolean isClient;
private final List<DataTracker> trackers = new ObjectArrayList<>();
private final List<ObjectTracker<?>> objectTrackers = new ObjectArrayList<>();
@ -44,13 +45,11 @@ public class DataTrackerManager {
packetEmitters.add((sender, initial) -> {
var update = initial ? tracker.getInitialPairs(lookup) : tracker.getDirtyPairs(lookup);
if (update.isPresent()) {
try (var payload = new MsgTrackedValues(
sender.accept(Channel.SERVER_TRACKED_ENTITY_DATA.toPacket(new MsgTrackedValues(
entity.getId(),
Optional.empty(),
update
)) {
sender.accept(Channel.SERVER_TRACKED_ENTITY_DATA.toPacket(payload));
}
)));
}
});
return tracker;
@ -62,13 +61,11 @@ public class DataTrackerManager {
packetEmitters.add((sender, initial) -> {
var update = initial ? tracker.getInitialPairs(lookup) : tracker.getDirtyPairs(lookup);
if (update.isPresent()) {
try (var payload = new MsgTrackedValues(
sender.accept(Channel.SERVER_TRACKED_ENTITY_DATA.toPacket(new MsgTrackedValues(
entity.getId(),
update,
Optional.empty()
)) {
sender.accept(Channel.SERVER_TRACKED_ENTITY_DATA.toPacket(payload));
}
)));
}
});
return tracker;

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.network.track;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -10,10 +9,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import com.minelittlepony.unicopia.util.serialization.PacketCodecUtils;
import com.sollace.fabwork.api.packets.Handled;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.RegistryByteBuf;
@ -25,7 +22,7 @@ public record MsgTrackedValues(
int owner,
Optional<TrackerObjects> updatedObjects,
Optional<TrackerEntries> updatedTrackers
) implements Handled<PlayerEntity>, AutoCloseable {
) implements Handled<PlayerEntity> {
public static final PacketCodec<RegistryByteBuf, MsgTrackedValues> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, MsgTrackedValues::owner,
PacketCodecs.optional(TrackerObjects.PACKET_CODEC), MsgTrackedValues::updatedObjects,
@ -35,54 +32,28 @@ public record MsgTrackedValues(
@Override
public void handle(PlayerEntity sender) {
try (this) {
Entity entity = sender.getWorld().getEntityById(owner);
if (entity instanceof Trackable trackable) {
trackable.getDataTrackers().load(this);
}
}
}
public record TrackerObjects(int id, Set<UUID> removedValues, Map<UUID, ByteBuf> values) implements Closeable {
public record TrackerObjects(int id, Set<UUID> removedValues, Map<UUID, byte[]> values) {
public static final PacketCodec<RegistryByteBuf, TrackerObjects> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, TrackerObjects::id,
Uuids.PACKET_CODEC.collect(PacketCodecs.toCollection(HashSet::new)), TrackerObjects::removedValues,
PacketCodecs.map(HashMap::new, Uuids.PACKET_CODEC, PacketCodecUtils.REGISTRY_BUFFER), TrackerObjects::values,
PacketCodecs.map(HashMap::new, Uuids.PACKET_CODEC, PacketCodecs.BYTE_ARRAY), TrackerObjects::values,
TrackerObjects::new
);
@Override
public void close() {
values.values().forEach(buf -> {
if (buf.refCnt() > 0) {
buf.release();
}
});
}
}
public record TrackerEntries(int id, boolean wipe, List<DataTracker.Pair<?>> values, Map<Integer, ByteBuf> objects) implements Closeable {
public record TrackerEntries(int id, boolean wipe, List<DataTracker.Pair<?>> values, Map<Integer, byte[]> objects) {
public static final PacketCodec<RegistryByteBuf, TrackerEntries> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, TrackerEntries::id,
PacketCodecs.BOOL, TrackerEntries::wipe,
DataTracker.Pair.PACKET_CODEC.collect(PacketCodecs.toCollection(ArrayList::new)), TrackerEntries::values,
PacketCodecs.map(HashMap::new, PacketCodecs.INTEGER, PacketCodecUtils.REGISTRY_BUFFER), TrackerEntries::objects,
PacketCodecs.map(HashMap::new, PacketCodecs.INTEGER, PacketCodecs.BYTE_ARRAY), TrackerEntries::objects,
TrackerEntries::new
);
@Override
public void close() {
objects.values().forEach(buf -> {
if (buf.refCnt() > 0) {
buf.release();
}
});
}
}
@Override
public void close() {
updatedObjects.ifPresent(TrackerObjects::close);
updatedObjects.ifPresent(TrackerObjects::close);
}
}

View file

@ -12,8 +12,10 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.network.track.TrackableObject.Status;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public class ObjectTracker<T extends TrackableObject<T>> {
@ -89,10 +91,10 @@ public class ObjectTracker<T extends TrackableObject<T>> {
return Optional.empty();
}
Map<UUID, ByteBuf> updates = new HashMap<>();
Map<UUID, byte[]> updates = new HashMap<>();
quickAccess.entrySet().forEach(object -> {
object.getValue().write(Status.NEW, lookup).ifPresent(data -> {
updates.put(object.getKey(), data);
updates.put(object.getKey(), data.copy().array());
});
});
@ -101,7 +103,7 @@ public class ObjectTracker<T extends TrackableObject<T>> {
synchronized Optional<MsgTrackedValues.TrackerObjects> getDirtyPairs(WrapperLookup lookup) {
if (!trackedObjects.isEmpty()) {
Map<UUID, ByteBuf> updates = new HashMap<>();
Map<UUID, byte[]> updates = new HashMap<>();
Set<UUID> removedTrackableObjects = new HashSet<>();
trackedObjects.entrySet().removeIf(object -> {
TrackableObject.Status status = object.getValue().getStatus();
@ -110,7 +112,7 @@ public class ObjectTracker<T extends TrackableObject<T>> {
return true;
}
object.getValue().write(status, lookup).ifPresent(data -> {
updates.put(object.getKey(), data);
updates.put(object.getKey(), data.copy().array());
});
return false;
});
@ -124,7 +126,7 @@ public class ObjectTracker<T extends TrackableObject<T>> {
return Optional.empty();
}
synchronized void load(MsgTrackedValues.TrackerObjects objects, WrapperLookup lookup) {
synchronized void load(MsgTrackedValues.TrackerObjects objects, DynamicRegistryManager lookup) {
objects.removedValues().forEach(removedId -> {
T o = trackedObjects.remove(removedId);
if (o != null) {
@ -137,7 +139,8 @@ public class ObjectTracker<T extends TrackableObject<T>> {
o = constructor.get();
trackedObjects.put(id, o);
}
o.read(data, lookup);
o.read(new RegistryByteBuf(Unpooled.wrappedBuffer(data), lookup), lookup);
});
quickAccess = Map.copyOf(trackedObjects);
}

View file

@ -5,13 +5,14 @@ import java.util.Optional;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public interface TrackableObject<T extends TrackableObject<T>> {
Status getStatus();
default void read(ByteBuf buffer, WrapperLookup lookup) {
default void read(RegistryByteBuf buffer, WrapperLookup lookup) {
readTrackedNbt(PacketCodecs.NBT_COMPOUND.decode(buffer), lookup);
}

View file

@ -8,6 +8,7 @@ import com.google.common.base.Predicates;
import com.mojang.serialization.Lifecycle;
import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
import net.fabricmc.fabric.api.event.registry.RegistryAttribute;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
@ -22,8 +23,16 @@ public interface RegistryUtils {
return FabricRegistryBuilder.from(new SimpleRegistry<T>(RegistryKey.ofRegistry(id), Lifecycle.stable())).buildAndRegister();
}
static <T> Registry<T> createDefaulted(Identifier id, String def) {
return FabricRegistryBuilder.from(new SimpleDefaultedRegistry<T>(def, RegistryKey.ofRegistry(id), Lifecycle.stable(), false)).buildAndRegister();
static <T> Registry<T> createSynced(Identifier id) {
return FabricRegistryBuilder.from(new SimpleRegistry<T>(RegistryKey.ofRegistry(id), Lifecycle.stable()))
.attribute(RegistryAttribute.SYNCED)
.buildAndRegister();
}
static <T> Registry<T> createSynced(Identifier id, String def) {
return FabricRegistryBuilder.from(new SimpleDefaultedRegistry<T>(def, RegistryKey.ofRegistry(id), Lifecycle.stable(), false))
.attribute(RegistryAttribute.SYNCED)
.buildAndRegister();
}
static <T> RegistryEntryList<T> entriesForTag(World world, TagKey<T> key) {

View file

@ -9,9 +9,7 @@ import java.util.function.Function;
import java.util.function.IntFunction;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryKey;
@ -21,17 +19,6 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public interface PacketCodecUtils {
PacketCodec<RegistryByteBuf, ByteBuf> REGISTRY_BUFFER = PacketCodec.of((bytes, buffer) -> {
int length = bytes.readableBytes();
buffer.writeInt(length);
if (length > 0) {
buffer.writeBytes(bytes, 0, length);
}
}, buffer -> {
int length = buffer.readInt();
return new RegistryByteBuf(length > 0 ? buffer.readBytes(length) : Unpooled.EMPTY_BUFFER, buffer.getRegistryManager());
});
PacketCodec<PacketByteBuf, RegistryKey<?>> REGISTRY_KEY = PacketCodec.tuple(
Identifier.PACKET_CODEC, RegistryKey::getRegistry,
Identifier.PACKET_CODEC, RegistryKey::getValue,

View file

@ -85,7 +85,8 @@
"seasons.MixinFertilizableUtil",
"ad_astra.MixinApiHelper",
"ad_astra.MixinOxygenApi",
"forgified.MixinIForgeBoat"
"forgified.MixinIForgeBoat",
"forgified.MixinPacketAndPayloadAcceptor"
],
"client": [
"client.MixinAnimalModel",