Remade networking hooks

This commit is contained in:
Adrian Siekierka 2019-02-10 02:40:56 +01:00
parent cc6ae9d3cf
commit 403d0c5a96
32 changed files with 1013 additions and 105 deletions

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.event.network;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import java.util.Collection;
public interface C2SPacketTypeCallback {
static final Event<C2SPacketTypeCallback> REGISTERED = EventFactory.createArrayBacked(
C2SPacketTypeCallback.class,
(callbacks) -> (client, types) -> {
for (C2SPacketTypeCallback callback : callbacks) {
callback.accept(client, types);
}
}
);
static final Event<C2SPacketTypeCallback> UNREGISTERED = EventFactory.createArrayBacked(
C2SPacketTypeCallback.class,
(callbacks) -> (client, types) -> {
for (C2SPacketTypeCallback callback : callbacks) {
callback.accept(client, types);
}
}
);
void accept(PlayerEntity client, Collection<Identifier> types);
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.event.network;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import java.util.Collection;
public interface S2CPacketTypeCallback {
static final Event<S2CPacketTypeCallback> REGISTERED = EventFactory.createArrayBacked(
S2CPacketTypeCallback.class,
(callbacks) -> (types) -> {
for (S2CPacketTypeCallback callback : callbacks) {
callback.accept(types);
}
}
);
static final Event<S2CPacketTypeCallback> UNREGISTERED = EventFactory.createArrayBacked(
S2CPacketTypeCallback.class,
(callbacks) -> (types) -> {
for (S2CPacketTypeCallback callback : callbacks) {
callback.accept(types);
}
}
);
void accept(Collection<Identifier> types);
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.network;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.fabric.impl.network.ClientSidePacketRegistryImpl;
import net.minecraft.network.Packet;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
/**
* The client-side packet registry.
*
* It is used for:
*
* - registering client-side packet receivers (server -> client packets)
* - sending packets to the server (client -> server packets).
*/
public interface ClientSidePacketRegistry extends PacketRegistry {
static final ClientSidePacketRegistry INSTANCE = new ClientSidePacketRegistryImpl();
/**
* Check if the server declared the ability to receive a given packet ID
* using the vanilla "register/unregister" protocol.
*
* @param id The packet identifier.
* @return True if the server side declared a given packet identifier.
*/
boolean canServerReceive(Identifier id);
/**
* Send a packet to the server.
* @param packet The packet to be sent.
* @param completionListener Completion listener. Can be used to check for
* the success or failure of sending a given packet, among others.
*/
void sendToServer(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> completionListener);
/**
* Send an identifier/buffer-based packet to the server.
* @param id The packet identifier.
* @param buf The packet byte buffer.
* @param completionListener Completion listener. Can be used to check for
* the success or failure of sending a given packet, among others.
*/
default void sendToServer(Identifier id, PacketByteBuf buf, GenericFutureListener<? extends Future<? super Void>> completionListener) {
sendToServer(toPacket(id, buf), completionListener);
}
/**
* Send a packet to the server.
* @param packet The packet to be sent.
*/
default void sendToServer(Packet<?> packet) {
sendToServer(packet, null);
}
/**
* Send an identifier/buffer-based packet to the server.
* @param id The packet identifier.
* @param buf The packet byte buffer.
*/
default void sendToServer(Identifier id, PacketByteBuf buf) {
sendToServer(id, buf, null);
}
}

View file

@ -1,78 +0,0 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.network;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* Registry for CustomPayload-based packet handling. You can use this
* to register your own CustomPayload packet handlers.
*/
public class CustomPayloadPacketRegistry {
public static final CustomPayloadPacketRegistry CLIENT = new CustomPayloadPacketRegistry();
public static final CustomPayloadPacketRegistry SERVER = new CustomPayloadPacketRegistry();
protected final Map<Identifier, BiConsumer<PacketContext, PacketByteBuf>> consumerMap;
protected CustomPayloadPacketRegistry() {
consumerMap = new LinkedHashMap<>();
}
/**
* Register a packet.
*
* @param id The packet Identifier.
* @param consumer The method used for handling the packet.
*/
public void register(Identifier id, BiConsumer<PacketContext, PacketByteBuf> consumer) {
if (consumerMap.containsKey(id)) {
// TODO: log warning
}
consumerMap.put(id, consumer);
}
/**
* Hook for accepting packets used in Fabric mixins.
*
* @param id The packet Identifier received.
* @param context The packet context provided.
* @param buf The packet data buffer received.
* @return Whether or not the packet was handled by this packet registry.
*/
public boolean accept(Identifier id, PacketContext context, PacketByteBuf buf) {
BiConsumer<PacketContext, PacketByteBuf> consumer = consumerMap.get(id);
if (consumer != null) {
try {
consumer.accept(context, buf);
} catch (Throwable t) {
// TODO: handle better
t.printStackTrace();
}
return true;
} else {
return false;
}
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.network;
import net.minecraft.util.PacketByteBuf;
/**
* Interface for receiving CustomPayload-based packets.
*/
@FunctionalInterface
public interface PacketConsumer {
/**
* Receive a CustomPayload-based packet.
*
* Please keep in mind that this CAN be called OUTSIDE of the main thread!
* Most game operations are not thread-safe, so you should look into using
* the thread task queue ({@link PacketContext#getTaskQueue()}) to split
* the "reading" (which should, but does not have to, happen off-thread)
* and "applying" (which, unless you know what you're doing, should happen
* on the main thread).
*
* @param context The context (receiving player, side, etc.)
* @param buffer The byte buffer containing the received packet data.
*/
void accept(PacketContext context, PacketByteBuf buffer);
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.network;
import net.minecraft.network.Packet;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public interface PacketRegistry {
/**
* Turn a (identifier, byte buffer) pair into a "custom payload" packet
* suitable for sending in the PacketRegistry's sending direction.
*
* @param id The identifier.
* @param buf The byte buffer.
* @return
*/
Packet<?> toPacket(Identifier id, PacketByteBuf buf);
/**
* Register a packet.
*
* @param id The packet Identifier.
* @param consumer The method used for handling the packet.
*/
void register(Identifier id, PacketConsumer consumer);
/**
* Unregister a packet.
*
* @param id The packet Identifier.
*/
void unregister(Identifier id);
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.network;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.fabric.api.server.PlayerStream;
import net.fabricmc.fabric.impl.network.ServerSidePacketRegistryImpl;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.Packet;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
/**
* The server-side packet registry.
*
* It is used for:
*
* - registering server-side packet receivers (client -> server packets)
* - sending packets to clients (server -> client packets).
*
* For iterating over clients in a server, see {@link PlayerStream}.
*/
public interface ServerSidePacketRegistry extends PacketRegistry {
static final ServerSidePacketRegistry INSTANCE = new ServerSidePacketRegistryImpl();
/**
* Check if a given client declared the ability to receive a given packet ID
* using the vanilla "register/unregister" protocol.
*
* @param id The packet identifier.
* @return True if the client side declared a given packet identifier.
*/
boolean canPlayerReceive(PlayerEntity player, Identifier id);
/**
* Send a packet to a given client.
*
* @param player The given client.
* @param packet The packet to be sent.
* @param completionListener Completion listener. Can be used to check for
* the success or failure of sending a given packet, among others.
*/
void sendToPlayer(PlayerEntity player, Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> completionListener);
/**
* Send an identifier/buffer-based packet to a given client.
*
* @param player The given client.
* @param id The packet identifier.
* @param buf The packet byte buffer.
* @param completionListener Completion listener. Can be used to check for
* the success or failure of sending a given packet, among others.
*/
default void sendToPlayer(PlayerEntity player, Identifier id, PacketByteBuf buf, GenericFutureListener<? extends Future<? super Void>> completionListener) {
sendToPlayer(player, toPacket(id, buf), completionListener);
}
/**
* Send a packet to a given client.
*
* @param player The given client.
* @param packet The packet to be sent.
*/
default void sendToPlayer(PlayerEntity player, Packet<?> packet) {
sendToPlayer(player, packet, null);
}
/**
* Send an identifier/buffer-based packet to a given client.
*
* @param player The given client.
* @param id The packet identifier.
* @param buf The packet byte buffer.
*/
default void sendToPlayer(PlayerEntity player, Identifier id, PacketByteBuf buf) {
sendToPlayer(player, id, buf, null);
}
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.server;
import net.fabricmc.fabric.impl.server.EntityTrackerStreamAccessor;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.EntityTracker;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.chunk.ChunkPos;
import java.util.stream.Stream;
/**
* Helper streams for looking up players on a server.
*
* In general, most of these methods will only function with a {@link ServerWorld} instance.
*/
public final class PlayerStream {
private PlayerStream() {
}
public static Stream<ServerPlayerEntity> all(MinecraftServer server) {
return server.getPlayerManager().getPlayerList().stream();
}
public static Stream<PlayerEntity> world(World world) {
return world.players.stream();
}
public static Stream<PlayerEntity> watching(World world, ChunkPos pos) {
if (!(world instanceof ServerWorld)) {
throw new RuntimeException("Only supported on ServerWorld!");
} else {
// noinspection unchecked
return ((Stream<PlayerEntity>) (Stream) ((ServerWorld) world).getChunkManager().getPlayersWatchingChunk(pos, false, false));
}
}
/**
* Warning: If the provided entity is a PlayerEntity themselves, it is not
* guaranteed by the contract that said PlayerEntity is included in the
* resulting stream.
*/
@SuppressWarnings("JavaDoc")
public static Stream<PlayerEntity> watching(Entity entity) {
World world = entity.getEntityWorld();
if (world instanceof ServerWorld) {
EntityTracker tracker = ((ServerWorld) world).getEntityTracker();
if (tracker instanceof EntityTrackerStreamAccessor) {
//noinspection unchecked
return ((Stream<PlayerEntity>) (Stream) ((EntityTrackerStreamAccessor) tracker).fabric_getTrackingPlayers(entity));
}
}
// fallback
return watching(world, new ChunkPos((int) (entity.x / 16.0D), (int) (entity.z / 16.0D)));
}
public static Stream<PlayerEntity> watching(BlockEntity entity) {
return watching(entity.getWorld(), entity.getPos());
}
public static Stream<PlayerEntity> watching(World world, BlockPos pos) {
return watching(world, new ChunkPos(pos));
}
public static Stream<PlayerEntity> around(World world, Vec3d vector, double radius) {
double radiusSq = radius * radius;
return world(world).filter((p) -> p.squaredDistanceTo(vector) <= radiusSq);
}
public static Stream<PlayerEntity> around(World world, BlockPos pos, double radius) {
double radiusSq = radius * radius;
return world(world).filter((p) -> p.squaredDistanceToCenter(pos) <= radiusSq);
}
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.tags;
package net.fabricmc.fabric.api.tag;
import net.minecraft.block.Block;
import net.minecraft.tag.Tag;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.tags;
package net.fabricmc.fabric.api.tag;
import net.minecraft.item.Item;
import net.minecraft.tag.Tag;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.tags;
package net.fabricmc.fabric.api.tag;
import net.minecraft.tag.Tag;
import net.minecraft.util.Identifier;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package net.fabricmc.fabric.api.tags;
package net.fabricmc.fabric.api.tag;
import net.minecraft.block.Block;
import net.minecraft.item.Item;

View file

@ -17,15 +17,15 @@
package net.fabricmc.fabric.impl;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.fabricmc.fabric.impl.client.gui.ScreenProviderRegistryImpl;
import net.fabricmc.fabric.api.network.CustomPayloadPacketRegistry;
import net.fabricmc.fabric.impl.registry.RegistrySyncManager;
import net.minecraft.client.MinecraftClient;
public class FabricAPIClientInitializer implements ClientModInitializer {
@Override
public void onInitializeClient() {
CustomPayloadPacketRegistry.CLIENT.register(RegistrySyncManager.ID, (ctx, buf) -> {
ClientSidePacketRegistry.INSTANCE.register(RegistrySyncManager.ID, (ctx, buf) -> {
// if not hosting server, apply packet
RegistrySyncManager.receivePacket(ctx, buf, !MinecraftClient.getInstance().isInSingleplayer());
});

View file

@ -19,8 +19,9 @@ package net.fabricmc.fabric.impl.client.gui;
import net.fabricmc.fabric.api.client.screen.ContainerScreenFactory;
import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
import net.fabricmc.fabric.api.container.ContainerFactory;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
import net.fabricmc.fabric.api.network.CustomPayloadPacketRegistry;
import net.fabricmc.fabric.impl.network.PacketTypes;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.ContainerScreen;
import net.minecraft.container.Container;
@ -40,7 +41,6 @@ public class ScreenProviderRegistryImpl implements ScreenProviderRegistry {
private static final Logger LOGGER = LogManager.getLogger();
private static final Identifier OPEN_CONTAINER = new Identifier("fabric", "open_container");
private static final Map<Identifier, ContainerFactory<ContainerScreen>> FACTORIES = new HashMap<>();
public void registerFactory(Identifier identifier, ContainerFactory<ContainerScreen> factory) {
@ -63,7 +63,7 @@ public class ScreenProviderRegistryImpl implements ScreenProviderRegistry {
}
public void init() {
CustomPayloadPacketRegistry.CLIENT.register(OPEN_CONTAINER, (packetContext, packetByteBuf) -> {
ClientSidePacketRegistry.INSTANCE.register(PacketTypes.OPEN_CONTAINER, (packetContext, packetByteBuf) -> {
Identifier identifier = packetByteBuf.readIdentifier();
int syncId = packetByteBuf.readUnsignedByte();
MinecraftClient.getInstance().execute(() -> {

View file

@ -19,6 +19,7 @@ package net.fabricmc.fabric.impl.container;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.container.ContainerFactory;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.fabricmc.fabric.impl.network.PacketTypes;
import net.minecraft.client.network.packet.CustomPayloadClientPacket;
import net.minecraft.container.Container;
import net.minecraft.entity.player.PlayerEntity;
@ -41,7 +42,6 @@ public class ContainerProviderImpl implements ContainerProviderRegistry {
private static final Logger LOGGER = LogManager.getLogger();
private static final Identifier OPEN_CONTAINER = new Identifier("fabric", "open_container");
private static final Map<Identifier, ContainerFactory<Container>> FACTORIES = new HashMap<>();
@Override
@ -71,7 +71,7 @@ public class ContainerProviderImpl implements ContainerProviderRegistry {
buf.writeByte(syncId);
writer.accept(buf);
player.networkHandler.sendPacket(new CustomPayloadClientPacket(OPEN_CONTAINER, buf));
player.networkHandler.sendPacket(new CustomPayloadClientPacket(PacketTypes.OPEN_CONTAINER, buf));
PacketByteBuf clonedBuf = new PacketByteBuf(buf.duplicate());
clonedBuf.readIdentifier();
@ -91,6 +91,7 @@ public class ContainerProviderImpl implements ContainerProviderRegistry {
LOGGER.error("No container factory found for {}!", identifier.toString());
return null;
}
//noinspection unchecked
return (C) factory.create(syncId, identifier, player, buf);
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.network;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.fabric.api.event.network.S2CPacketTypeCallback;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.Packet;
import net.minecraft.server.network.packet.CustomPayloadServerPacket;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
public class ClientSidePacketRegistryImpl extends PacketRegistryImpl implements ClientSidePacketRegistry {
private final Collection<Identifier> serverPayloadIds = new HashSet<>();
public static void invalidateRegisteredIdList() {
((ClientSidePacketRegistryImpl) ClientSidePacketRegistry.INSTANCE).serverPayloadIds.clear();
}
@Override
public boolean canServerReceive(Identifier id) {
return serverPayloadIds.contains(id);
}
@Override
public void sendToServer(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> completionListener) {
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
if (handler != null) {
if (completionListener == null) {
// stay closer to the vanilla codepath
handler.sendPacket(packet);
} else {
handler.getClientConnection().sendPacket(packet, completionListener);
}
} else {
// TODO: log warning
}
}
@Override
public Packet<?> toPacket(Identifier id, PacketByteBuf buf) {
return new CustomPayloadServerPacket(id, buf);
}
@Override
protected void onRegister(Identifier id) {
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
if (handler != null) {
handler.sendPacket(createRegisterTypePacket(PacketTypes.REGISTER, Collections.singleton(id)));
}
}
@Override
protected void onUnregister(Identifier id) {
ClientPlayNetworkHandler handler = MinecraftClient.getInstance().getNetworkHandler();
if (handler != null) {
handler.sendPacket(createRegisterTypePacket(PacketTypes.UNREGISTER, Collections.singleton(id)));
}
}
@Override
protected Collection<Identifier> getIdCollectionFor(PacketContext context) {
return serverPayloadIds;
}
@Override
protected void onReceivedRegisterPacket(PacketContext context, Collection<Identifier> ids) {
S2CPacketTypeCallback.REGISTERED.invoker().accept(ids);
}
@Override
protected void onReceivedUnregisterPacket(PacketContext context, Collection<Identifier> ids) {
S2CPacketTypeCallback.UNREGISTERED.invoker().accept(ids);
}
}

View file

@ -23,7 +23,7 @@ import net.minecraft.util.PacketByteBuf;
* Helper interface containing getters for SPacketCustomPayload
* which were omitted from the compiled game.
*/
public interface SPacketCustomPayloadAccessor {
public interface CustomPayloadC2SPacketAccessor {
Identifier getChannel();
PacketByteBuf getData();
}

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.network;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.network.PacketConsumer;
import net.fabricmc.fabric.api.network.PacketContext;
import net.fabricmc.fabric.api.network.PacketRegistry;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.minecraft.network.Packet;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import java.util.*;
public abstract class PacketRegistryImpl implements PacketRegistry {
protected final Map<Identifier, PacketConsumer> consumerMap;
PacketRegistryImpl() {
consumerMap = new LinkedHashMap<>();
}
public static Packet<?> createInitialRegisterPacket(PacketRegistry registry) {
PacketRegistryImpl impl = (PacketRegistryImpl) registry;
return impl.createRegisterTypePacket(PacketTypes.REGISTER, impl.consumerMap.keySet());
}
@Override
public void register(Identifier id, PacketConsumer consumer) {
boolean isNew = true;
if (consumerMap.containsKey(id)) {
// TODO: log warning
isNew = false;
}
consumerMap.put(id, consumer);
if (isNew) {
onRegister(id);
}
}
@Override
public void unregister(Identifier id) {
consumerMap.remove(id);
onUnregister(id);
}
protected abstract void onRegister(Identifier id);
protected abstract void onUnregister(Identifier id);
protected abstract Collection<Identifier> getIdCollectionFor(PacketContext context);
protected abstract void onReceivedRegisterPacket(PacketContext context, Collection<Identifier> ids);
protected abstract void onReceivedUnregisterPacket(PacketContext context, Collection<Identifier> ids);
protected Packet<?> createRegisterTypePacket(Identifier id, Collection<Identifier> ids) {
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
for (Identifier a : ids) {
buf.writeString(a.toString());
}
return toPacket(id, buf);
}
/**
* Hook for accepting packets used in Fabric mixins.
*
* @param id The packet Identifier received.
* @param context The packet context provided.
* @param buf The packet data buffer received.
* @return Whether or not the packet was handled by this packet registry.
*/
public boolean accept(Identifier id, PacketContext context, PacketByteBuf buf) {
if (id.equals(PacketTypes.REGISTER) || id.equals(PacketTypes.UNREGISTER)) {
Collection<Identifier> ids = new HashSet<>();
while (buf.readerIndex() < buf.writerIndex() /* TODO: check correctness */) {
Identifier newId = new Identifier(buf.readString(32767));
ids.add(newId);
}
Collection<Identifier> target = getIdCollectionFor(context);
if (id.equals(PacketTypes.UNREGISTER)) {
target.removeAll(ids);
onReceivedUnregisterPacket(context, ids);
} else {
target.addAll(ids);
onReceivedRegisterPacket(context, ids);
}
return false; // continue execution for other mods
}
PacketConsumer consumer = consumerMap.get(id);
if (consumer != null) {
try {
consumer.accept(context, buf);
} catch (Throwable t) {
// TODO: handle better
t.printStackTrace();
}
return true;
} else {
return false;
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.network;
import net.minecraft.util.Identifier;
public final class PacketTypes {
static final Identifier BRAND = new Identifier("minecraft:brand");
static final Identifier REGISTER = new Identifier("minecraft:register");
static final Identifier UNREGISTER = new Identifier("minecraft:unregister");
public static final Identifier OPEN_CONTAINER = new Identifier("fabric", "container/open");
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.network;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.fabricmc.fabric.api.event.network.C2SPacketTypeCallback;
import net.fabricmc.fabric.api.network.PacketContext;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.fabricmc.fabric.api.server.PlayerStream;
import net.fabricmc.loader.FabricLoader;
import net.minecraft.client.network.packet.CustomPayloadClientPacket;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.Packet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.WeakHashMap;
public class ServerSidePacketRegistryImpl extends PacketRegistryImpl implements ServerSidePacketRegistry {
private final WeakHashMap<PlayerEntity, Collection<Identifier>> playerPayloadIds = new WeakHashMap<>();
@Override
public boolean canPlayerReceive(PlayerEntity player, Identifier id) {
Collection<Identifier> ids = playerPayloadIds.get(player);
if (ids != null) {
return ids.contains(id);
} else {
return false;
}
}
@Override
public void sendToPlayer(PlayerEntity player, Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> completionListener) {
if (!(player instanceof ServerPlayerEntity)) {
throw new RuntimeException("Can only send to ServerPlayerEntities!");
} else {
((ServerPlayerEntity) player).networkHandler.sendPacket(packet, completionListener);
}
}
@Override
public Packet<?> toPacket(Identifier id, PacketByteBuf buf) {
return new CustomPayloadClientPacket(id, buf);
}
@Override
protected void onRegister(Identifier id) {
MinecraftServer server = FabricLoader.INSTANCE.getEnvironmentHandler().getServerInstance();
if (server != null) {
Packet<?> packet = createRegisterTypePacket(PacketTypes.REGISTER, Collections.singleton(id));
PlayerStream.all(server).forEach((p) -> sendToPlayer(p, packet));
}
}
@Override
protected void onUnregister(Identifier id) {
MinecraftServer server = FabricLoader.INSTANCE.getEnvironmentHandler().getServerInstance();
if (server != null) {
Packet<?> packet = createRegisterTypePacket(PacketTypes.UNREGISTER, Collections.singleton(id));
PlayerStream.all(server).forEach((p) -> sendToPlayer(p, packet));
}
}
@Override
protected Collection<Identifier> getIdCollectionFor(PacketContext context) {
return playerPayloadIds.computeIfAbsent(context.getPlayer(), (p) -> new HashSet<>());
}
@Override
protected void onReceivedRegisterPacket(PacketContext context, Collection<Identifier> ids) {
C2SPacketTypeCallback.REGISTERED.invoker().accept(context.getPlayer(), ids);
}
@Override
protected void onReceivedUnregisterPacket(PacketContext context, Collection<Identifier> ids) {
C2SPacketTypeCallback.UNREGISTERED.invoker().accept(context.getPlayer(), ids);
}
}

View file

@ -21,8 +21,9 @@ import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.client.network.packet.CustomPayloadClientPacket;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.Packet;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.util.registry.IdRegistry;
@ -43,12 +44,11 @@ public final class RegistrySyncManager {
}
public static CustomPayloadClientPacket createPacket() {
public static Packet<?> createPacket() {
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
buf.writeCompoundTag(toTag(true));
CustomPayloadClientPacket packet = new CustomPayloadClientPacket(ID, buf);
return packet;
return ServerSidePacketRegistry.INSTANCE.toPacket(ID, buf);
}
public static void receivePacket(PacketContext context, PacketByteBuf buf, boolean accept) {
@ -120,4 +120,13 @@ public final class RegistrySyncManager {
}
}
}
public static void unmap() throws RemapException {
for (Identifier registryId : Registry.REGISTRIES.keys()) {
ModifiableRegistry registry = Registry.REGISTRIES.get(registryId);
if (registry instanceof RemappableRegistry) {
((RemappableRegistry) registry).unmap();
}
}
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.server;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.stream.Stream;
public interface EntityTrackerEntryStreamAccessor {
Stream<ServerPlayerEntity> fabric_getTrackingPlayers();
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.server;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.stream.Stream;
public interface EntityTrackerStreamAccessor {
Stream<ServerPlayerEntity> fabric_getTrackingPlayers(Entity entity);
}

View file

@ -17,16 +17,25 @@
package net.fabricmc.fabric.mixin.entity;
import net.fabricmc.fabric.api.entity.EntityTrackingRegistry;
import net.fabricmc.fabric.impl.server.EntityTrackerEntryStreamAccessor;
import net.fabricmc.fabric.impl.server.EntityTrackerStreamAccessor;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.EntityTracker;
import net.minecraft.server.network.EntityTrackerEntry;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.IntHashMap;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.stream.Stream;
@Mixin(EntityTracker.class)
public abstract class MixinEntityTracker {
public abstract class MixinEntityTracker implements EntityTrackerStreamAccessor {
@Shadow
private IntHashMap<EntityTrackerEntry> trackedEntitiesById;
@Shadow
public abstract void add(Entity var1, int var2, int var3, boolean var4);
@ -40,4 +49,14 @@ public abstract class MixinEntityTracker {
}
}
}
@Override
public Stream<ServerPlayerEntity> fabric_getTrackingPlayers(Entity entity) {
EntityTrackerEntry entry = trackedEntitiesById.get(entity.getEntityId());
if (entry != null) {
return ((EntityTrackerEntryStreamAccessor) entry).fabric_getTrackingPlayers();
} else {
return Stream.empty();
}
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.entity;
import net.fabricmc.fabric.impl.server.EntityTrackerEntryStreamAccessor;
import net.minecraft.server.network.EntityTrackerEntry;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Set;
import java.util.stream.Stream;
@Mixin(EntityTrackerEntry.class)
public class MixinEntityTrackerEntry implements EntityTrackerEntryStreamAccessor {
@Shadow
private Set<ServerPlayerEntity> trackingPlayers;
@Override
public Stream<ServerPlayerEntity> fabric_getTrackingPlayers() {
return trackingPlayers.stream();
}
}

View file

@ -16,13 +16,23 @@
package net.fabricmc.fabric.mixin.networking;
import com.mojang.authlib.GameProfile;
import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.network.CustomPayloadPacketRegistry;
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
import net.fabricmc.fabric.api.network.PacketContext;
import net.fabricmc.fabric.impl.network.ClientSidePacketRegistryImpl;
import net.fabricmc.fabric.impl.network.PacketRegistryImpl;
import net.minecraft.class_2901;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Screen;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.packet.CustomPayloadClientPacket;
import net.minecraft.client.network.packet.GameJoinClientPacket;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.Packet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ThreadTaskQueue;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -31,13 +41,21 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPlayNetworkHandler.class)
public class MixinClientPlayNetworkHandler implements PacketContext {
public abstract class MixinClientPlayNetworkHandler implements PacketContext {
@Shadow
private MinecraftClient client;
@Shadow
public abstract void sendPacket(Packet<?> var1);
@Inject(at = @At("RETURN"), method = "onGameJoin")
public void onGameJoin(GameJoinClientPacket packet, CallbackInfo info) {
sendPacket(PacketRegistryImpl.createInitialRegisterPacket(ClientSidePacketRegistry.INSTANCE));
}
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
public void onCustomPayload(CustomPayloadClientPacket packet, CallbackInfo info) {
if (CustomPayloadPacketRegistry.CLIENT.accept(packet.getChannel(), this, packet.getData())) {
if (((ClientSidePacketRegistryImpl) ClientSidePacketRegistry.INSTANCE).accept(packet.getChannel(), this, packet.getData())) {
info.cancel();
}
}

View file

@ -16,7 +16,7 @@
package net.fabricmc.fabric.mixin.networking;
import net.fabricmc.fabric.impl.network.SPacketCustomPayloadAccessor;
import net.fabricmc.fabric.impl.network.CustomPayloadC2SPacketAccessor;
import net.minecraft.server.network.packet.CustomPayloadServerPacket;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
@ -24,7 +24,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(CustomPayloadServerPacket.class)
public class MixinSPacketCustomPayload implements SPacketCustomPayloadAccessor {
public class MixinCustomPayloadC2SPacket implements CustomPayloadC2SPacketAccessor {
@Shadow
private Identifier channel;
@Shadow

View file

@ -17,9 +17,10 @@
package net.fabricmc.fabric.mixin.networking;
import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.network.CustomPayloadPacketRegistry;
import net.fabricmc.fabric.api.network.PacketContext;
import net.fabricmc.fabric.impl.network.SPacketCustomPayloadAccessor;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.fabricmc.fabric.impl.network.CustomPayloadC2SPacketAccessor;
import net.fabricmc.fabric.impl.network.ServerSidePacketRegistryImpl;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayNetworkHandler;
@ -41,9 +42,9 @@ public class MixinServerPlayNetworkHandler implements PacketContext {
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
public void onCustomPayload(CustomPayloadServerPacket packet, CallbackInfo info) {
SPacketCustomPayloadAccessor accessor = ((SPacketCustomPayloadAccessor) packet);
CustomPayloadC2SPacketAccessor accessor = ((CustomPayloadC2SPacketAccessor) packet);
if (CustomPayloadPacketRegistry.SERVER.accept(accessor.getChannel(), this, accessor.getData())) {
if (((ServerSidePacketRegistryImpl) ServerSidePacketRegistry.INSTANCE).accept(accessor.getChannel(), this, accessor.getData())) {
info.cancel();
}
}

View file

@ -16,6 +16,9 @@
package net.fabricmc.fabric.mixin.registry;
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
import net.fabricmc.fabric.impl.network.PacketRegistryImpl;
import net.fabricmc.fabric.impl.network.ServerSidePacketRegistryImpl;
import net.fabricmc.fabric.impl.registry.RegistrySyncManager;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.Packet;
@ -36,6 +39,8 @@ public abstract class MixinServerPlayNetworkHandler {
@Inject(method = "<init>", at = @At("RETURN"))
public void init(MinecraftServer server, ClientConnection connection, ServerPlayerEntity player, CallbackInfo info) {
// TODO: If integrated and local, don't send the packet (it's ignored)
// TODO: Refactor out into network + move registry hook to event
sendPacket(PacketRegistryImpl.createInitialRegisterPacket(ServerSidePacketRegistry.INSTANCE));
sendPacket(RegistrySyncManager.createPacket());
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016, 2017, 2018 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.registry.client;
import net.fabricmc.fabric.impl.network.ClientSidePacketRegistryImpl;
import net.fabricmc.fabric.impl.registry.RegistrySyncManager;
import net.fabricmc.fabric.impl.registry.RemapException;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Screen;
import org.apache.logging.log4j.Logger;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public class MixinMinecraftClient {
@Shadow
private static Logger LOGGER;
// Unmap the registry before loading a new SP/MP setup.
@Inject(at = @At("RETURN"), method = "method_18096")
public void method_18096(Screen screen_1, CallbackInfo info) {
ClientSidePacketRegistryImpl.invalidateRegisteredIdList();
try {
RegistrySyncManager.unmap();
} catch (RemapException e) {
LOGGER.warn("Failed to unmap Fabric registries!", e);
}
}
}

View file

@ -21,6 +21,7 @@
"registry.client.MixinBlockColorMap",
"registry.client.MixinItemColorMap",
"registry.client.MixinItemModelMap",
"registry.client.MixinMinecraftClient",
"registry.client.MixinParticleManager",
"resources.MixinKeyedResourceReloadListener$Client",
"resources.MixinMinecraftGame"

View file

@ -9,6 +9,7 @@
"commands.MixinServerCommandManager",
"container.MixinServerPlayerEntity",
"entity.MixinEntityTracker",
"entity.MixinEntityTrackerEntry",
"events.objectbuilder.MixinBlock",
"events.objectbuilder.MixinItem",
"events.playerinteraction.MixinServerPlayNetworkHandler",
@ -20,8 +21,8 @@
"item.MixinAbstractFurnaceBlockEntity",
"itemgroup.MixinItemGroup",
"misc.MixinCrashReport",
"networking.MixinCustomPayloadC2SPacket",
"networking.MixinServerPlayNetworkHandler",
"networking.MixinSPacketCustomPayload",
"registry.MixinBootstrap",
"registry.MixinIdList",
"registry.MixinIdRegistry",