001 package cpw.mods.fml.common.network;
002
003 import java.util.Arrays;
004 import java.util.List;
005 import java.util.Map;
006 import java.util.Set;
007 import java.util.logging.Level;
008
009 import net.minecraft.entity.player.EntityPlayer;
010 import net.minecraft.entity.player.EntityPlayerMP;
011 import net.minecraft.inventory.Container;
012 import net.minecraft.network.*;
013 import net.minecraft.network.packet.*;
014 import net.minecraft.server.MinecraftServer;
015 import net.minecraft.world.World;
016
017 import com.google.common.base.Charsets;
018 import com.google.common.base.Joiner;
019 import com.google.common.base.Splitter;
020 import com.google.common.base.Strings;
021 import com.google.common.collect.ArrayListMultimap;
022 import com.google.common.collect.Iterables;
023 import com.google.common.collect.Lists;
024 import com.google.common.collect.Maps;
025 import com.google.common.collect.Multimap;
026 import com.google.common.collect.Sets;
027
028 import cpw.mods.fml.common.FMLCommonHandler;
029 import cpw.mods.fml.common.FMLLog;
030 import cpw.mods.fml.common.Loader;
031 import cpw.mods.fml.common.ModContainer;
032 import cpw.mods.fml.common.network.FMLPacket.Type;
033 import cpw.mods.fml.relauncher.Side;
034
035 /**
036 * @author cpw
037 *
038 */
039 public class NetworkRegistry
040 {
041
042 private static final NetworkRegistry INSTANCE = new NetworkRegistry();
043 /**
044 * A map of active channels per player
045 */
046 private Multimap<Player, String> activeChannels = ArrayListMultimap.create();
047 /**
048 * A map of the packet handlers for packets
049 */
050 private Multimap<String, IPacketHandler> universalPacketHandlers = ArrayListMultimap.create();
051 private Multimap<String, IPacketHandler> clientPacketHandlers = ArrayListMultimap.create();
052 private Multimap<String, IPacketHandler> serverPacketHandlers = ArrayListMultimap.create();
053 /**
054 * A linked set of registered connection handlers
055 */
056 private Set<IConnectionHandler> connectionHandlers = Sets.newLinkedHashSet();
057 private Map<ModContainer, IGuiHandler> serverGuiHandlers = Maps.newHashMap();
058 private Map<ModContainer, IGuiHandler> clientGuiHandlers = Maps.newHashMap();
059 private List<IChatListener> chatListeners = Lists.newArrayList();
060
061 public static NetworkRegistry instance()
062 {
063 return INSTANCE;
064 }
065 /**
066 * Get the packet 250 channel registration string
067 * @return
068 */
069 byte[] getPacketRegistry(Side side)
070 {
071 return Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML"),universalPacketHandlers.keySet(), side.isClient() ? clientPacketHandlers.keySet() : serverPacketHandlers.keySet())).getBytes(Charsets.UTF_8);
072 }
073 /**
074 * Is the specified channel active for the player?
075 * @param channel
076 * @param player
077 */
078 public boolean isChannelActive(String channel, Player player)
079 {
080 return activeChannels.containsEntry(player,channel);
081 }
082 /**
083 * register a channel to a mod
084 * @param container
085 * @param channelName
086 */
087 public void registerChannel(IPacketHandler handler, String channelName)
088 {
089 if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
090 {
091 FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
092 throw new RuntimeException("Channel name is invalid");
093
094 }
095 universalPacketHandlers.put(channelName, handler);
096 }
097
098 public void registerChannel(IPacketHandler handler, String channelName, Side side)
099 {
100 if (side == null)
101 {
102 registerChannel(handler, channelName);
103 return;
104 }
105 if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
106 {
107 FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
108 throw new RuntimeException("Channel name is invalid");
109
110 }
111 if (side.isClient())
112 {
113 clientPacketHandlers.put(channelName, handler);
114 }
115 else
116 {
117 serverPacketHandlers.put(channelName, handler);
118 }
119 }
120 /**
121 * Activate the channel for the player
122 * @param player
123 */
124 void activateChannel(Player player, String channel)
125 {
126 activeChannels.put(player, channel);
127 }
128 /**
129 * Deactivate the channel for the player
130 * @param player
131 * @param channel
132 */
133 void deactivateChannel(Player player, String channel)
134 {
135 activeChannels.remove(player, channel);
136 }
137 /**
138 * Register a connection handler
139 *
140 * @param handler
141 */
142 public void registerConnectionHandler(IConnectionHandler handler)
143 {
144 connectionHandlers.add(handler);
145 }
146
147 /**
148 * Register a chat listener
149 * @param listener
150 */
151 public void registerChatListener(IChatListener listener)
152 {
153 chatListeners.add(listener);
154 }
155
156 void playerLoggedIn(EntityPlayerMP player, NetServerHandler netHandler, INetworkManager manager)
157 {
158 generateChannelRegistration(player, netHandler, manager);
159 for (IConnectionHandler handler : connectionHandlers)
160 {
161 handler.playerLoggedIn((Player)player, netHandler, manager);
162 }
163 }
164
165 String connectionReceived(NetLoginHandler netHandler, INetworkManager manager)
166 {
167 for (IConnectionHandler handler : connectionHandlers)
168 {
169 String kick = handler.connectionReceived(netHandler, manager);
170 if (!Strings.isNullOrEmpty(kick))
171 {
172 return kick;
173 }
174 }
175 return null;
176 }
177
178 void connectionOpened(NetHandler netClientHandler, String server, int port, INetworkManager networkManager)
179 {
180 for (IConnectionHandler handler : connectionHandlers)
181 {
182 handler.connectionOpened(netClientHandler, server, port, networkManager);
183 }
184 }
185
186 void connectionOpened(NetHandler netClientHandler, MinecraftServer server, INetworkManager networkManager)
187 {
188 for (IConnectionHandler handler : connectionHandlers)
189 {
190 handler.connectionOpened(netClientHandler, server, networkManager);
191 }
192 }
193
194 void clientLoggedIn(NetHandler clientHandler, INetworkManager manager, Packet1Login login)
195 {
196 generateChannelRegistration(clientHandler.getPlayer(), clientHandler, manager);
197 for (IConnectionHandler handler : connectionHandlers)
198 {
199 handler.clientLoggedIn(clientHandler, manager, login);
200 }
201 }
202
203 void connectionClosed(INetworkManager manager, EntityPlayer player)
204 {
205 for (IConnectionHandler handler : connectionHandlers)
206 {
207 handler.connectionClosed(manager);
208 }
209 activeChannels.removeAll(player);
210 }
211
212 void generateChannelRegistration(EntityPlayer player, NetHandler netHandler, INetworkManager manager)
213 {
214 Packet250CustomPayload pkt = new Packet250CustomPayload();
215 pkt.channel = "REGISTER";
216 pkt.data = getPacketRegistry(player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT);
217 pkt.length = pkt.data.length;
218 manager.addToSendQueue(pkt);
219 }
220
221 void handleCustomPacket(Packet250CustomPayload packet, INetworkManager network, NetHandler handler)
222 {
223 if ("REGISTER".equals(packet.channel))
224 {
225 handleRegistrationPacket(packet, (Player)handler.getPlayer());
226 }
227 else if ("UNREGISTER".equals(packet.channel))
228 {
229 handleUnregistrationPacket(packet, (Player)handler.getPlayer());
230 }
231 else
232 {
233 handlePacket(packet, network, (Player)handler.getPlayer());
234 }
235 }
236
237
238 private void handlePacket(Packet250CustomPayload packet, INetworkManager network, Player player)
239 {
240 String channel = packet.channel;
241 for (IPacketHandler handler : Iterables.concat(universalPacketHandlers.get(channel), player instanceof EntityPlayerMP ? serverPacketHandlers.get(channel) : clientPacketHandlers.get(channel)))
242 {
243 handler.onPacketData(network, packet, player);
244 }
245 }
246
247 private void handleRegistrationPacket(Packet250CustomPayload packet, Player player)
248 {
249 List<String> channels = extractChannelList(packet);
250 for (String channel : channels)
251 {
252 activateChannel(player, channel);
253 }
254 }
255 private void handleUnregistrationPacket(Packet250CustomPayload packet, Player player)
256 {
257 List<String> channels = extractChannelList(packet);
258 for (String channel : channels)
259 {
260 deactivateChannel(player, channel);
261 }
262 }
263
264 private List<String> extractChannelList(Packet250CustomPayload packet)
265 {
266 String request = new String(packet.data, Charsets.UTF_8);
267 List<String> channels = Lists.newArrayList(Splitter.on('\0').split(request));
268 return channels;
269 }
270
271 public void registerGuiHandler(Object mod, IGuiHandler handler)
272 {
273 ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
274 if (mc == null)
275 {
276 mc = Loader.instance().activeModContainer();
277 FMLLog.log(Level.WARNING, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
278 }
279 NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
280 if (nmh == null)
281 {
282 FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
283 }
284 else
285 {
286 serverGuiHandlers.put(mc, handler);
287 }
288 clientGuiHandlers.put(mc, handler);
289 }
290 void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
291 {
292 IGuiHandler handler = serverGuiHandlers.get(mc);
293 NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
294 if (handler != null && nmh != null)
295 {
296 Container container = (Container)handler.getServerGuiElement(modGuiId, player, world, x, y, z);
297 if (container != null)
298 {
299 player.incrementWindowID();
300 player.closeInventory();
301 int windowId = player.currentWindowId;
302 Packet250CustomPayload pkt = new Packet250CustomPayload();
303 pkt.channel = "FML";
304 pkt.data = FMLPacket.makePacket(Type.GUIOPEN, windowId, nmh.getNetworkId(), modGuiId, x, y, z);
305 pkt.length = pkt.data.length;
306 player.playerNetServerHandler.sendPacketToPlayer(pkt);
307 player.openContainer = container;
308 player.openContainer.windowId = windowId;
309 player.openContainer.addCraftingToCrafters(player);
310 }
311 }
312 }
313 void openLocalGui(ModContainer mc, EntityPlayer player, int modGuiId, World world, int x, int y, int z)
314 {
315 IGuiHandler handler = clientGuiHandlers.get(mc);
316 FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
317 }
318 public Packet3Chat handleChat(NetHandler handler, Packet3Chat chat)
319 {
320 Side s = Side.CLIENT;
321 if (handler instanceof NetServerHandler)
322 {
323 s = Side.SERVER;
324 }
325 for (IChatListener listener : chatListeners)
326 {
327 chat = s.isClient() ? listener.clientChat(handler, chat) : listener.serverChat(handler, chat);
328 }
329
330 return chat;
331 }
332 public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
333 {
334 NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler((int)mapData.itemID);
335 if (nmh == null)
336 {
337 FMLLog.info("Received a tiny packet for network id %d that is not recognised here", mapData.itemID);
338 return;
339 }
340 if (nmh.hasTinyPacketHandler())
341 {
342 nmh.getTinyPacketHandler().handle(handler, mapData);
343 }
344 else
345 {
346 FMLLog.info("Received a tiny packet for a network mod that does not accept tiny packets %s", nmh.getContainer().getModId());
347 }
348 }
349 }