package cpw.mods.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 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; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.network.internal.FMLProxyPacket; import cpw.mods.fml.common.registry.GameData; public abstract class FMLHandshakeMessage { public static FMLProxyPacket makeCustomChannelRegistration(Set<String> channels) { String salutation = Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML|HS","FML"),channels)); FMLProxyPacket proxy = new FMLProxyPacket(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 ModIdData extends FMLHandshakeMessage { public ModIdData() { } public ModIdData(GameData.GameDataSnapshot snapshot) { this.modIds = snapshot.idMap; this.blockSubstitutions = snapshot.blockSubstitutions; this.itemSubstitutions = snapshot.itemSubstitutions; } private Map<String,Integer> modIds; private Set<String> blockSubstitutions; private Set<String> itemSubstitutions; @Override public void fromBytes(ByteBuf buffer) { int length = ByteBufUtils.readVarInt(buffer, 3); modIds = Maps.newHashMap(); blockSubstitutions = Sets.newHashSet(); itemSubstitutions = Sets.newHashSet(); for (int i = 0; i < length; i++) { modIds.put(ByteBufUtils.readUTF8String(buffer),ByteBufUtils.readVarInt(buffer, 3)); } // we don't have any more data to read if (!buffer.isReadable()) { return; } length = ByteBufUtils.readVarInt(buffer, 3); for (int i = 0; i < length; i++) { blockSubstitutions.add(ByteBufUtils.readUTF8String(buffer)); } length = ByteBufUtils.readVarInt(buffer, 3); for (int i = 0; i < length; i++) { itemSubstitutions.add(ByteBufUtils.readUTF8String(buffer)); } } @Override public void toBytes(ByteBuf buffer) { ByteBufUtils.writeVarInt(buffer, modIds.size(), 3); for (Entry<String, Integer> entry: modIds.entrySet()) { ByteBufUtils.writeUTF8String(buffer, entry.getKey()); ByteBufUtils.writeVarInt(buffer, entry.getValue(), 3); } ByteBufUtils.writeVarInt(buffer, blockSubstitutions.size(), 3); for (String entry: blockSubstitutions) { ByteBufUtils.writeUTF8String(buffer, entry); } ByteBufUtils.writeVarInt(buffer, blockSubstitutions.size(), 3); for (String entry: itemSubstitutions) { ByteBufUtils.writeUTF8String(buffer, entry); } } public Map<String,Integer> dataList() { return modIds; } public Set<String> blockSubstitutions() { return blockSubstitutions; } public Set<String> itemSubstitutions() { return itemSubstitutions; } @Override public String toString(Class<? extends Enum<?>> side) { return super.toString(side) + ":"+modIds.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('$')); } }