001 package cpw.mods.fml.common.network;
002
003 import java.lang.reflect.Method;
004 import java.util.Set;
005 import java.util.logging.Level;
006
007 import net.minecraft.item.Item;
008
009 import com.google.common.base.Strings;
010
011 import cpw.mods.fml.common.FMLCommonHandler;
012 import cpw.mods.fml.common.FMLLog;
013 import cpw.mods.fml.common.ModContainer;
014 import cpw.mods.fml.common.discovery.ASMDataTable;
015 import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;
016 import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
017 import cpw.mods.fml.common.versioning.InvalidVersionSpecificationException;
018 import cpw.mods.fml.common.versioning.VersionRange;
019 import cpw.mods.fml.relauncher.Side;
020
021 public class NetworkModHandler
022 {
023 private static Object connectionHandlerDefaultValue;
024 private static Object packetHandlerDefaultValue;
025 private static Object clientHandlerDefaultValue;
026 private static Object serverHandlerDefaultValue;
027 private static Object tinyPacketHandlerDefaultValue;
028
029 private static int assignedIds = 1;
030
031 private int localId;
032 private int networkId;
033
034 private ModContainer container;
035 private NetworkMod mod;
036 private Method checkHandler;
037
038 private VersionRange acceptableRange;
039 private ITinyPacketHandler tinyPacketHandler;
040
041 public NetworkModHandler(ModContainer container, NetworkMod modAnnotation)
042 {
043 this.container = container;
044 this.mod = modAnnotation;
045 this.localId = assignedIds++;
046 this.networkId = this.localId;
047 // Skip over the map object because it has special network id meaning
048 if (Item.map.itemID == assignedIds)
049 {
050 assignedIds++;
051 }
052 }
053 public NetworkModHandler(ModContainer container, Class<?> networkModClass, ASMDataTable table)
054 {
055 this(container, networkModClass.getAnnotation(NetworkMod.class));
056 if (this.mod == null)
057 {
058 return;
059 }
060
061 Set<ASMData> versionCheckHandlers = table.getAnnotationsFor(container).get(NetworkMod.VersionCheckHandler.class.getName());
062 String versionCheckHandlerMethod = null;
063 for (ASMData vch : versionCheckHandlers)
064 {
065 if (vch.getClassName().equals(networkModClass.getName()))
066 {
067 versionCheckHandlerMethod = vch.getObjectName();
068 break;
069 }
070 }
071 if (versionCheckHandlerMethod != null)
072 {
073 try
074 {
075 Method checkHandlerMethod = networkModClass.getDeclaredMethod(versionCheckHandlerMethod, String.class);
076 if (checkHandlerMethod.isAnnotationPresent(NetworkMod.VersionCheckHandler.class))
077 {
078 this.checkHandler = checkHandlerMethod;
079 }
080 }
081 catch (Exception e)
082 {
083 FMLLog.log(Level.WARNING, e, "The declared version check handler method %s on network mod id %s is not accessible", versionCheckHandlerMethod, container.getModId());
084 }
085 }
086
087 if (this.checkHandler == null)
088 {
089 String versionBounds = mod.versionBounds();
090 if (!Strings.isNullOrEmpty(versionBounds))
091 {
092 try
093 {
094 this.acceptableRange = VersionRange.createFromVersionSpec(versionBounds);
095 }
096 catch (InvalidVersionSpecificationException e)
097 {
098 FMLLog.log(Level.WARNING, e, "Invalid bounded range %s specified for network mod id %s", versionBounds, container.getModId());
099 }
100 }
101 }
102
103 FMLLog.finest("Testing mod %s to verify it accepts its own version in a remote connection", container.getModId());
104 boolean acceptsSelf = acceptVersion(container.getVersion());
105 if (!acceptsSelf)
106 {
107 FMLLog.severe("The mod %s appears to reject its own version number (%s) in its version handling. This is likely a severe bug in the mod!", container.getModId(), container.getVersion());
108 }
109 else
110 {
111 FMLLog.finest("The mod %s accepts its own version (%s)", container.getModId(), container.getVersion());
112 }
113
114 tryCreatingPacketHandler(container, mod.packetHandler(), mod.channels(), null);
115 if (FMLCommonHandler.instance().getSide().isClient())
116 {
117 if (mod.clientPacketHandlerSpec() != getClientHandlerSpecDefaultValue())
118 {
119 tryCreatingPacketHandler(container, mod.clientPacketHandlerSpec().packetHandler(), mod.clientPacketHandlerSpec().channels(), Side.CLIENT);
120 }
121 }
122 if (mod.serverPacketHandlerSpec() != getServerHandlerSpecDefaultValue())
123 {
124 tryCreatingPacketHandler(container, mod.serverPacketHandlerSpec().packetHandler(), mod.serverPacketHandlerSpec().channels(), Side.SERVER);
125 }
126
127 if (mod.connectionHandler() != getConnectionHandlerDefaultValue())
128 {
129 IConnectionHandler instance;
130 try
131 {
132 instance = mod.connectionHandler().newInstance();
133 }
134 catch (Exception e)
135 {
136 FMLLog.log(Level.SEVERE, e, "Unable to create connection handler instance %s", mod.connectionHandler().getName());
137 throw new FMLNetworkException(e);
138 }
139
140 NetworkRegistry.instance().registerConnectionHandler(instance);
141 }
142
143 if (mod.tinyPacketHandler()!=getTinyPacketHandlerDefaultValue())
144 {
145 try
146 {
147 tinyPacketHandler = mod.tinyPacketHandler().newInstance();
148 }
149 catch (Exception e)
150 {
151 FMLLog.log(Level.SEVERE, e, "Unable to create tiny packet handler instance %s", mod.tinyPacketHandler().getName());
152 throw new FMLNetworkException(e);
153 }
154 }
155 }
156 /**
157 * @param container
158 */
159 private void tryCreatingPacketHandler(ModContainer container, Class<? extends IPacketHandler> clazz, String[] channels, Side side)
160 {
161 if (side!=null && side.isClient() && ! FMLCommonHandler.instance().getSide().isClient())
162 {
163 return;
164 }
165 if (clazz!=getPacketHandlerDefaultValue())
166 {
167 if (channels.length==0)
168 {
169 FMLLog.log(Level.WARNING, "The mod id %s attempted to register a packet handler without specifying channels for it", container.getModId());
170 }
171 else
172 {
173 IPacketHandler instance;
174 try
175 {
176 instance = clazz.newInstance();
177 }
178 catch (Exception e)
179 {
180 FMLLog.log(Level.SEVERE, e, "Unable to create a packet handler instance %s for mod %s", clazz.getName(), container.getModId());
181 throw new FMLNetworkException(e);
182 }
183
184 for (String channel : channels)
185 {
186 NetworkRegistry.instance().registerChannel(instance, channel, side);
187 }
188 }
189 }
190 else if (channels.length > 0)
191 {
192 FMLLog.warning("The mod id %s attempted to register channels without specifying a packet handler", container.getModId());
193 }
194 }
195 /**
196 * @return
197 */
198 private Object getConnectionHandlerDefaultValue()
199 {
200 try {
201 if (connectionHandlerDefaultValue == null)
202 {
203 connectionHandlerDefaultValue = NetworkMod.class.getMethod("connectionHandler").getDefaultValue();
204 }
205 return connectionHandlerDefaultValue;
206 }
207 catch (NoSuchMethodException e)
208 {
209 throw new RuntimeException("Derp?", e);
210 }
211 }
212
213 /**
214 * @return
215 */
216 private Object getPacketHandlerDefaultValue()
217 {
218 try {
219 if (packetHandlerDefaultValue == null)
220 {
221 packetHandlerDefaultValue = NetworkMod.class.getMethod("packetHandler").getDefaultValue();
222 }
223 return packetHandlerDefaultValue;
224 }
225 catch (NoSuchMethodException e)
226 {
227 throw new RuntimeException("Derp?", e);
228 }
229 }
230
231 private Object getTinyPacketHandlerDefaultValue()
232 {
233 try {
234 if (tinyPacketHandlerDefaultValue == null)
235 {
236 tinyPacketHandlerDefaultValue = NetworkMod.class.getMethod("tinyPacketHandler").getDefaultValue();
237 }
238 return tinyPacketHandlerDefaultValue;
239 }
240 catch (NoSuchMethodException e)
241 {
242 throw new RuntimeException("Derp?", e);
243 }
244 }
245 /**
246 * @return
247 */
248 private Object getClientHandlerSpecDefaultValue()
249 {
250 try {
251 if (clientHandlerDefaultValue == null)
252 {
253 clientHandlerDefaultValue = NetworkMod.class.getMethod("clientPacketHandlerSpec").getDefaultValue();
254 }
255 return clientHandlerDefaultValue;
256 }
257 catch (NoSuchMethodException e)
258 {
259 throw new RuntimeException("Derp?", e);
260 }
261 }
262 /**
263 * @return
264 */
265 private Object getServerHandlerSpecDefaultValue()
266 {
267 try {
268 if (serverHandlerDefaultValue == null)
269 {
270 serverHandlerDefaultValue = NetworkMod.class.getMethod("serverPacketHandlerSpec").getDefaultValue();
271 }
272 return serverHandlerDefaultValue;
273 }
274 catch (NoSuchMethodException e)
275 {
276 throw new RuntimeException("Derp?", e);
277 }
278 }
279 public boolean requiresClientSide()
280 {
281 return mod.clientSideRequired();
282 }
283
284 public boolean requiresServerSide()
285 {
286 return mod.serverSideRequired();
287 }
288
289 public boolean acceptVersion(String version)
290 {
291 if (checkHandler != null)
292 {
293 try
294 {
295 return (Boolean)checkHandler.invoke(container.getMod(), version);
296 }
297 catch (Exception e)
298 {
299 FMLLog.log(Level.WARNING, e, "There was a problem invoking the checkhandler method %s for network mod id %s", checkHandler.getName(), container.getModId());
300 return false;
301 }
302 }
303
304 if (acceptableRange!=null)
305 {
306 return acceptableRange.containsVersion(new DefaultArtifactVersion(version));
307 }
308
309 return container.getVersion().equals(version);
310 }
311
312 public int getLocalId()
313 {
314 return localId;
315 }
316
317 public int getNetworkId()
318 {
319 return networkId;
320 }
321
322 public ModContainer getContainer()
323 {
324 return container;
325 }
326
327 public NetworkMod getMod()
328 {
329 return mod;
330 }
331
332 public boolean isNetworkMod()
333 {
334 return mod != null;
335 }
336
337 public void setNetworkId(int value)
338 {
339 this.networkId = value;
340 }
341
342 public boolean hasTinyPacketHandler()
343 {
344 return tinyPacketHandler != null;
345 }
346 public ITinyPacketHandler getTinyPacketHandler()
347 {
348 return tinyPacketHandler;
349 }
350 }