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') {
|
project(':forge') {
|
||||||
evaluationDependsOn(':clean')
|
evaluationDependsOn(':clean')
|
||||||
|
apply plugin: 'java-library'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
apply plugin: 'net.minecraftforge.gradle.forgedev.patcher'
|
apply plugin: 'net.minecraftforge.gradle.forgedev.patcher'
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
@ -95,17 +96,17 @@ project(':forge') {
|
||||||
maxFuzz 3
|
maxFuzz 3
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'net.minecraft:client:1.13:extra'
|
api 'net.minecraft:client:1.13:extra'
|
||||||
implementation 'cpw.mods:modlauncher:0.1.0-rc.3' //Pinned until cpw fixes getCommonSupertype
|
api 'cpw.mods:modlauncher:0.1.0-rc.3' //Pinned until cpw fixes getCommonSupertype
|
||||||
implementation 'net.minecraftforge:accesstransformers:0.10+:shadowed'
|
api 'net.minecraftforge:accesstransformers:0.10+:shadowed'
|
||||||
implementation 'net.minecraftforge:eventbus:0.1+:service'
|
api 'net.minecraftforge:eventbus:0.1+:service'
|
||||||
implementation 'net.minecraftforge:forgespi:0.1+'
|
api 'net.minecraftforge:forgespi:0.1+'
|
||||||
implementation 'net.minecraftforge:coremods:0.1+'
|
api 'net.minecraftforge:coremods:0.1+'
|
||||||
implementation 'com.electronwill.night-config:core:3.4.0'
|
api 'com.electronwill.night-config:core:3.4.0'
|
||||||
implementation 'com.electronwill.night-config:toml:3.4.0'
|
api 'com.electronwill.night-config:toml:3.4.0'
|
||||||
implementation 'org.jline:jline:3.5.1'
|
api 'org.jline:jline:3.5.1'
|
||||||
implementation 'org.apache.maven:maven-artifact:3.5.3'
|
api 'org.apache.maven:maven-artifact:3.5.3'
|
||||||
implementation 'java3d:vecmath:1.5.2'
|
api 'java3d:vecmath:1.5.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
float f7 = f2 * f4;
|
float f7 = f2 * f4;
|
||||||
- double d3 = 5.0D;
|
- double d3 = 5.0D;
|
||||||
- Vec3d vec3d1 = vec3d.add((double)f6 * 5.0D, (double)f5 * 5.0D, (double)f7 * 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);
|
+ 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);
|
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(axformFilter);
|
||||||
logcontext.getConfiguration().addFilter(eventbusFilter);
|
logcontext.getConfiguration().addFilter(eventbusFilter);
|
||||||
logcontext.updateLoggers();
|
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()) {
|
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 assets = System.getenv().getOrDefault("assetDirectory", "assets");
|
||||||
String target = System.getenv().get("target");
|
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.NBTTagList;
|
||||||
import net.minecraft.nbt.NBTTagString;
|
import net.minecraft.nbt.NBTTagString;
|
||||||
import net.minecraft.network.PacketBuffer;
|
import net.minecraft.network.PacketBuffer;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistry;
|
||||||
|
import net.minecraftforge.registries.IForgeRegistryEntry;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
class FMLHandshakeMessage
|
public class FMLHandshakeMessages
|
||||||
{
|
{
|
||||||
// Login index sequence number
|
static class LoginIndexedMessage {
|
||||||
private int index;
|
private int loginIndex;
|
||||||
void setPacketIndexSequence(int i)
|
|
||||||
{
|
|
||||||
this.index = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getPacketIndexSequence()
|
void setLoginIndex(final int loginIndex) {
|
||||||
{
|
this.loginIndex = loginIndex;
|
||||||
return index;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
int getLoginIndex() {
|
||||||
|
return loginIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Server to client "list of mods". Always first handshake message.
|
* 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 NBTTagList channels;
|
||||||
private List<String> modList;
|
private List<String> modList;
|
||||||
|
|
||||||
S2CModList() {
|
public S2CModList() {
|
||||||
this.modList = ModList.get().getMods().stream().map(ModInfo::getModId).collect(Collectors.toList());
|
this.modList = ModList.get().getMods().stream().map(ModInfo::getModId).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,13 +45,13 @@ class FMLHandshakeMessage
|
||||||
this.channels = nbtTagCompound.getTagList("channels", 10);
|
this.channels = nbtTagCompound.getTagList("channels", 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
static S2CModList decode(PacketBuffer packetBuffer)
|
public static S2CModList decode(PacketBuffer packetBuffer)
|
||||||
{
|
{
|
||||||
final NBTTagCompound nbtTagCompound = packetBuffer.readCompoundTag();
|
final NBTTagCompound nbtTagCompound = packetBuffer.readCompoundTag();
|
||||||
return new S2CModList(nbtTagCompound);
|
return new S2CModList(nbtTagCompound);
|
||||||
}
|
}
|
||||||
|
|
||||||
void encode(PacketBuffer packetBuffer)
|
public void encode(PacketBuffer packetBuffer)
|
||||||
{
|
{
|
||||||
NBTTagCompound tag = new NBTTagCompound();
|
NBTTagCompound tag = new NBTTagCompound();
|
||||||
tag.setTag("modlist",modList.stream().map(NBTTagString::new).collect(Collectors.toCollection(NBTTagList::new)));
|
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();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@ class FMLHandshakeMessage
|
||||||
super(buffer);
|
super(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static C2SModListReply decode(PacketBuffer buffer)
|
public static C2SModListReply decode(PacketBuffer buffer)
|
||||||
{
|
{
|
||||||
return new C2SModListReply(buffer.readCompoundTag());
|
return new C2SModListReply(buffer.readCompoundTag());
|
||||||
}
|
}
|
||||||
|
@ -86,4 +88,30 @@ class FMLHandshakeMessage
|
||||||
super.encode(buffer);
|
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 io.netty.util.AttributeKey;
|
||||||
import net.minecraft.network.NetworkManager;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.Marker;
|
import org.apache.logging.log4j.Marker;
|
||||||
import org.apache.logging.log4j.MarkerManager;
|
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
|
public class FMLNetworking
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
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");
|
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() {
|
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() {
|
default int getIndex() {
|
||||||
|
|
|
@ -20,12 +20,18 @@
|
||||||
package net.minecraftforge.fml.network;
|
package net.minecraftforge.fml.network;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
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.NetworkManager;
|
||||||
import net.minecraft.network.PacketBuffer;
|
import net.minecraft.network.PacketBuffer;
|
||||||
import net.minecraft.util.IThreadListener;
|
import net.minecraft.util.IThreadListener;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
import net.minecraftforge.fml.LogicalSidedProvider;
|
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;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class NetworkEvent extends Event
|
public class NetworkEvent extends Event
|
||||||
|
@ -41,6 +47,13 @@ public class NetworkEvent extends Event
|
||||||
this.loginIndex = payload.getIndex();
|
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()
|
public PacketBuffer getPayload()
|
||||||
{
|
{
|
||||||
return payload;
|
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}
|
* Context for {@link NetworkEvent}
|
||||||
*/
|
*/
|
||||||
|
@ -96,20 +126,34 @@ public class NetworkEvent extends Event
|
||||||
* The {@link NetworkDirection} this message has been received on.
|
* The {@link NetworkDirection} this message has been received on.
|
||||||
*/
|
*/
|
||||||
private final NetworkDirection side;
|
private final NetworkDirection side;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packet dispatcher for this event. Sends back to the origin.
|
||||||
|
*/
|
||||||
|
private final PacketDispatcher packetDispatcher;
|
||||||
private boolean packetHandled;
|
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.side = side;
|
||||||
|
this.packetDispatcher = dispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkDirection getDirection() {
|
public NetworkDirection getDirection() {
|
||||||
return side;
|
return side;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkManager getNetworkManager() {
|
public PacketDispatcher getPacketDispatcher() {
|
||||||
return networkManager;
|
return packetDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Attribute<T> attr(AttributeKey<T> key) {
|
||||||
|
return networkManager.channel().attr(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPacketHandled(boolean packetHandled) {
|
public void setPacketHandled(boolean packetHandled) {
|
||||||
|
@ -125,5 +169,9 @@ public class NetworkEvent extends Event
|
||||||
public <V> ListenableFuture<V> enqueueWork(Runnable runnable) {
|
public <V> ListenableFuture<V> enqueueWork(Runnable runnable) {
|
||||||
return (ListenableFuture<V>)LogicalSidedProvider.WORKQUEUE.<IThreadListener>get(getDirection().getLogicalSide()).addScheduledTask(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.NetworkManager;
|
||||||
import net.minecraft.network.Packet;
|
import net.minecraft.network.Packet;
|
||||||
import net.minecraft.network.handshake.client.CPacketHandshake;
|
import net.minecraft.network.handshake.client.CPacketHandshake;
|
||||||
import net.minecraft.network.login.client.CPacketCustomPayloadLogin;
|
|
||||||
import net.minecraft.server.network.NetHandlerLoginServer;
|
import net.minecraft.server.network.NetHandlerLoginServer;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ public class NetworkHooks
|
||||||
{
|
{
|
||||||
public static final String NETVERSION = "FML1";
|
public static final String NETVERSION = "FML1";
|
||||||
public static final String NOVERSION = "NONE";
|
public static final String NOVERSION = "NONE";
|
||||||
static final ResourceLocation FMLHANDSHAKE = new ResourceLocation("fml", "handshake");
|
|
||||||
public static String getFMLVersion(final String ip)
|
public static String getFMLVersion(final String ip)
|
||||||
{
|
{
|
||||||
return ip.contains("\0") ? Objects.equals(ip.split("\0")[1], NETVERSION) ? NETVERSION : ip.split("\0")[1] : NOVERSION;
|
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)
|
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)
|
public static ConnectionType getConnectionType(final NetHandlerPlayServer connection)
|
||||||
|
@ -64,17 +63,17 @@ public class NetworkHooks
|
||||||
public static void registerServerLoginChannel(NetworkManager manager, CPacketHandshake packet)
|
public static void registerServerLoginChannel(NetworkManager manager, CPacketHandshake packet)
|
||||||
{
|
{
|
||||||
manager.channel().attr(FMLNetworking.FML_MARKER).set(packet.getFMLVersion());
|
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)
|
public static void registerClientLoginChannel(NetworkManager manager)
|
||||||
{
|
{
|
||||||
manager.channel().attr(FMLNetworking.FML_MARKER).set(NETVERSION);
|
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)
|
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;
|
package net.minecraftforge.fml.network;
|
||||||
|
|
||||||
import net.minecraft.nbt.INBTBase;
|
|
||||||
import net.minecraft.network.NetworkManager;
|
import net.minecraft.network.NetworkManager;
|
||||||
import net.minecraft.network.PacketBuffer;
|
import net.minecraft.network.PacketBuffer;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
import net.minecraftforge.eventbus.api.IEventBus;
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
import net.minecraftforge.eventbus.api.IEventListener;
|
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.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -63,6 +64,11 @@ public class NetworkInstance
|
||||||
this.networkEventBus.addListener(eventListener);
|
this.networkEventBus.addListener(eventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addGatherListener(Consumer<NetworkEvent.GatherLoginPayloadsEvent> eventListener)
|
||||||
|
{
|
||||||
|
this.networkEventBus.addListener(eventListener);
|
||||||
|
}
|
||||||
|
|
||||||
public void registerObject(final Object object) {
|
public void registerObject(final Object object) {
|
||||||
this.networkEventBus.register(object);
|
this.networkEventBus.register(object);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +79,7 @@ public class NetworkInstance
|
||||||
|
|
||||||
boolean dispatch(final NetworkDirection side, final ICustomPacket<?> packet, final NetworkManager manager)
|
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));
|
this.networkEventBus.post(side.getEvent(packet, () -> context));
|
||||||
return context.getPacketHandled();
|
return context.getPacketHandled();
|
||||||
}
|
}
|
||||||
|
@ -90,4 +96,12 @@ public class NetworkInstance
|
||||||
boolean tryClientVersionOnServer(final String clientVersion) {
|
boolean tryClientVersionOnServer(final String clientVersion) {
|
||||||
return this.serverAcceptedVersions.test(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.NBTTagCompound;
|
||||||
import net.minecraft.nbt.NBTTagList;
|
import net.minecraft.nbt.NBTTagList;
|
||||||
|
import net.minecraft.network.NetworkManager;
|
||||||
|
import net.minecraft.network.PacketBuffer;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraftforge.fml.network.event.EventNetworkChannel;
|
import net.minecraftforge.fml.network.event.EventNetworkChannel;
|
||||||
import net.minecraftforge.fml.network.simple.SimpleChannel;
|
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.Marker;
|
||||||
import org.apache.logging.log4j.MarkerManager;
|
import org.apache.logging.log4j.MarkerManager;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
import java.util.function.BiFunction;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The network registry. Tracks channels on behalf of mods.
|
||||||
|
*/
|
||||||
public class NetworkRegistry
|
public class NetworkRegistry
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
private static final Marker NETREGISTRY = MarkerManager.getMarker("NETREGISTRY");
|
private static final Marker NETREGISTRY = MarkerManager.getMarker("NETREGISTRY");
|
||||||
|
|
||||||
private static Map<ResourceLocation, NetworkInstance> instances = new HashMap<>();
|
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()
|
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) {
|
public static SimpleChannel newSimpleChannel(final ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions) {
|
||||||
return new SimpleChannel(createInstance(name, networkProtocolVersion, clientAcceptedVersions, 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) {
|
public static EventNetworkChannel newEventChannel(final ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions) {
|
||||||
return new EventNetworkChannel(createInstance(name, networkProtocolVersion, clientAcceptedVersions, 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)
|
private static NetworkInstance createInstance(ResourceLocation name, Supplier<String> networkProtocolVersion, Predicate<String> clientAcceptedVersions, Predicate<String> serverAcceptedVersions)
|
||||||
{
|
{
|
||||||
final NetworkInstance networkInstance = new NetworkInstance(name, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
|
final NetworkInstance networkInstance = new NetworkInstance(name, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
|
||||||
|
@ -69,11 +117,25 @@ public class NetworkRegistry
|
||||||
return networkInstance;
|
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)
|
static Optional<NetworkInstance> findTarget(ResourceLocation resourceLocation)
|
||||||
{
|
{
|
||||||
return Optional.ofNullable(instances.get(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() {
|
static NBTTagList buildChannelVersions() {
|
||||||
return instances.entrySet().stream().map(e-> {
|
return instances.entrySet().stream().map(e-> {
|
||||||
final NBTTagCompound tag = new NBTTagCompound();
|
final NBTTagCompound tag = new NBTTagCompound();
|
||||||
|
@ -83,50 +145,120 @@ public class NetworkRegistry
|
||||||
}).collect(Collectors.toCollection(NBTTagList::new));
|
}).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) {
|
static boolean validateClientChannels(final NBTTagList channels) {
|
||||||
final List<Pair<ResourceLocation, Boolean>> results = channels.stream().map(t -> {
|
return validateChannels(channels, "server", NetworkInstance::tryServerVersionOnClient);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
static boolean validateServerChannels(final NBTTagList channels) {
|
||||||
final List<Pair<ResourceLocation, Boolean>> results = channels.stream().map(t -> {
|
return validateChannels(channels, "client", NetworkInstance::tryClientVersionOnServer);
|
||||||
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);
|
* Tests if the nbt list matches with the supplied predicate tester
|
||||||
LOGGER.debug(NETREGISTRY, "Channel {} : Server version test of ''{}'' from client : {}", rl, clientVersion, test);
|
*
|
||||||
return Pair.of(rl, test);
|
* @param channels An @{@link NBTTagList} of name->version pairs for testing
|
||||||
}).filter(p->!p.getRight()).collect(Collectors.toList());
|
* @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()) {
|
if (!results.isEmpty()) {
|
||||||
LOGGER.error(NETREGISTRY, "Channels [{}] rejected their client side version number",
|
LOGGER.error(NETREGISTRY, "Channels [{}] rejected their {} side version number",
|
||||||
results.stream().map(Pair::getLeft).map(Object::toString).collect(Collectors.joining(",")));
|
results.stream().map(Pair::getLeft).map(Object::toString).collect(Collectors.joining(",")),
|
||||||
|
originName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOGGER.debug(NETREGISTRY, "Accepting channel list from client");
|
LOGGER.debug(NETREGISTRY, "Accepting channel list from {}", originName);
|
||||||
return true;
|
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 {
|
public static class ChannelBuilder {
|
||||||
private ResourceLocation channelName;
|
private ResourceLocation channelName;
|
||||||
private Supplier<String> networkProtocolVersion;
|
private Supplier<String> networkProtocolVersion;
|
||||||
private Predicate<String> clientAcceptedVersions;
|
private Predicate<String> clientAcceptedVersions;
|
||||||
private Predicate<String> serverAcceptedVersions;
|
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)
|
public static ChannelBuilder named(ResourceLocation channelName)
|
||||||
{
|
{
|
||||||
ChannelBuilder builder = new ChannelBuilder();
|
ChannelBuilder builder = new ChannelBuilder();
|
||||||
|
@ -134,32 +266,68 @@ public class NetworkRegistry
|
||||||
return builder;
|
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)
|
public ChannelBuilder networkProtocolVersion(Supplier<String> networkProtocolVersion)
|
||||||
{
|
{
|
||||||
this.networkProtocolVersion = networkProtocolVersion;
|
this.networkProtocolVersion = networkProtocolVersion;
|
||||||
return this;
|
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)
|
public ChannelBuilder clientAcceptedVersions(Predicate<String> clientAcceptedVersions)
|
||||||
{
|
{
|
||||||
this.clientAcceptedVersions = clientAcceptedVersions;
|
this.clientAcceptedVersions = clientAcceptedVersions;
|
||||||
return this;
|
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)
|
public ChannelBuilder serverAcceptedVersions(Predicate<String> serverAcceptedVersions)
|
||||||
{
|
{
|
||||||
this.serverAcceptedVersions = serverAcceptedVersions;
|
this.serverAcceptedVersions = serverAcceptedVersions;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the network instance
|
||||||
|
* @return the {@link NetworkInstance}
|
||||||
|
*/
|
||||||
private NetworkInstance createNetworkInstance() {
|
private NetworkInstance createNetworkInstance() {
|
||||||
return createInstance(channelName, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
|
return createInstance(channelName, networkProtocolVersion, clientAcceptedVersions, serverAcceptedVersions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new {@link SimpleChannel} with this builder's configuration.
|
||||||
|
*
|
||||||
|
* @return A new {@link SimpleChannel}
|
||||||
|
*/
|
||||||
public SimpleChannel simpleChannel() {
|
public SimpleChannel simpleChannel() {
|
||||||
return new SimpleChannel(createNetworkInstance());
|
return new SimpleChannel(createNetworkInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new {@link EventNetworkChannel} with this builder's configuration.
|
||||||
|
* @return A new {@link EventNetworkChannel}
|
||||||
|
*/
|
||||||
public EventNetworkChannel eventNetworkChannel() {
|
public EventNetworkChannel eventNetworkChannel() {
|
||||||
return new EventNetworkChannel(createNetworkInstance());
|
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());
|
return (MessageHandler<MSG>) types.get(msgToReply.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
<MSG> MessageHandler<MSG> findIndex(final short i) {
|
||||||
|
return (MessageHandler<MSG>) indicies.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
class MessageHandler<MSG>
|
class MessageHandler<MSG>
|
||||||
{
|
{
|
||||||
|
@ -85,6 +90,15 @@ public class IndexedMessageCodec
|
||||||
public Optional<Function<MSG, Integer>> getLoginIndexGetter() {
|
public Optional<Function<MSG, Integer>> getLoginIndexGetter() {
|
||||||
return this.loginIndexGetter;
|
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)
|
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.NetworkManager;
|
||||||
import net.minecraft.network.Packet;
|
import net.minecraft.network.Packet;
|
||||||
import net.minecraft.network.PacketBuffer;
|
import net.minecraft.network.PacketBuffer;
|
||||||
import net.minecraftforge.fml.network.ICustomPacket;
|
import net.minecraftforge.fml.network.*;
|
||||||
import net.minecraftforge.fml.network.NetworkDirection;
|
|
||||||
import net.minecraftforge.fml.network.NetworkEvent;
|
|
||||||
import net.minecraftforge.fml.network.NetworkInstance;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
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.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -38,14 +38,26 @@ public class SimpleChannel
|
||||||
{
|
{
|
||||||
private final NetworkInstance instance;
|
private final NetworkInstance instance;
|
||||||
private final IndexedMessageCodec indexedCodec;
|
private final IndexedMessageCodec indexedCodec;
|
||||||
|
private List<Supplier<? extends List<? extends Pair<String,?>>>> loginPackets;
|
||||||
|
|
||||||
public SimpleChannel(NetworkInstance instance)
|
public SimpleChannel(NetworkInstance instance)
|
||||||
{
|
{
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.indexedCodec = new IndexedMessageCodec();
|
this.indexedCodec = new IndexedMessageCodec();
|
||||||
|
this.loginPackets = new ArrayList<>();
|
||||||
instance.addListener(this::networkEventListener);
|
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)
|
private void networkEventListener(final NetworkEvent networkEvent)
|
||||||
{
|
{
|
||||||
this.indexedCodec.consume(networkEvent.getPayload(), networkEvent.getLoginIndex(), networkEvent.getSource());
|
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)
|
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) {
|
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 BiConsumer<MSG, Supplier<NetworkEvent.Context>> consumer;
|
||||||
private Function<MSG, Integer> loginIndexGetter;
|
private Function<MSG, Integer> loginIndexGetter;
|
||||||
private BiConsumer<MSG, Integer> loginIndexSetter;
|
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) {
|
private static <MSG> MessageBuilder<MSG> forType(final SimpleChannel channel, final Class<MSG> type, int id) {
|
||||||
MessageBuilder<MSG> builder = new MessageBuilder<>();
|
MessageBuilder<MSG> builder = new MessageBuilder<>();
|
||||||
|
@ -117,6 +130,24 @@ public class SimpleChannel
|
||||||
this.loginIndexSetter = loginIndexSetter;
|
this.loginIndexSetter = loginIndexSetter;
|
||||||
return this;
|
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) {
|
public MessageBuilder<MSG> consumer(BiConsumer<MSG, Supplier<NetworkEvent.Context>> consumer) {
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
return this;
|
return this;
|
||||||
|
@ -130,6 +161,9 @@ public class SimpleChannel
|
||||||
if (this.loginIndexGetter != null) {
|
if (this.loginIndexGetter != null) {
|
||||||
message.setLoginIndexGetter(this.loginIndexGetter);
|
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;
|
package net.minecraftforge.registries;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@ -31,8 +34,11 @@ import com.google.common.collect.Sets;
|
||||||
import com.google.common.collect.Sets.SetView;
|
import com.google.common.collect.Sets.SetView;
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
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.ForgeRegistry.Snapshot;
|
||||||
import net.minecraftforge.registries.IForgeRegistry.*;
|
import net.minecraftforge.registries.IForgeRegistry.*;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
@ -145,4 +151,14 @@ public class RegistryManager
|
||||||
this.registries.clear();
|
this.registries.clear();
|
||||||
this.superTypes.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