Net handling pass 2. Tidied up login packet sourcing, generate registry packets.
This commit is contained in:
parent
9f2c7c881c
commit
abacf8e141
17 changed files with 787 additions and 220 deletions
23
build.gradle
23
build.gradle
|
@ -49,6 +49,7 @@ project(':clean') {
|
|||
|
||||
project(':forge') {
|
||||
evaluationDependsOn(':clean')
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'net.minecraftforge.gradle.forgedev.patcher'
|
||||
sourceSets {
|
||||
|
@ -95,17 +96,17 @@ project(':forge') {
|
|||
maxFuzz 3
|
||||
}
|
||||
dependencies {
|
||||
implementation 'net.minecraft:client:1.13:extra'
|
||||
implementation 'cpw.mods:modlauncher:0.1.0-rc.3' //Pinned until cpw fixes getCommonSupertype
|
||||
implementation 'net.minecraftforge:accesstransformers:0.10+:shadowed'
|
||||
implementation 'net.minecraftforge:eventbus:0.1+:service'
|
||||
implementation 'net.minecraftforge:forgespi:0.1+'
|
||||
implementation 'net.minecraftforge:coremods:0.1+'
|
||||
implementation 'com.electronwill.night-config:core:3.4.0'
|
||||
implementation 'com.electronwill.night-config:toml:3.4.0'
|
||||
implementation 'org.jline:jline:3.5.1'
|
||||
implementation 'org.apache.maven:maven-artifact:3.5.3'
|
||||
implementation 'java3d:vecmath:1.5.2'
|
||||
api 'net.minecraft:client:1.13:extra'
|
||||
api 'cpw.mods:modlauncher:0.1.0-rc.3' //Pinned until cpw fixes getCommonSupertype
|
||||
api 'net.minecraftforge:accesstransformers:0.10+:shadowed'
|
||||
api 'net.minecraftforge:eventbus:0.1+:service'
|
||||
api 'net.minecraftforge:forgespi:0.1+'
|
||||
api 'net.minecraftforge:coremods:0.1+'
|
||||
api 'com.electronwill.night-config:core:3.4.0'
|
||||
api 'com.electronwill.night-config:toml:3.4.0'
|
||||
api 'org.jline:jline:3.5.1'
|
||||
api 'org.apache.maven:maven-artifact:3.5.3'
|
||||
api 'java3d:vecmath:1.5.2'
|
||||
}
|
||||
|
||||
configurations {
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
float f7 = f2 * f4;
|
||||
- double d3 = 5.0D;
|
||||
- Vec3d vec3d1 = vec3d.add((double)f6 * 5.0D, (double)f5 * 5.0D, (double)f7 * 5.0D);
|
||||
+ double d3 = 6;
|
||||
+ double d3 = playerIn.getEntityAttribute(EntityPlayer.REACH_DISTANCE).getAttributeValue();
|
||||
+ Vec3d vec3d1 = vec3d.add((double)f6 * d3, (double)f5 * d3, (double)f7 * d3);
|
||||
return worldIn.func_200259_a(vec3d, vec3d1, useLiquids ? RayTraceFluidMode.SOURCE_ONLY : RayTraceFluidMode.NEVER, false, false);
|
||||
}
|
||||
|
|
|
@ -45,9 +45,9 @@ public class LaunchTesting
|
|||
logcontext.getConfiguration().addFilter(axformFilter);
|
||||
logcontext.getConfiguration().addFilter(eventbusFilter);
|
||||
logcontext.updateLoggers();
|
||||
File invsorter = new File("/home/cpw/projects/mods/inventorysorter/classes");
|
||||
File invsorter = new File("/home/cpw/projects/minecraft/inventorysorter/classes");
|
||||
if (invsorter.exists()) {
|
||||
System.setProperty("fml.explodedDir", "/home/cpw/projects/mods/inventorysorter/classes"); //TODO: Move this to a example included in our tests, not a random location...
|
||||
System.setProperty("fml.explodedDir", "/home/cpw/projects/minecraft/inventorysorter/classes"); //TODO: Move this to a example included in our tests, not a random location...
|
||||
}
|
||||
String assets = System.getenv().getOrDefault("assetDirectory", "assets");
|
||||
String target = System.getenv().get("target");
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
package net.minecraftforge.fml.network;
|
||||
|
||||
import io.netty.util.AttributeKey;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.server.network.NetHandlerLoginServer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraftforge.fml.Logging;
|
||||
import net.minecraftforge.fml.network.simple.SimpleChannel;
|
||||
import net.minecraftforge.fml.util.ThreeConsumer;
|
||||
import net.minecraftforge.registries.RegistryManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Instance responsible for handling the overall FML network handshake.
|
||||
*
|
||||
* <p>An instance is created during {@link net.minecraft.network.handshake.client.CPacketHandshake} handling, and attached
|
||||
* to the {@link NetworkManager#channel} via {@link #FML_HANDSHAKE_HANDLER_ATTRIBUTE_KEY}.
|
||||
*
|
||||
* <p>The {@link #channel} is a {@link SimpleChannel} with standard messages flowing in both directions.
|
||||
*
|
||||
* <p>The {@link #loginWrapper} transforms these messages into {@link net.minecraft.network.login.client.CPacketCustomPayloadLogin}
|
||||
* and {@link net.minecraft.network.login.server.SPacketCustomPayloadLogin} compatible messages, by means of wrapping.
|
||||
*
|
||||
* <p>The handshake is ticked {@link #tickLogin(NetworkManager)} from the {@link NetHandlerLoginServer#update()} method,
|
||||
* utilizing the {@link NetHandlerLoginServer.LoginState#NEGOTIATING} state, which is otherwise unused in vanilla code.
|
||||
*
|
||||
* <p>During client to server initiation, on the <em>server</em>, the {@link NetworkEvent.GatherLoginPayloadsEvent} is fired,
|
||||
* which solicits all registered channels at the {@link NetworkRegistry} for any
|
||||
* {@link net.minecraftforge.fml.network.NetworkRegistry.LoginPayload} they wish to supply.
|
||||
*
|
||||
* <p>The collected {@link net.minecraftforge.fml.network.NetworkRegistry.LoginPayload} are sent, one per tick, via
|
||||
* the {@link FMLLoginWrapper#wrapPacket(ResourceLocation, PacketBuffer)} mechanism to the incoming client connection. Each
|
||||
* packet is indexed via {@link net.minecraft.network.login.client.CPacketCustomPayloadLogin#field_209922_a}, which is
|
||||
* the only mechanism available for tracking request/response pairs.
|
||||
*
|
||||
* <p>Each packet sent from the server should be replied by the client, though not necessarily in sent order. The reply
|
||||
* should contain the index of the server's packet it is replying to. The {@link FMLLoginWrapper} class handles indexing
|
||||
* replies correctly automatically.
|
||||
*
|
||||
* <p>Once all packets have been dispatched, we wait for all replies to be received. Once all replies are received, the
|
||||
* final login phase will commence.
|
||||
*/
|
||||
public class FMLHandshakeHandler {
|
||||
static final Marker FMLHSMARKER = MarkerManager.getMarker("FMLHANDSHAKE").setParents(FMLNetworking.NETWORK);
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
static final ResourceLocation FML_HANDSHAKE_RESOURCE = new ResourceLocation("fml:handshake");
|
||||
private static final AttributeKey<FMLHandshakeHandler> FML_HANDSHAKE_HANDLER_ATTRIBUTE_KEY = AttributeKey.newInstance("fml:handshake");
|
||||
|
||||
private static final FMLLoginWrapper loginWrapper = new FMLLoginWrapper();
|
||||
private static SimpleChannel channel;
|
||||
static {
|
||||
channel = NetworkRegistry.ChannelBuilder.named(FML_HANDSHAKE_RESOURCE).
|
||||
clientAcceptedVersions(a -> true).
|
||||
serverAcceptedVersions(a -> true).
|
||||
networkProtocolVersion(() -> NetworkHooks.NETVERSION).
|
||||
simpleChannel();
|
||||
channel.messageBuilder(FMLHandshakeMessages.C2SAcknowledge.class, 99).
|
||||
loginIndex(FMLHandshakeMessages.LoginIndexedMessage::getLoginIndex, FMLHandshakeMessages.LoginIndexedMessage::setLoginIndex).
|
||||
decoder(FMLHandshakeMessages.C2SAcknowledge::decode).
|
||||
encoder(FMLHandshakeMessages.C2SAcknowledge::encode).
|
||||
consumer(indexFirst(FMLHandshakeHandler::handleClientAck)).
|
||||
add();
|
||||
channel.messageBuilder(FMLHandshakeMessages.S2CModList.class, 1).
|
||||
loginIndex(FMLHandshakeMessages.LoginIndexedMessage::getLoginIndex, FMLHandshakeMessages.LoginIndexedMessage::setLoginIndex).
|
||||
decoder(FMLHandshakeMessages.S2CModList::decode).
|
||||
encoder(FMLHandshakeMessages.S2CModList::encode).
|
||||
markAsLoginPacket().
|
||||
consumer(biConsumerFor(FMLHandshakeHandler::handleServerModListOnClient)).
|
||||
add();
|
||||
channel.messageBuilder(FMLHandshakeMessages.C2SModListReply.class, 2).
|
||||
loginIndex(FMLHandshakeMessages.LoginIndexedMessage::getLoginIndex, FMLHandshakeMessages.LoginIndexedMessage::setLoginIndex).
|
||||
decoder(FMLHandshakeMessages.C2SModListReply::decode).
|
||||
encoder(FMLHandshakeMessages.C2SModListReply::encode).
|
||||
consumer(indexFirst(FMLHandshakeHandler::handleClientModListOnServer)).
|
||||
add();
|
||||
channel.messageBuilder(FMLHandshakeMessages.S2CRegistry.class, 3).
|
||||
loginIndex(FMLHandshakeMessages.LoginIndexedMessage::getLoginIndex, FMLHandshakeMessages.LoginIndexedMessage::setLoginIndex).
|
||||
decoder(FMLHandshakeMessages.S2CRegistry::decode).
|
||||
encoder(FMLHandshakeMessages.S2CRegistry::encode).
|
||||
buildLoginPacketList(RegistryManager::generateRegistryPackets).
|
||||
consumer(biConsumerFor(FMLHandshakeHandler::handleRegistryMessage)).
|
||||
add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a two-argument instance method reference into a {@link BiConsumer} based on the {@link #getHandshake(Supplier)} function.
|
||||
*
|
||||
* @param consumer A two argument instance method reference
|
||||
* @param <MSG> message type
|
||||
* @return A {@link BiConsumer} for use in message handling
|
||||
*/
|
||||
private static <MSG extends FMLHandshakeMessages.LoginIndexedMessage> BiConsumer<MSG, Supplier<NetworkEvent.Context>> biConsumerFor(ThreeConsumer<FMLHandshakeHandler, ? super MSG, ? super Supplier<NetworkEvent.Context>> consumer)
|
||||
{
|
||||
return (m, c) -> ThreeConsumer.bindArgs(consumer, m, c).accept(getHandshake(c));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a two-argument instance method reference into a {@link BiConsumer} {@link #biConsumerFor(ThreeConsumer)}, first calling the {@link #handleIndexedMessage(FMLHandshakeMessages.LoginIndexedMessage, Supplier)}
|
||||
* method to handle index tracking. Used for client to server replies.
|
||||
* @param next The method reference to call after index handling
|
||||
* @param <MSG> message type
|
||||
* @return A {@link BiConsumer} for use in message handling
|
||||
*/
|
||||
private static <MSG extends FMLHandshakeMessages.LoginIndexedMessage> BiConsumer<MSG, Supplier<NetworkEvent.Context>> indexFirst(ThreeConsumer<FMLHandshakeHandler, MSG, Supplier<NetworkEvent.Context>> next)
|
||||
{
|
||||
final BiConsumer<MSG, Supplier<NetworkEvent.Context>> loginIndexedMessageSupplierBiConsumer = biConsumerFor(FMLHandshakeHandler::handleIndexedMessage);
|
||||
return loginIndexedMessageSupplierBiConsumer.andThen(biConsumerFor(next));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new handshake instance. Called when connection is first created during the {@link net.minecraft.network.handshake.client.CPacketHandshake}
|
||||
* handling.
|
||||
*
|
||||
* @param manager The network manager for this connection
|
||||
* @param direction The {@link NetworkDirection} for this connection: {@link NetworkDirection#LOGIN_TO_SERVER} or {@link NetworkDirection#LOGIN_TO_CLIENT}
|
||||
*/
|
||||
static void registerHandshake(NetworkManager manager, NetworkDirection direction) {
|
||||
manager.channel().attr(FML_HANDSHAKE_HANDLER_ATTRIBUTE_KEY).compareAndSet(null, new FMLHandshakeHandler(manager, direction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the handshake from the {@link NetworkEvent.Context}
|
||||
*
|
||||
* @param contextSupplier the {@link NetworkEvent.Context}
|
||||
* @return The handshake handler for the connection
|
||||
*/
|
||||
private static FMLHandshakeHandler getHandshake(Supplier<NetworkEvent.Context> contextSupplier) {
|
||||
return contextSupplier.get().attr(FML_HANDSHAKE_HANDLER_ATTRIBUTE_KEY).get();
|
||||
}
|
||||
|
||||
static boolean tickLogin(NetworkManager networkManager)
|
||||
{
|
||||
return networkManager.channel().attr(FML_HANDSHAKE_HANDLER_ATTRIBUTE_KEY).get().tickServer();
|
||||
}
|
||||
|
||||
private List<NetworkRegistry.LoginPayload> messageList;
|
||||
|
||||
private List<Integer> sentMessages = new ArrayList<>();
|
||||
|
||||
private final NetworkDirection direction;
|
||||
private final NetworkManager manager;
|
||||
private int packetPosition;
|
||||
private FMLHandshakeHandler(NetworkManager networkManager, NetworkDirection side)
|
||||
{
|
||||
this.direction = side;
|
||||
this.manager = networkManager;
|
||||
this.messageList = NetworkRegistry.gatherLoginPayloads();
|
||||
LOGGER.debug(FMLHSMARKER, "Starting new modded network connection. Found {} messages to dispatch.", this.messageList.size());
|
||||
}
|
||||
|
||||
private void handleServerModListOnClient(FMLHandshakeMessages.S2CModList serverModList, Supplier<NetworkEvent.Context> c)
|
||||
{
|
||||
LOGGER.debug(FMLHSMARKER, "Logging into server with mod list [{}]", serverModList.getModList());
|
||||
boolean accepted = NetworkRegistry.validateClientChannels(serverModList.getChannels());
|
||||
c.get().setPacketHandled(true);
|
||||
if (!accepted) {
|
||||
LOGGER.error(FMLHSMARKER, "Terminating connection with server, mismatched mod list");
|
||||
c.get().getNetworkManager().closeChannel(new TextComponentString("Connection closed - mismatched mod channel list"));
|
||||
return;
|
||||
}
|
||||
final FMLHandshakeMessages.C2SModListReply reply = new FMLHandshakeMessages.C2SModListReply();
|
||||
channel.reply(reply, c.get());
|
||||
LOGGER.debug(FMLHSMARKER, "Accepted server connection");
|
||||
}
|
||||
|
||||
private <MSG extends FMLHandshakeMessages.LoginIndexedMessage> void handleIndexedMessage(MSG message, Supplier<NetworkEvent.Context> c)
|
||||
{
|
||||
LOGGER.debug(FMLHSMARKER, "Received client indexed reply {} of type {}", message.getLoginIndex(), message.getClass().getName());
|
||||
boolean removed = this.sentMessages.removeIf(i->i==message.getLoginIndex());
|
||||
if (!removed) {
|
||||
LOGGER.error(FMLHSMARKER, "Recieved unexpected index {} in client reply", message.getLoginIndex());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleClientModListOnServer(FMLHandshakeMessages.C2SModListReply clientModList, Supplier<NetworkEvent.Context> c)
|
||||
{
|
||||
LOGGER.debug(FMLHSMARKER, "Received client connection with modlist [{}]", clientModList.getModList());
|
||||
boolean accepted = NetworkRegistry.validateServerChannels(clientModList.getChannels());
|
||||
c.get().setPacketHandled(true);
|
||||
if (!accepted) {
|
||||
LOGGER.error(FMLHSMARKER, "Terminating connection with client, mismatched mod list");
|
||||
c.get().getNetworkManager().closeChannel(new TextComponentString("Connection closed - mismatched mod channel list"));
|
||||
return;
|
||||
}
|
||||
LOGGER.debug(FMLHSMARKER, "Accepted client connection mod list");
|
||||
}
|
||||
private void handleRegistryMessage(final FMLHandshakeMessages.S2CRegistry registryPacket, final Supplier<NetworkEvent.Context> contextSupplier) {
|
||||
RegistryManager.acceptRegistry(registryPacket, contextSupplier);
|
||||
contextSupplier.get().setPacketHandled(true);
|
||||
final FMLHandshakeMessages.C2SAcknowledge reply = new FMLHandshakeMessages.C2SAcknowledge();
|
||||
channel.reply(reply, contextSupplier.get());
|
||||
}
|
||||
|
||||
private void handleClientAck(final FMLHandshakeMessages.C2SAcknowledge msg, final Supplier<NetworkEvent.Context> contextSupplier) {
|
||||
LOGGER.debug(FMLHSMARKER, "Received acknowledgement from client");
|
||||
contextSupplier.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* FML will send packets, from Server to Client, from the messages queue until the queue is drained. Each message
|
||||
* will be indexed, and placed into the "pending acknowledgement" queue.
|
||||
*
|
||||
* As indexed packets are received at the server, they will be removed from the "pending acknowledgement" queue.
|
||||
*
|
||||
* Once the pending queue is drained, this method returns true - indicating that login processing can proceed to
|
||||
* the next step.
|
||||
*
|
||||
* @return true if there is no more need to tick this login connection.
|
||||
*/
|
||||
public boolean tickServer()
|
||||
{
|
||||
if (packetPosition < messageList.size()) {
|
||||
NetworkRegistry.LoginPayload message = messageList.get(packetPosition);
|
||||
|
||||
LOGGER.debug(FMLHSMARKER, "Sending ticking packet info '{}' to '{}' sequence {}", message.getMessageContext(), message.getChannelName(), packetPosition);
|
||||
loginWrapper.sendServerToClientLoginPacket(message.getChannelName(), message.getData(), packetPosition, this.manager);
|
||||
sentMessages.add(packetPosition);
|
||||
packetPosition++;
|
||||
}
|
||||
|
||||
// we're done when sentMessages is empty
|
||||
if (sentMessages.isEmpty() && packetPosition >= messageList.size()-1) {
|
||||
// clear ourselves - we're done!
|
||||
this.manager.channel().attr(FML_HANDSHAKE_HANDLER_ATTRIBUTE_KEY).set(null);
|
||||
LOGGER.debug(FMLHSMARKER, "Handshake complete!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -5,35 +5,37 @@ import net.minecraft.nbt.NBTTagCompound;
|
|||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.nbt.NBTTagString;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
import net.minecraftforge.registries.ForgeRegistry;
|
||||
import net.minecraftforge.registries.IForgeRegistryEntry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class FMLHandshakeMessage
|
||||
public class FMLHandshakeMessages
|
||||
{
|
||||
// Login index sequence number
|
||||
private int index;
|
||||
void setPacketIndexSequence(int i)
|
||||
{
|
||||
this.index = i;
|
||||
static class LoginIndexedMessage {
|
||||
private int loginIndex;
|
||||
|
||||
void setLoginIndex(final int loginIndex) {
|
||||
this.loginIndex = loginIndex;
|
||||
}
|
||||
|
||||
int getPacketIndexSequence()
|
||||
{
|
||||
return index;
|
||||
int getLoginIndex() {
|
||||
return loginIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server to client "list of mods". Always first handshake message.
|
||||
*/
|
||||
static class S2CModList extends FMLHandshakeMessage
|
||||
public static class S2CModList extends LoginIndexedMessage
|
||||
{
|
||||
private NBTTagList channels;
|
||||
private List<String> modList;
|
||||
|
||||
S2CModList() {
|
||||
public S2CModList() {
|
||||
this.modList = ModList.get().getMods().stream().map(ModInfo::getModId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
@ -43,13 +45,13 @@ class FMLHandshakeMessage
|
|||
this.channels = nbtTagCompound.getTagList("channels", 10);
|
||||
}
|
||||
|
||||
static S2CModList decode(PacketBuffer packetBuffer)
|
||||
public static S2CModList decode(PacketBuffer packetBuffer)
|
||||
{
|
||||
final NBTTagCompound nbtTagCompound = packetBuffer.readCompoundTag();
|
||||
return new S2CModList(nbtTagCompound);
|
||||
}
|
||||
|
||||
void encode(PacketBuffer packetBuffer)
|
||||
public void encode(PacketBuffer packetBuffer)
|
||||
{
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setTag("modlist",modList.stream().map(NBTTagString::new).collect(Collectors.toCollection(NBTTagList::new)));
|
||||
|
@ -66,9 +68,9 @@ class FMLHandshakeMessage
|
|||
}
|
||||
}
|
||||
|
||||
static class C2SModListReply extends S2CModList
|
||||
public static class C2SModListReply extends S2CModList
|
||||
{
|
||||
C2SModListReply() {
|
||||
public C2SModListReply() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -76,7 +78,7 @@ class FMLHandshakeMessage
|
|||
super(buffer);
|
||||
}
|
||||
|
||||
static C2SModListReply decode(PacketBuffer buffer)
|
||||
public static C2SModListReply decode(PacketBuffer buffer)
|
||||
{
|
||||
return new C2SModListReply(buffer.readCompoundTag());
|
||||
}
|
||||
|
@ -86,4 +88,30 @@ class FMLHandshakeMessage
|
|||
super.encode(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static class C2SAcknowledge extends LoginIndexedMessage {
|
||||
public void encode(PacketBuffer buf) {
|
||||
|
||||
}
|
||||
|
||||
public static C2SAcknowledge decode(PacketBuffer buf) {
|
||||
return new C2SAcknowledge();
|
||||
}
|
||||
}
|
||||
|
||||
public static class S2CRegistry extends LoginIndexedMessage {
|
||||
|
||||
public S2CRegistry(final ResourceLocation key, final ForgeRegistry<? extends IForgeRegistryEntry<?>> registry) {
|
||||
}
|
||||
|
||||
S2CRegistry() {
|
||||
}
|
||||
|
||||
void encode(final PacketBuffer buffer) {
|
||||
}
|
||||
|
||||
public static S2CRegistry decode(final PacketBuffer buffer) {
|
||||
return new S2CRegistry();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package net.minecraftforge.fml.network;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.network.event.EventNetworkChannel;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Wrapper for custom login packets. Transforms unnamed login channel messages into channels dispatched the same
|
||||
* as regular custom packets.
|
||||
*/
|
||||
public class FMLLoginWrapper {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
static final ResourceLocation WRAPPER = new ResourceLocation("fml:loginwrapper");
|
||||
private EventNetworkChannel wrapperChannel;
|
||||
|
||||
FMLLoginWrapper() {
|
||||
wrapperChannel = NetworkRegistry.ChannelBuilder.named(FMLLoginWrapper.WRAPPER).
|
||||
clientAcceptedVersions(a->true).
|
||||
serverAcceptedVersions(a->true).
|
||||
networkProtocolVersion(()->NetworkHooks.NETVERSION)
|
||||
.eventNetworkChannel();
|
||||
wrapperChannel.addListener(this::wrapperReceived);
|
||||
}
|
||||
|
||||
private <T extends NetworkEvent> void wrapperReceived(final T packet) {
|
||||
final NetworkEvent.Context wrappedContext = packet.getSource().get();
|
||||
final PacketBuffer payload = packet.getPayload();
|
||||
ResourceLocation targetNetworkReceiver = FMLHandshakeHandler.FML_HANDSHAKE_RESOURCE;
|
||||
PacketBuffer data = null;
|
||||
if (payload != null) {
|
||||
targetNetworkReceiver = payload.readResourceLocation();
|
||||
final int payloadLength = payload.readVarInt();
|
||||
data = new PacketBuffer(payload.readBytes(payloadLength));
|
||||
}
|
||||
final int loginSequence = packet.getLoginIndex();
|
||||
LOGGER.debug(FMLHandshakeHandler.FMLHSMARKER, "Recieved login wrapper packet event for channel {} with index {}", targetNetworkReceiver, loginSequence);
|
||||
final NetworkEvent.Context context = new NetworkEvent.Context(wrappedContext.getNetworkManager(), wrappedContext.getDirection(), new PacketDispatcher((rl, buf) -> {
|
||||
LOGGER.debug(FMLHandshakeHandler.FMLHSMARKER, "Dispatching wrapped packet reply for channel {} with index {}", rl, loginSequence);
|
||||
wrappedContext.getPacketDispatcher().sendPacket(WRAPPER, this.wrapPacket(rl, buf));
|
||||
}));
|
||||
final NetworkEvent.LoginPayloadEvent loginPayloadEvent = new NetworkEvent.LoginPayloadEvent(data, () -> context, loginSequence);
|
||||
NetworkRegistry.findTarget(targetNetworkReceiver).ifPresent(ni -> {
|
||||
ni.dispatchLoginPacket(loginPayloadEvent);
|
||||
wrappedContext.setPacketHandled(context.getPacketHandled());
|
||||
});
|
||||
}
|
||||
|
||||
private PacketBuffer wrapPacket(final ResourceLocation rl, final PacketBuffer buf) {
|
||||
PacketBuffer pb = new PacketBuffer(Unpooled.buffer(buf.capacity()));
|
||||
pb.writeResourceLocation(rl);
|
||||
pb.writeVarInt(buf.readableBytes());
|
||||
pb.writeBytes(buf);
|
||||
return pb;
|
||||
}
|
||||
|
||||
void sendServerToClientLoginPacket(final ResourceLocation resourceLocation, final PacketBuffer buffer, final int index, final NetworkManager manager) {
|
||||
PacketBuffer pb = wrapPacket(resourceLocation, buffer);
|
||||
manager.sendPacket(NetworkDirection.LOGIN_TO_CLIENT.buildPacket(Pair.of(pb, index), WRAPPER).getThis());
|
||||
}
|
||||
}
|
|
@ -2,150 +2,14 @@ package net.minecraftforge.fml.network;
|
|||
|
||||
import io.netty.util.AttributeKey;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraftforge.fml.network.simple.SimpleChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class FMLNetworking
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Marker FMLHSMARKER = MarkerManager.getMarker("FMLHSMARKER");
|
||||
static final Marker NETWORK = MarkerManager.getMarker("FMLNETWORK");
|
||||
static final AttributeKey<String> FML_MARKER = AttributeKey.valueOf("fml:marker");
|
||||
private static final AttributeKey<FMLHandshake> FML_HANDSHAKE = AttributeKey.newInstance("fml:handshake");
|
||||
|
||||
static void registerHandshake(NetworkManager manager, NetworkDirection direction) {
|
||||
manager.channel().attr(FML_HANDSHAKE).compareAndSet(null, new FMLHandshake(manager, direction));
|
||||
}
|
||||
|
||||
private static FMLHandshake getHandshake(Supplier<NetworkEvent.Context> contextSupplier) {
|
||||
final NetworkManager networkManager = contextSupplier.get().getNetworkManager();
|
||||
return getHandshake(networkManager);
|
||||
}
|
||||
|
||||
private static FMLHandshake getHandshake(NetworkManager manager)
|
||||
{
|
||||
return manager.channel().attr(FML_HANDSHAKE).get();
|
||||
}
|
||||
|
||||
static boolean dispatch(NetworkManager networkManager)
|
||||
{
|
||||
return getHandshake(networkManager).tickServer();
|
||||
}
|
||||
|
||||
public static class FMLHandshake {
|
||||
private static SimpleChannel channel;
|
||||
private static List<Supplier<FMLHandshakeMessage>> messages = Arrays.asList(FMLHandshakeMessage.S2CModList::new);
|
||||
private List<FMLHandshakeMessage> sentMessages = new ArrayList<>();
|
||||
static {
|
||||
channel = NetworkRegistry.ChannelBuilder.named(NetworkHooks.FMLHANDSHAKE).
|
||||
clientAcceptedVersions(a -> true).
|
||||
serverAcceptedVersions(a -> true).
|
||||
networkProtocolVersion(() -> NetworkHooks.NETVERSION).
|
||||
simpleChannel();
|
||||
channel.messageBuilder(FMLHandshakeMessage.S2CModList.class, 1).
|
||||
decoder(FMLHandshakeMessage.S2CModList::decode).
|
||||
encoder(FMLHandshakeMessage.S2CModList::encode).
|
||||
loginIndex(FMLHandshakeMessage::getPacketIndexSequence, FMLHandshakeMessage::setPacketIndexSequence).
|
||||
consumer((m,c)->getHandshake(c).handleServerModListOnClient(m, c)).
|
||||
add();
|
||||
channel.messageBuilder(FMLHandshakeMessage.C2SModListReply.class, 2).
|
||||
loginIndex(FMLHandshakeMessage::getPacketIndexSequence, FMLHandshakeMessage::setPacketIndexSequence).
|
||||
decoder(FMLHandshakeMessage.C2SModListReply::decode).
|
||||
encoder(FMLHandshakeMessage.C2SModListReply::encode).
|
||||
consumer((m,c) -> getHandshake(c).handleClientModListOnServer(m,c)).
|
||||
add();
|
||||
}
|
||||
|
||||
private final NetworkDirection direction;
|
||||
|
||||
private final NetworkManager manager;
|
||||
private int packetPosition;
|
||||
|
||||
public FMLHandshake(NetworkManager networkManager, NetworkDirection side)
|
||||
{
|
||||
this.direction = side;
|
||||
this.manager = networkManager;
|
||||
}
|
||||
|
||||
private void handleServerModListOnClient(FMLHandshakeMessage.S2CModList serverModList, Supplier<NetworkEvent.Context> c)
|
||||
{
|
||||
LOGGER.debug(FMLHSMARKER, "Logging into server with mod list [{}]", serverModList.getModList());
|
||||
boolean accepted = NetworkRegistry.validateClientChannels(serverModList.getChannels());
|
||||
c.get().setPacketHandled(true);
|
||||
if (!accepted) {
|
||||
LOGGER.error(FMLHSMARKER, "Terminating connection with server, mismatched mod list");
|
||||
c.get().getNetworkManager().closeChannel(new TextComponentString("Connection closed - mismatched mod channel list"));
|
||||
return;
|
||||
}
|
||||
final FMLHandshakeMessage.C2SModListReply reply = new FMLHandshakeMessage.C2SModListReply();
|
||||
reply.setPacketIndexSequence(serverModList.getPacketIndexSequence());
|
||||
channel.reply(reply, c.get());
|
||||
LOGGER.debug(FMLHSMARKER, "Sent C2SModListReply packet with index {}", reply.getPacketIndexSequence());
|
||||
}
|
||||
|
||||
private void handleClientModListOnServer(FMLHandshakeMessage.C2SModListReply clientModList, Supplier<NetworkEvent.Context> c)
|
||||
{
|
||||
LOGGER.debug(FMLHSMARKER, "Received client connection with modlist [{}]", clientModList.getModList());
|
||||
final FMLHandshakeMessage message = this.sentMessages.stream().filter(ob -> ob.getPacketIndexSequence() == clientModList.getPacketIndexSequence()).findFirst().orElseThrow(() -> new RuntimeException("Unexpected reply from client"));
|
||||
boolean removed = this.sentMessages.remove(message);
|
||||
boolean accepted = NetworkRegistry.validateServerChannels(clientModList.getChannels());
|
||||
c.get().setPacketHandled(true);
|
||||
if (!accepted) {
|
||||
LOGGER.error(FMLHSMARKER, "Terminating connection with client, mismatched mod list");
|
||||
c.get().getNetworkManager().closeChannel(new TextComponentString("Connection closed - mismatched mod channel list"));
|
||||
return;
|
||||
}
|
||||
LOGGER.debug(FMLHSMARKER, "Cleared original message {}", removed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Design of handshake.
|
||||
*
|
||||
* After {@link net.minecraft.server.network.NetHandlerLoginServer} enters the {@link net.minecraft.server.network.NetHandlerLoginServer.LoginState#NEGOTIATING}
|
||||
* state, this will be ticked once per server tick.
|
||||
*
|
||||
* FML will send packets, from Server to Client, from the messages queue until the queue is drained. Each message
|
||||
* will be indexed, and placed into the "pending acknowledgement" queue.
|
||||
*
|
||||
* The client should send an acknowledgement for every packet that has a positive index, containing
|
||||
* that index (and maybe other data as well).
|
||||
*
|
||||
* As indexed packets are received at the server, they will be removed from the "pending acknowledgement" queue.
|
||||
*
|
||||
* Once the pending queue is drained, this method returns true - indicating that login processing can proceed to
|
||||
* the next step.
|
||||
*
|
||||
* @return true if there is no more need to tick this login connection.
|
||||
*/
|
||||
public boolean tickServer()
|
||||
{
|
||||
if (packetPosition < messages.size()) {
|
||||
final FMLHandshakeMessage message = messages.get(packetPosition).get();
|
||||
message.setPacketIndexSequence(packetPosition);
|
||||
LOGGER.debug(FMLHSMARKER, "Sending ticking packet {} index {}", message.getClass().getName(), message.getPacketIndexSequence());
|
||||
channel.sendTo(message, this.manager, this.direction);
|
||||
sentMessages.add(message);
|
||||
packetPosition++;
|
||||
}
|
||||
|
||||
// we're done when sentMessages is empty
|
||||
if (sentMessages.isEmpty()) {
|
||||
// clear ourselves - we're done!
|
||||
this.manager.channel().attr(FML_HANDSHAKE).set(null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public interface ICustomPacket<T extends Packet<?>> {
|
|||
}
|
||||
|
||||
default ResourceLocation getName() {
|
||||
return Fields.lookup.get(this.getClass()).channel.map(f->UnsafeHacks.<ResourceLocation>getField(f, this)).orElse(NetworkHooks.FMLHANDSHAKE);
|
||||
return Fields.lookup.get(this.getClass()).channel.map(f->UnsafeHacks.<ResourceLocation>getField(f, this)).orElse(FMLLoginWrapper.WRAPPER);
|
||||
}
|
||||
|
||||
default int getIndex() {
|
||||
|
|
|
@ -20,12 +20,18 @@
|
|||
package net.minecraftforge.fml.network;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import io.netty.util.Attribute;
|
||||
import io.netty.util.AttributeKey;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.IThreadListener;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.LogicalSidedProvider;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class NetworkEvent extends Event
|
||||
|
@ -41,6 +47,13 @@ public class NetworkEvent extends Event
|
|||
this.loginIndex = payload.getIndex();
|
||||
}
|
||||
|
||||
private NetworkEvent(final PacketBuffer payload, final Supplier<Context> source, final int loginIndex)
|
||||
{
|
||||
this.payload = payload;
|
||||
this.source = source;
|
||||
this.loginIndex = loginIndex;
|
||||
}
|
||||
|
||||
public PacketBuffer getPayload()
|
||||
{
|
||||
return payload;
|
||||
|
@ -82,6 +95,23 @@ public class NetworkEvent extends Event
|
|||
}
|
||||
}
|
||||
|
||||
public static class GatherLoginPayloadsEvent extends Event {
|
||||
private final List<NetworkRegistry.LoginPayload> collected;
|
||||
|
||||
public GatherLoginPayloadsEvent(final List<NetworkRegistry.LoginPayload> loginPayloadList) {
|
||||
this.collected = loginPayloadList;
|
||||
}
|
||||
|
||||
public void add(PacketBuffer buffer, ResourceLocation channelName, String context) {
|
||||
collected.add(new NetworkRegistry.LoginPayload(buffer, channelName, context));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoginPayloadEvent extends NetworkEvent {
|
||||
LoginPayloadEvent(final PacketBuffer payload, final Supplier<Context> source, final int loginIndex) {
|
||||
super(payload, source, loginIndex);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Context for {@link NetworkEvent}
|
||||
*/
|
||||
|
@ -96,20 +126,34 @@ public class NetworkEvent extends Event
|
|||
* The {@link NetworkDirection} this message has been received on.
|
||||
*/
|
||||
private final NetworkDirection side;
|
||||
|
||||
/**
|
||||
* The packet dispatcher for this event. Sends back to the origin.
|
||||
*/
|
||||
private final PacketDispatcher packetDispatcher;
|
||||
private boolean packetHandled;
|
||||
|
||||
Context(NetworkManager netHandler, NetworkDirection side)
|
||||
Context(NetworkManager netHandler, NetworkDirection side, int index)
|
||||
{
|
||||
this.networkManager = netHandler;
|
||||
this(netHandler, side, new PacketDispatcher.NetworkManagerDispatcher(netHandler, index, side.reply()::buildPacket));
|
||||
}
|
||||
|
||||
Context(NetworkManager networkManager, NetworkDirection side, PacketDispatcher dispatcher) {
|
||||
this.networkManager = networkManager;
|
||||
this.side = side;
|
||||
this.packetDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
public NetworkDirection getDirection() {
|
||||
return side;
|
||||
}
|
||||
|
||||
public NetworkManager getNetworkManager() {
|
||||
return networkManager;
|
||||
public PacketDispatcher getPacketDispatcher() {
|
||||
return packetDispatcher;
|
||||
}
|
||||
|
||||
public <T> Attribute<T> attr(AttributeKey<T> key) {
|
||||
return networkManager.channel().attr(key);
|
||||
}
|
||||
|
||||
public void setPacketHandled(boolean packetHandled) {
|
||||
|
@ -125,5 +169,9 @@ public class NetworkEvent extends Event
|
|||
public <V> ListenableFuture<V> enqueueWork(Runnable runnable) {
|
||||
return (ListenableFuture<V>)LogicalSidedProvider.WORKQUEUE.<IThreadListener>get(getDirection().getLogicalSide()).addScheduledTask(runnable);
|
||||
}
|
||||
|
||||
NetworkManager getNetworkManager() {
|
||||
return networkManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import net.minecraft.network.NetHandlerPlayServer;
|
|||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.handshake.client.CPacketHandshake;
|
||||
import net.minecraft.network.login.client.CPacketCustomPayloadLogin;
|
||||
import net.minecraft.server.network.NetHandlerLoginServer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
|
@ -35,7 +34,7 @@ public class NetworkHooks
|
|||
{
|
||||
public static final String NETVERSION = "FML1";
|
||||
public static final String NOVERSION = "NONE";
|
||||
static final ResourceLocation FMLHANDSHAKE = new ResourceLocation("fml", "handshake");
|
||||
|
||||
public static String getFMLVersion(final String ip)
|
||||
{
|
||||
return ip.contains("\0") ? Objects.equals(ip.split("\0")[1], NETVERSION) ? NETVERSION : ip.split("\0")[1] : NOVERSION;
|
||||
|
@ -43,7 +42,7 @@ public class NetworkHooks
|
|||
|
||||
public static boolean accepts(final CPacketHandshake packet)
|
||||
{
|
||||
return Objects.equals(packet.getFMLVersion(), NETVERSION);
|
||||
return Objects.equals(packet.getFMLVersion(), NETVERSION) || NetworkRegistry.acceptsVanillaConnections();
|
||||
}
|
||||
|
||||
public static ConnectionType getConnectionType(final NetHandlerPlayServer connection)
|
||||
|
@ -64,17 +63,17 @@ public class NetworkHooks
|
|||
public static void registerServerLoginChannel(NetworkManager manager, CPacketHandshake packet)
|
||||
{
|
||||
manager.channel().attr(FMLNetworking.FML_MARKER).set(packet.getFMLVersion());
|
||||
FMLNetworking.registerHandshake(manager, NetworkDirection.LOGIN_TO_CLIENT);
|
||||
FMLHandshakeHandler.registerHandshake(manager, NetworkDirection.LOGIN_TO_CLIENT);
|
||||
}
|
||||
|
||||
public static void registerClientLoginChannel(NetworkManager manager)
|
||||
{
|
||||
manager.channel().attr(FMLNetworking.FML_MARKER).set(NETVERSION);
|
||||
FMLNetworking.registerHandshake(manager, NetworkDirection.LOGIN_TO_SERVER);
|
||||
FMLHandshakeHandler.registerHandshake(manager, NetworkDirection.LOGIN_TO_SERVER);
|
||||
}
|
||||
|
||||
public static boolean tickNegotiation(NetHandlerLoginServer netHandlerLoginServer, NetworkManager networkManager, EntityPlayerMP player)
|
||||
{
|
||||
return FMLNetworking.dispatch(networkManager);
|
||||
return FMLHandshakeHandler.tickLogin(networkManager);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,15 @@
|
|||
|
||||
package net.minecraftforge.fml.network;
|
||||
|
||||
import net.minecraft.nbt.INBTBase;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.IEventListener;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -63,6 +64,11 @@ public class NetworkInstance
|
|||
this.networkEventBus.addListener(eventListener);
|
||||
}
|
||||
|
||||
public void addGatherListener(Consumer<NetworkEvent.GatherLoginPayloadsEvent> eventListener)
|
||||
{
|
||||
this.networkEventBus.addListener(eventListener);
|
||||
}
|
||||
|
||||
public void registerObject(final Object object) {
|
||||
this.networkEventBus.register(object);
|
||||
}
|
||||
|
@ -73,7 +79,7 @@ public class NetworkInstance
|
|||
|
||||
boolean dispatch(final NetworkDirection side, final ICustomPacket<?> packet, final NetworkManager manager)
|
||||
{
|
||||
final NetworkEvent.Context context = new NetworkEvent.Context(manager, side);
|
||||
final NetworkEvent.Context context = new NetworkEvent.Context(manager, side, packet.getIndex());
|
||||
this.networkEventBus.post(side.getEvent(packet, () -> context));
|
||||
return context.getPacketHandled();
|
||||
}
|
||||
|
@ -90,4 +96,12 @@ public class NetworkInstance
|
|||
boolean tryClientVersionOnServer(final String clientVersion) {
|
||||
return this.serverAcceptedVersions.test(clientVersion);
|
||||
}
|
||||
|
||||
void dispatchGatherLogin(final List<NetworkRegistry.LoginPayload> loginPayloadList) {
|
||||
this.networkEventBus.post(new NetworkEvent.GatherLoginPayloadsEvent(loginPayloadList));
|
||||
}
|
||||
|
||||
void dispatchLoginPacket(final NetworkEvent.LoginPayloadEvent loginPayloadEvent) {
|
||||
this.networkEventBus.post(loginPayloadEvent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ package net.minecraftforge.fml.network;
|
|||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.network.event.EventNetworkChannel;
|
||||
import net.minecraftforge.fml.network.simple.SimpleChannel;
|
||||
|
@ -30,34 +32,80 @@ import org.apache.logging.log4j.Logger;
|
|||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The network registry. Tracks channels on behalf of mods.
|
||||
*/
|
||||
public class NetworkRegistry
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Marker NETREGISTRY = MarkerManager.getMarker("NETREGISTRY");
|
||||
|
||||
private static Map<ResourceLocation, NetworkInstance> instances = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Special value for clientAcceptedVersions and serverAcceptedVersions predicates indicating the other side lacks
|
||||
* this channel.
|
||||
*/
|
||||
@SuppressWarnings("RedundantStringConstructorCall")
|
||||
public static String ABSENT = new String("ABSENT \uD83E\uDD14");
|
||||
|
||||
public static List<String> getNonVanillaNetworkMods()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
return instances.keySet().stream().map(Object::toString).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static boolean acceptsVanillaConnections() {
|
||||
return instances.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link SimpleChannel}.
|
||||
*
|
||||
* @param name The registry name for this channel. Must be unique
|
||||
* @param networkProtocolVersion The network protocol version string that will be offered to the remote side {@link ChannelBuilder#networkProtocolVersion(Supplier)}
|
||||
* @param clientAcceptedVersions Called on the client with the networkProtocolVersion string from the server {@link ChannelBuilder#clientAcceptedVersions(Predicate)}
|
||||
* @param serverAcceptedVersions Called on the server with the networkProtocolVersion string from the client {@link ChannelBuilder#serverAcceptedVersions(Predicate)}
|
||||
* @return A new {@link SimpleChannel}
|
||||
*
|
||||
* @see ChannelBuilder#newSimpleChannel(ResourceLocation, Supplier, Predicate, Predicate)
|
||||
*/
|
||||
public static SimpleChannel newSimpleChannel(final ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions) {
|
||||
return new SimpleChannel(createInstance(name, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link EventNetworkChannel}.
|
||||
*
|
||||
* @param name The registry name for this channel. Must be unique
|
||||
* @param networkProtocolVersion The network protocol version string that will be offered to the remote side {@link ChannelBuilder#networkProtocolVersion(Supplier)}
|
||||
* @param clientAcceptedVersions Called on the client with the networkProtocolVersion string from the server {@link ChannelBuilder#clientAcceptedVersions(Predicate)}
|
||||
* @param serverAcceptedVersions Called on the server with the networkProtocolVersion string from the client {@link ChannelBuilder#serverAcceptedVersions(Predicate)}
|
||||
|
||||
* @return A new {@link EventNetworkChannel}
|
||||
*
|
||||
* @see ChannelBuilder#newEventChannel(ResourceLocation, Supplier, Predicate, Predicate)
|
||||
*/
|
||||
public static EventNetworkChannel newEventChannel(final ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions) {
|
||||
return new EventNetworkChannel(createInstance(name, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the internal {@link NetworkInstance} that tracks the channel data.
|
||||
* @param name registry name
|
||||
* @param networkProtocolVersion The protocol version string
|
||||
* @param clientAcceptedVersions The client accepted predicate
|
||||
* @param serverAcceptedVersions The server accepted predicate
|
||||
* @return The {@link NetworkInstance}
|
||||
* @throws IllegalArgumentException if the name already exists
|
||||
*/
|
||||
private static NetworkInstance createInstance(ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions)
|
||||
{
|
||||
final NetworkInstance networkInstance = new NetworkInstance(name, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
|
||||
|
@ -69,11 +117,25 @@ public class NetworkRegistry
|
|||
return networkInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the {@link NetworkInstance}, if possible
|
||||
*
|
||||
* @param resourceLocation The network instance to lookup
|
||||
* @return The {@link Optional} {@link NetworkInstance}
|
||||
*/
|
||||
static Optional<NetworkInstance> findTarget(ResourceLocation resourceLocation)
|
||||
{
|
||||
return Optional.ofNullable(instances.get(resourceLocation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the NBT representation of the channel list, for use during login handshaking
|
||||
*
|
||||
* @see FMLHandshakeMessages.S2CModList
|
||||
* @see FMLHandshakeMessages.C2SModListReply
|
||||
*
|
||||
* @return An nbt tag list
|
||||
*/
|
||||
static NBTTagList buildChannelVersions() {
|
||||
return instances.entrySet().stream().map(e-> {
|
||||
final NBTTagCompound tag = new NBTTagCompound();
|
||||
|
@ -83,50 +145,120 @@ public class NetworkRegistry
|
|||
}).collect(Collectors.toCollection(NBTTagList::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the channels from the server on the client. Tests the client predicates against the server
|
||||
* supplied network protocol version.
|
||||
*
|
||||
* @param channels An @{@link NBTTagList} of name->version pairs for testing
|
||||
* @return true if all channels accept themselves
|
||||
*/
|
||||
static boolean validateClientChannels(final NBTTagList channels) {
|
||||
final List<Pair<ResourceLocation, Boolean>> results = channels.stream().map(t -> {
|
||||
NBTTagCompound tag = (NBTTagCompound) t;
|
||||
final ResourceLocation rl = new ResourceLocation(tag.getString("name"));
|
||||
final String serverVersion = tag.getString("version");
|
||||
boolean test = instances.get(rl).tryServerVersionOnClient(serverVersion);
|
||||
LOGGER.debug(NETREGISTRY, "Channel {} : Client version test of ''{}'' from server : {}", rl, serverVersion, test);
|
||||
return Pair.of(rl, test);
|
||||
}).filter(p->!p.getRight()).collect(Collectors.toList());
|
||||
|
||||
if (!results.isEmpty()) {
|
||||
LOGGER.error(NETREGISTRY, "Channels [{}] rejected their server side version number",
|
||||
results.stream().map(Pair::getLeft).map(Object::toString).collect(Collectors.joining(",")));
|
||||
return false;
|
||||
}
|
||||
LOGGER.debug(NETREGISTRY, "Accepting channel list from server");
|
||||
return true;
|
||||
return validateChannels(channels, "server", NetworkInstance::tryServerVersionOnClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the channels from the client on the server. Tests the server predicates against the client
|
||||
* supplied network protocol version.
|
||||
* @param channels An @{@link NBTTagList} of name->version pairs for testing
|
||||
* @return true if all channels accept themselves
|
||||
*/
|
||||
static boolean validateServerChannels(final NBTTagList channels) {
|
||||
final List<Pair<ResourceLocation, Boolean>> results = channels.stream().map(t -> {
|
||||
NBTTagCompound tag = (NBTTagCompound) t;
|
||||
final ResourceLocation rl = new ResourceLocation(tag.getString("name"));
|
||||
final String clientVersion = tag.getString("version");
|
||||
boolean test = instances.get(rl).tryClientVersionOnServer(clientVersion);
|
||||
LOGGER.debug(NETREGISTRY, "Channel {} : Server version test of ''{}'' from client : {}", rl, clientVersion, test);
|
||||
return Pair.of(rl, test);
|
||||
return validateChannels(channels, "client", NetworkInstance::tryClientVersionOnServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the nbt list matches with the supplied predicate tester
|
||||
*
|
||||
* @param channels An @{@link NBTTagList} of name->version pairs for testing
|
||||
* @param originName A label for use in logging (where the version pairs came from)
|
||||
* @param testFunction The test function to use for testing
|
||||
* @return true if all channels accept themselves
|
||||
*/
|
||||
private static boolean validateChannels(final NBTTagList channels, final String originName, BiFunction<NetworkInstance, String, Boolean> testFunction) {
|
||||
Map<ResourceLocation, String> incoming = channels.stream().map(NBTTagCompound.class::cast).collect(Collectors.toMap(tag->new ResourceLocation(tag.getString("name")),tag->tag.getString("version")));
|
||||
|
||||
final List<Pair<ResourceLocation, Boolean>> results = instances.values().stream().
|
||||
map(ni -> {
|
||||
final String incomingVersion = incoming.getOrDefault(ni.getChannelName(), ABSENT);
|
||||
final boolean test = testFunction.apply(ni, incomingVersion);
|
||||
LOGGER.debug(NETREGISTRY, "Channel '{}' : Version test of '{}' from {} : {}", ni.getChannelName(), incomingVersion, originName, test ? "ACCEPTED" : "REJECTED");
|
||||
return Pair.of(ni.getChannelName(), test);
|
||||
}).filter(p->!p.getRight()).collect(Collectors.toList());
|
||||
|
||||
if (!results.isEmpty()) {
|
||||
LOGGER.error(NETREGISTRY, "Channels [{}] rejected their client side version number",
|
||||
results.stream().map(Pair::getLeft).map(Object::toString).collect(Collectors.joining(",")));
|
||||
LOGGER.error(NETREGISTRY, "Channels [{}] rejected their {} side version number",
|
||||
results.stream().map(Pair::getLeft).map(Object::toString).collect(Collectors.joining(",")),
|
||||
originName);
|
||||
return false;
|
||||
}
|
||||
LOGGER.debug(NETREGISTRY, "Accepting channel list from client");
|
||||
LOGGER.debug(NETREGISTRY, "Accepting channel list from {}", originName);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the {@link LoginPayload} list for dispatch during {@link FMLHandshakeHandler#tickLogin(NetworkManager)} handling.
|
||||
* Dispatches {@link net.minecraftforge.fml.network.NetworkEvent.GatherLoginPayloadsEvent} to each {@link NetworkInstance}.
|
||||
*
|
||||
* @return The {@link LoginPayload} list
|
||||
*/
|
||||
static List<LoginPayload> gatherLoginPayloads() {
|
||||
List<LoginPayload> gatheredPayloads = new ArrayList<>();
|
||||
instances.values().forEach(ni->ni.dispatchGatherLogin(gatheredPayloads));
|
||||
return gatheredPayloads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks individual outbound messages for dispatch to clients during login handling. Gathered by dispatching
|
||||
* {@link net.minecraftforge.fml.network.NetworkEvent.GatherLoginPayloadsEvent} during early connection handling.
|
||||
*/
|
||||
public static class LoginPayload {
|
||||
/**
|
||||
* The data for sending
|
||||
*/
|
||||
private final PacketBuffer data;
|
||||
/**
|
||||
* A channel which will receive a {@link NetworkEvent.LoginPayloadEvent} from the {@link FMLLoginWrapper}
|
||||
*/
|
||||
private final ResourceLocation channelName;
|
||||
|
||||
/**
|
||||
* Some context for logging purposes
|
||||
*/
|
||||
private final String messageContext;
|
||||
|
||||
public LoginPayload(final PacketBuffer buffer, final ResourceLocation channelName, final String messageContext) {
|
||||
this.data = buffer;
|
||||
this.channelName = channelName;
|
||||
this.messageContext = messageContext;
|
||||
}
|
||||
|
||||
public PacketBuffer getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public ResourceLocation getChannelName() {
|
||||
return channelName;
|
||||
}
|
||||
|
||||
public String getMessageContext() {
|
||||
return messageContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for constructing network channels using a builder style API.
|
||||
*/
|
||||
public static class ChannelBuilder {
|
||||
private ResourceLocation channelName;
|
||||
private Supplier<String> networkProtocolVersion;
|
||||
private Predicate<String> clientAcceptedVersions;
|
||||
private Predicate<String> serverAcceptedVersions;
|
||||
|
||||
/**
|
||||
* The name of the channel. Must be unique.
|
||||
* @param channelName The name of the channel
|
||||
* @return the channel builder
|
||||
*/
|
||||
public static ChannelBuilder named(ResourceLocation channelName)
|
||||
{
|
||||
ChannelBuilder builder = new ChannelBuilder();
|
||||
|
@ -134,32 +266,68 @@ public class NetworkRegistry
|
|||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* The network protocol string for this channel. This will be gathered during login and sent to
|
||||
* the remote partner, where it will be tested with against the relevant predicate.
|
||||
*
|
||||
* @see #serverAcceptedVersions(Predicate)
|
||||
* @see #clientAcceptedVersions(Predicate)
|
||||
* @param networkProtocolVersion A supplier of strings for network protocol version testing
|
||||
* @return the channel builder
|
||||
*/
|
||||
public ChannelBuilder networkProtocolVersion(Supplier<String> networkProtocolVersion)
|
||||
{
|
||||
this.networkProtocolVersion = networkProtocolVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate run on the client, with the {@link #networkProtocolVersion(Supplier)} string from
|
||||
* the server, or the special value {@link NetworkRegistry#ABSENT} indicating the absence of
|
||||
* the channel on the remote side.
|
||||
* @param clientAcceptedVersions A predicate for testing
|
||||
* @return the channel builder
|
||||
*/
|
||||
public ChannelBuilder clientAcceptedVersions(Predicate<String> clientAcceptedVersions)
|
||||
{
|
||||
this.clientAcceptedVersions = clientAcceptedVersions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A predicate run on the server, with the {@link #networkProtocolVersion(Supplier)} string from
|
||||
* the server, or the special value {@link NetworkRegistry#ABSENT} indicating the absence of
|
||||
* the channel on the remote side.
|
||||
* @param serverAcceptedVersions A predicate for testing
|
||||
* @return the channel builder
|
||||
*/
|
||||
public ChannelBuilder serverAcceptedVersions(Predicate<String> serverAcceptedVersions)
|
||||
{
|
||||
this.serverAcceptedVersions = serverAcceptedVersions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the network instance
|
||||
* @return the {@link NetworkInstance}
|
||||
*/
|
||||
private NetworkInstance createNetworkInstance() {
|
||||
return createInstance(channelName, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link SimpleChannel} with this builder's configuration.
|
||||
*
|
||||
* @return A new {@link SimpleChannel}
|
||||
*/
|
||||
public SimpleChannel simpleChannel() {
|
||||
return new SimpleChannel(createNetworkInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link EventNetworkChannel} with this builder's configuration.
|
||||
* @return A new {@link EventNetworkChannel}
|
||||
*/
|
||||
public EventNetworkChannel eventNetworkChannel() {
|
||||
return new EventNetworkChannel(createNetworkInstance());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package net.minecraftforge.fml.network;
|
||||
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Dispatcher for sending packets in response to a received packet. Abstracts out the difference between wrapped packets
|
||||
* and unwrapped packets.
|
||||
*/
|
||||
public class PacketDispatcher {
|
||||
BiConsumer<ResourceLocation, PacketBuffer> packetSink;
|
||||
|
||||
PacketDispatcher(final BiConsumer<ResourceLocation, PacketBuffer> packetSink) {
|
||||
this.packetSink = packetSink;
|
||||
}
|
||||
|
||||
private PacketDispatcher() {
|
||||
|
||||
}
|
||||
|
||||
public void sendPacket(ResourceLocation resourceLocation, PacketBuffer buffer) {
|
||||
packetSink.accept(resourceLocation, buffer);
|
||||
}
|
||||
|
||||
static class NetworkManagerDispatcher extends PacketDispatcher {
|
||||
private final NetworkManager manager;
|
||||
private final int packetIndex;
|
||||
private final BiFunction<Pair<PacketBuffer, Integer>, ResourceLocation, ICustomPacket<?>> customPacketSupplier;
|
||||
|
||||
NetworkManagerDispatcher(NetworkManager manager, int packetIndex, BiFunction<Pair<PacketBuffer, Integer>, ResourceLocation, ICustomPacket<?>> customPacketSupplier) {
|
||||
super();
|
||||
this.packetSink = this::dispatchPacket;
|
||||
this.manager = manager;
|
||||
this.packetIndex = packetIndex;
|
||||
this.customPacketSupplier = customPacketSupplier;
|
||||
}
|
||||
|
||||
private void dispatchPacket(final ResourceLocation resourceLocation, final PacketBuffer buffer) {
|
||||
final ICustomPacket<?> packet = this.customPacketSupplier.apply(Pair.of(buffer, packetIndex), resourceLocation);
|
||||
this.manager.sendPacket(packet.getThis());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,6 +45,11 @@ public class IndexedMessageCodec
|
|||
return (MessageHandler<MSG>) types.get(msgToReply.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<MSG> MessageHandler<MSG> findIndex(final short i) {
|
||||
return (MessageHandler<MSG>) indicies.get(i);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
class MessageHandler<MSG>
|
||||
{
|
||||
|
@ -85,6 +90,15 @@ public class IndexedMessageCodec
|
|||
public Optional<Function<MSG, Integer>> getLoginIndexGetter() {
|
||||
return this.loginIndexGetter;
|
||||
}
|
||||
|
||||
MSG newInstance() {
|
||||
try {
|
||||
return messageType.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
LOGGER.error("Invalid login message", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <M> void tryDecode(PacketBuffer payload, Supplier<NetworkEvent.Context> context, int payloadIndex, MessageHandler<M> codec)
|
||||
|
|
|
@ -24,12 +24,12 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.ICustomPacket;
|
||||
import net.minecraftforge.fml.network.NetworkDirection;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraftforge.fml.network.NetworkInstance;
|
||||
import net.minecraftforge.fml.network.*;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -38,14 +38,26 @@ public class SimpleChannel
|
|||
{
|
||||
private final NetworkInstance instance;
|
||||
private final IndexedMessageCodec indexedCodec;
|
||||
private List<Supplier<? extends List<? extends Pair<String,?>>>> loginPackets;
|
||||
|
||||
public SimpleChannel(NetworkInstance instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
this.indexedCodec = new IndexedMessageCodec();
|
||||
this.loginPackets = new ArrayList<>();
|
||||
instance.addListener(this::networkEventListener);
|
||||
instance.addGatherListener(this::networkLoginGather);
|
||||
}
|
||||
|
||||
private void networkLoginGather(final NetworkEvent.GatherLoginPayloadsEvent gatherEvent) {
|
||||
loginPackets.forEach(packetGenerator->{
|
||||
packetGenerator.get().forEach(p->{
|
||||
PacketBuffer pb = new PacketBuffer(Unpooled.buffer());
|
||||
this.indexedCodec.build(p.getRight(), pb);
|
||||
gatherEvent.add(pb, this.instance.getChannelName(), p.getLeft());
|
||||
});
|
||||
});
|
||||
}
|
||||
private void networkEventListener(final NetworkEvent networkEvent)
|
||||
{
|
||||
this.indexedCodec.consume(networkEvent.getPayload(), networkEvent.getLoginIndex(), networkEvent.getSource());
|
||||
|
@ -77,7 +89,7 @@ public class SimpleChannel
|
|||
|
||||
public <MSG> void reply(MSG msgToReply, NetworkEvent.Context context)
|
||||
{
|
||||
sendTo(msgToReply, context.getNetworkManager(), context.getDirection().reply());
|
||||
context.getPacketDispatcher().sendPacket(instance.getChannelName(), toBuffer(msgToReply).getLeft());
|
||||
}
|
||||
|
||||
public <M> MessageBuilder<M> messageBuilder(final Class<M> type, int id) {
|
||||
|
@ -93,6 +105,7 @@ public class SimpleChannel
|
|||
private BiConsumer<MSG, Supplier<NetworkEvent.Context>> consumer;
|
||||
private Function<MSG, Integer> loginIndexGetter;
|
||||
private BiConsumer<MSG, Integer> loginIndexSetter;
|
||||
private Supplier<List<Pair<String,MSG>>> loginPacketGenerators;
|
||||
|
||||
private static <MSG> MessageBuilder<MSG> forType(final SimpleChannel channel, final Class<MSG> type, int id) {
|
||||
MessageBuilder<MSG> builder = new MessageBuilder<>();
|
||||
|
@ -117,6 +130,24 @@ public class SimpleChannel
|
|||
this.loginIndexSetter = loginIndexSetter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageBuilder<MSG> buildLoginPacketList(Supplier<List<Pair<String,MSG>>> loginPacketGenerators) {
|
||||
this.loginPacketGenerators = loginPacketGenerators;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageBuilder<MSG> markAsLoginPacket()
|
||||
{
|
||||
this.loginPacketGenerators = () -> {
|
||||
try {
|
||||
return Collections.singletonList(Pair.of(type.getName(), type.newInstance()));
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException("Inaccessible no-arg constructor for message "+type.getName(),e);
|
||||
}
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageBuilder<MSG> consumer(BiConsumer<MSG, Supplier<NetworkEvent.Context>> consumer) {
|
||||
this.consumer = consumer;
|
||||
return this;
|
||||
|
@ -130,6 +161,9 @@ public class SimpleChannel
|
|||
if (this.loginIndexGetter != null) {
|
||||
message.setLoginIndexGetter(this.loginIndexGetter);
|
||||
}
|
||||
if (this.loginPacketGenerators != null) {
|
||||
this.channel.loginPackets.add(this.loginPacketGenerators);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/main/java/net/minecraftforge/fml/util/ThreeConsumer.java
Normal file
28
src/main/java/net/minecraftforge/fml/util/ThreeConsumer.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package net.minecraftforge.fml.util;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Three-consumer version of consumer. Allows wrapping methods with three arguments.
|
||||
*/
|
||||
public interface ThreeConsumer<T, U, V>
|
||||
{
|
||||
|
||||
/**
|
||||
* Bind arguments to the three consumer to generate a consumer.
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* ThreeConsumer.bindArgs(MyClass::instanceMethodReference, arg1, arg2).apply(myClassInstance)
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a Consumer which has the second and third arguments bound.
|
||||
*/
|
||||
static <T, U, V> Consumer<T> bindArgs(ThreeConsumer<? super T, U, V> c, U arg2, V arg3)
|
||||
{
|
||||
return (arg1) -> c.accept(arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
void accept(T t, U u, V v);
|
||||
}
|
|
@ -19,8 +19,11 @@
|
|||
|
||||
package net.minecraftforge.registries;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -31,8 +34,11 @@ import com.google.common.collect.Sets;
|
|||
import com.google.common.collect.Sets.SetView;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.network.FMLHandshakeMessages;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistry.Snapshot;
|
||||
import net.minecraftforge.registries.IForgeRegistry.*;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
@ -145,4 +151,14 @@ public class RegistryManager
|
|||
this.registries.clear();
|
||||
this.superTypes.clear();
|
||||
}
|
||||
|
||||
public static List<Pair<String, FMLHandshakeMessages.S2CRegistry>> generateRegistryPackets() {
|
||||
return ACTIVE.registries.entrySet().stream().
|
||||
map(e->Pair.of("Registry "+e.getKey(), new FMLHandshakeMessages.S2CRegistry(e.getKey(), e.getValue()))).
|
||||
collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static void acceptRegistry(final FMLHandshakeMessages.S2CRegistry registryUpdate, final Supplier<NetworkEvent.Context> contextSupplier) {
|
||||
LOGGER.debug("Received registry packet");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue