package net.minecraftforge.fml.common.network.handshake; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.NetworkRegistry; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import net.minecraftforge.fml.common.registry.GameData; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; public abstract class FMLHandshakeMessage { public static FMLProxyPacket makeCustomChannelRegistration(Set<String> channels) { String salutation = Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML|HS","FML", "FML|MP"),channels)); FMLProxyPacket proxy = new FMLProxyPacket(new PacketBuffer(Unpooled.wrappedBuffer(salutation.getBytes(Charsets.UTF_8))), "REGISTER"); return proxy; } public static class ServerHello extends FMLHandshakeMessage { private byte serverProtocolVersion; private int overrideDimension; public ServerHello() { // noargs for the proto } public ServerHello(int overrideDim) { this.overrideDimension = overrideDim; } @Override public void toBytes(ByteBuf buffer) { buffer.writeByte(NetworkRegistry.FML_PROTOCOL); buffer.writeInt(overrideDimension); } @Override public void fromBytes(ByteBuf buffer) { serverProtocolVersion = buffer.readByte(); // Extended dimension support during login if (serverProtocolVersion > 1) { overrideDimension = buffer.readInt(); FMLLog.fine("Server FML protocol version %d, 4 byte dimension received %d", serverProtocolVersion, overrideDimension); } else { FMLLog.info("Server FML protocol version %d, no additional data received", serverProtocolVersion); } } public byte protocolVersion() { return serverProtocolVersion; } public int overrideDim() { return overrideDimension; } } public static class ClientHello extends FMLHandshakeMessage { private byte serverProtocolVersion; @Override public void toBytes(ByteBuf buffer) { buffer.writeByte(NetworkRegistry.FML_PROTOCOL); } @Override public void fromBytes(ByteBuf buffer) { serverProtocolVersion = buffer.readByte(); } public byte protocolVersion() { return serverProtocolVersion; } } public static class ModList extends FMLHandshakeMessage { public ModList() { } public ModList(List<ModContainer> modList) { for (ModContainer mod : modList) { modTags.put(mod.getModId(), mod.getVersion()); } } private Map<String,String> modTags = Maps.newHashMap(); @Override public void toBytes(ByteBuf buffer) { super.toBytes(buffer); ByteBufUtils.writeVarInt(buffer, modTags.size(), 2); for (Map.Entry<String,String> modTag: modTags.entrySet()) { ByteBufUtils.writeUTF8String(buffer, modTag.getKey()); ByteBufUtils.writeUTF8String(buffer, modTag.getValue()); } } @Override public void fromBytes(ByteBuf buffer) { super.fromBytes(buffer); int modCount = ByteBufUtils.readVarInt(buffer, 2); for (int i = 0; i < modCount; i++) { modTags.put(ByteBufUtils.readUTF8String(buffer), ByteBufUtils.readUTF8String(buffer)); } } public String modListAsString() { return Joiner.on(',').withKeyValueSeparator("@").join(modTags); } public int modListSize() { return modTags.size(); } public Map<String, String> modList() { return modTags; } @Override public String toString(Class<? extends Enum<?>> side) { return super.toString(side)+":"+modTags.size()+" mods"; } } public static class RegistryData extends FMLHandshakeMessage { public RegistryData() { } public RegistryData(boolean hasMore, String name, GameData.GameDataSnapshot.Entry entry) { this.hasMore = hasMore; this.name = name; this.ids = entry.ids; this.substitutions = entry.substitutions; } private boolean hasMore; private String name; private Map<String,Integer> ids; private Set<String> substitutions; @Override public void fromBytes(ByteBuf buffer) { this.hasMore = buffer.readBoolean(); this.name = ByteBufUtils.readUTF8String(buffer); int length = ByteBufUtils.readVarInt(buffer, 3); ids = Maps.newHashMap(); for (int i = 0; i < length; i++) { ids.put(ByteBufUtils.readUTF8String(buffer), ByteBufUtils.readVarInt(buffer, 3)); } length = ByteBufUtils.readVarInt(buffer, 3); substitutions = Sets.newHashSet(); for (int i = 0; i < length; i++) { substitutions.add(ByteBufUtils.readUTF8String(buffer)); } //if (!buffer.isReadable()) return; // In case we expand } @Override public void toBytes(ByteBuf buffer) { buffer.writeBoolean(this.hasMore); ByteBufUtils.writeUTF8String(buffer, this.name); ByteBufUtils.writeVarInt(buffer, ids.size(), 3); for (Entry<String, Integer> entry: ids.entrySet()) { ByteBufUtils.writeUTF8String(buffer, entry.getKey()); ByteBufUtils.writeVarInt(buffer, entry.getValue(), 3); } ByteBufUtils.writeVarInt(buffer, substitutions.size(), 3); for (String entry: substitutions) { ByteBufUtils.writeUTF8String(buffer, entry); } } public Map<String,Integer> getIdMap() { return ids; } public Set<String> getSubstitutions() { return substitutions; } public String getName() { return this.name; } public boolean hasMore() { return this.hasMore; } @Override public String toString(Class<? extends Enum<?>> side) { return super.toString(side) + ":"+ids.size()+" mappings"; } } public static class HandshakeAck extends FMLHandshakeMessage { int phase; public HandshakeAck() {} HandshakeAck(int phase) { this.phase = phase; } @Override public void fromBytes(ByteBuf buffer) { phase = buffer.readByte(); } @Override public void toBytes(ByteBuf buffer) { buffer.writeByte(phase); } @Override public String toString(Class<? extends Enum<?>> side) { return super.toString(side) + ":{"+phase+"}"; } } public static class HandshakeReset extends FMLHandshakeMessage { public HandshakeReset() {} } public void fromBytes(ByteBuf buffer) { } public void toBytes(ByteBuf buffer) { } public String toString(Class<? extends Enum<?>> side) { return getClass().getName().substring(getClass().getName().lastIndexOf('$')); } }