Skip to content

Commit

Permalink
[1.20.5] NeoForge Network API Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMCLoveMan committed Apr 24, 2024
1 parent 7e9682a commit 18c8123
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 131 deletions.
65 changes: 30 additions & 35 deletions src/main/java/net/themcbrothers/lib/network/BasePacketHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,61 @@
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent;
import net.neoforged.neoforge.network.handling.*;
import net.neoforged.neoforge.network.registration.IDirectionAwarePayloadHandlerBuilder;
import net.neoforged.neoforge.network.registration.IPayloadRegistrar;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import net.neoforged.neoforge.network.registration.HandlerThread;
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
import net.themcbrothers.lib.util.Version;

public abstract class BasePacketHandler {
protected BasePacketHandler(IEventBus modEventBus, String modId, Version version) {
modEventBus.addListener(RegisterPayloadHandlerEvent.class, event -> {
IPayloadRegistrar registrar = event.registrar(modId).versioned(version.toString());
protected BasePacketHandler(IEventBus modEventBus, String modId, Version version, HandlerThread handlerThread) {
modEventBus.addListener(RegisterPayloadHandlersEvent.class, event -> {
PayloadRegistrar registrarMainThread = event.registrar(modId).versioned(version.toString());
PayloadRegistrar registrarNetworkThread = registrarMainThread.executesOn(HandlerThread.NETWORK);

registerClientToServer(new PacketRegistrar(registrar, IDirectionAwarePayloadHandlerBuilder::server));
registerServerToClient(new PacketRegistrar(registrar, IDirectionAwarePayloadHandlerBuilder::client));
registerPackets(new PacketRegistrar(registrarMainThread));
registerPacketsNetworkThread(new PacketRegistrar(registrarNetworkThread));
});
}

protected abstract void registerClientToServer(PacketRegistrar registrar);
protected abstract void registerPackets(PacketRegistrar registrar);

protected abstract void registerServerToClient(PacketRegistrar registrar);
protected abstract void registerPacketsNetworkThread(PacketRegistrar registrar);

@FunctionalInterface
private interface ContextAwareHandler {
<PAYLOAD extends CustomPacketPayload, HANDLER> void accept(IDirectionAwarePayloadHandlerBuilder<PAYLOAD, HANDLER> builder, HANDLER handler);
}

protected record PacketRegistrar(IPayloadRegistrar registrar, ContextAwareHandler contextAwareHandler) {
private <MSG extends PacketMessage<IPayloadContext>> void common(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader, IPayloadHandler<MSG> handler) {
registrar.common(id, reader, builder -> contextAwareHandler.accept(builder, handler));
protected record PacketRegistrar(PayloadRegistrar registrar) {
public <MSG extends PacketMessage> void commonBidirectional(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
registrar.commonBidirectional(id, reader, PacketMessage::handle);
}

public <MSG extends PacketMessage<IPayloadContext>> void common(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
common(id, reader, PacketMessage::handleMainThread);
public <MSG extends PacketMessage> void commonToServer(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
registrar.commonToServer(id, reader, PacketMessage::handle);
}

public <MSG extends PacketMessage<IPayloadContext>> void commonNetworkThread(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
common(id, reader, PacketMessage::handle);
public <MSG extends PacketMessage> void commonToClient(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
registrar.commonToClient(id, reader, PacketMessage::handle);
}

private <MSG extends PacketMessage<ConfigurationPayloadContext>> void configuration(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader, IConfigurationPayloadHandler<MSG> handler) {
registrar.configuration(id, reader, builder -> contextAwareHandler.accept(builder, handler));
public <MSG extends PacketMessage> void configurationBidirectional(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
registrar.configurationBidirectional(id, reader, PacketMessage::handle);
}

public <MSG extends PacketMessage<ConfigurationPayloadContext>> void configuration(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
configuration(id, reader, PacketMessage::handleMainThread);
public <MSG extends PacketMessage> void configurationToServer(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
registrar.configurationToServer(id, reader, PacketMessage::handle);
}

public <MSG extends PacketMessage<ConfigurationPayloadContext>> void configurationNetworkThread(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
configuration(id, reader, PacketMessage::handle);
public <MSG extends PacketMessage> void configurationToClient(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader) {
registrar.configurationToClient(id, reader, PacketMessage::handle);
}

private <MSG extends PacketMessage<PlayPayloadContext>> void play(CustomPacketPayload.Type<MSG> id, StreamCodec<? super FriendlyByteBuf, MSG> reader, IPlayPayloadHandler<MSG> handler) {
registrar.play(id, reader, builder -> contextAwareHandler.accept(builder, handler));
public <MSG extends PacketMessage> void playBidirectional(CustomPacketPayload.Type<MSG> id, StreamCodec<FriendlyByteBuf, MSG> reader) {
registrar.playBidirectional(id, reader, PacketMessage::handle);
}

public <MSG extends PacketMessage<PlayPayloadContext>> void play(CustomPacketPayload.Type<MSG> id, StreamCodec<FriendlyByteBuf, MSG> reader) {
play(id, reader, PacketMessage::handleMainThread);
public <MSG extends PacketMessage> void playToServer(CustomPacketPayload.Type<MSG> id, StreamCodec<FriendlyByteBuf, MSG> reader) {
registrar.playToServer(id, reader, PacketMessage::handle);
}

public <MSG extends PacketMessage<PlayPayloadContext>> void playNetworkThread(CustomPacketPayload.Type<MSG> id, StreamCodec<FriendlyByteBuf, MSG> reader) {
play(id, reader, PacketMessage::handle);
public <MSG extends PacketMessage> void playToClient(CustomPacketPayload.Type<MSG> id, StreamCodec<FriendlyByteBuf, MSG> reader) {
registrar.playToClient(id, reader, PacketMessage::handle);
}
}
}
16 changes: 10 additions & 6 deletions src/main/java/net/themcbrothers/lib/network/PacketMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.neoforged.neoforge.network.handling.IPayloadContext;

public interface PacketMessage<CONTEXT extends IPayloadContext> extends CustomPacketPayload {
void handle(CONTEXT context);

default void handleMainThread(CONTEXT context) {
context.workHandler().execute(() -> handle(context));
}
/**
* Implement this interface on your packets
*/
public interface PacketMessage extends CustomPacketPayload {
/**
* Handle the packet with a given context
*
* @param context The context
*/
void handle(IPayloadContext context);
}
96 changes: 6 additions & 90 deletions src/main/java/net/themcbrothers/lib/network/PacketUtils.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
package net.themcbrothers.lib.network;

import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.neoforged.neoforge.network.PacketDistributor;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import net.neoforged.neoforge.server.ServerLifecycleHooks;

import java.util.Objects;
import java.util.Optional;

@SuppressWarnings("OptionalOfNullableMisuse")
public class PacketUtils {
private PacketUtils() {
}

public static Optional<ServerPlayer> asServerPlayer(IPayloadContext context) {
return context.player()
return Optional.ofNullable(context.player())
.filter(ServerPlayer.class::isInstance)
.map(ServerPlayer.class::cast);
}
Expand All @@ -35,91 +27,15 @@ public static <CLASS> Optional<CLASS> blockEntity(IPayloadContext context, Block
}

public static Optional<BlockEntity> blockEntity(IPayloadContext context, BlockPos pos) {
return context.level().map(level -> level.getBlockEntity(pos));
return Optional.ofNullable(context.player())
.map(Entity::level)
.map(level -> level.getBlockEntity(pos));
}

public static <CLASS extends AbstractContainerMenu> Optional<CLASS> container(IPayloadContext context, Class<CLASS> clazz) {
return context.player()
return Optional.ofNullable(context.player())
.map(player -> player.containerMenu)
.filter(clazz::isInstance)
.map(clazz::cast);
}

/**
* Send this message to the specified player.
*
* @param message - the message to send
* @param player - the player to send it to
*/
public static <MSG extends CustomPacketPayload> void sendTo(MSG message, ServerPlayer player) {
PacketDistributor.PLAYER.with(player).send(message);
}

/**
* Send this message to everyone connected to the server.
*
* @param message - message to send
*/
public static <MSG extends CustomPacketPayload> void sendToAll(MSG message) {
PacketDistributor.ALL.noArg().send(message);
}

/**
* Send this message to everyone connected to the server if the server has loaded.
*
* @param message - message to send
* @apiNote This is useful for reload listeners
*/
public static <MSG extends CustomPacketPayload> void sendToAllIfLoaded(MSG message) {
if (ServerLifecycleHooks.getCurrentServer() != null) {
// If the server has loaded, send to all players
sendToAll(message);
}
}

/**
* Send this message to everyone within the supplied dimension.
*
* @param message - the message to send
* @param dimension - the dimension to target
*/
public static <MSG extends CustomPacketPayload> void sendToDimension(MSG message, ResourceKey<Level> dimension) {
PacketDistributor.DIMENSION.with(dimension).send(message);
}

/**
* Send this message to the server.
*
* @param message - the message to send
*/
public static <MSG extends CustomPacketPayload> void sendToServer(MSG message) {
PacketDistributor.SERVER.noArg().send(message);
}

public static <MSG extends CustomPacketPayload> void sendToAllTracking(MSG message, Entity entity) {
PacketDistributor.TRACKING_ENTITY.with(entity).send(message);
}

public static <MSG extends CustomPacketPayload> void sendToAllTrackingAndSelf(MSG message, Entity entity) {
PacketDistributor.TRACKING_ENTITY_AND_SELF.with(entity).send(message);
}

public static <MSG extends CustomPacketPayload> void sendToAllTracking(MSG message, BlockEntity tile) {
sendToAllTracking(message, Objects.requireNonNull(tile.getLevel()), tile.getBlockPos());
}

public static <MSG extends CustomPacketPayload> void sendToAllTracking(MSG message, Level level, BlockPos pos) {
if (level instanceof ServerLevel serverLevel) {
// If we have a ServerWorld just directly figure out the ChunkPos to not require looking up the chunk
// This provides a decent performance boost over using the packet distributor
serverLevel.getChunkSource().chunkMap.getPlayers(new ChunkPos(pos), false).forEach(p -> sendTo(message, p));
} else {
// Otherwise, fallback to entities tracking the chunk if some mod did something odd and our level is not a ServerWorld
PacketDistributor.TRACKING_CHUNK.with(level.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()))).send(message);
}
}

private static boolean isChunkTracked(ServerPlayer player, int chunkX, int chunkZ) {
return player.getChunkTrackingView().contains(chunkX, chunkZ) && !player.connection.chunkSender.isPending(ChunkPos.asLong(chunkX, chunkZ));
}
}

0 comments on commit 18c8123

Please sign in to comment.