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