001package cpw.mods.fml.common.network; 002 003import java.lang.reflect.Method; 004import java.util.Set; 005import java.util.logging.Level; 006 007import net.minecraft.item.Item; 008 009import com.google.common.base.Strings; 010 011import cpw.mods.fml.common.FMLCommonHandler; 012import cpw.mods.fml.common.FMLLog; 013import cpw.mods.fml.common.ModContainer; 014import cpw.mods.fml.common.discovery.ASMDataTable; 015import cpw.mods.fml.common.discovery.ASMDataTable.ASMData; 016import cpw.mods.fml.common.versioning.DefaultArtifactVersion; 017import cpw.mods.fml.common.versioning.InvalidVersionSpecificationException; 018import cpw.mods.fml.common.versioning.VersionRange; 019import cpw.mods.fml.relauncher.Side; 020 021public 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 the default {@link NetworkMod#connectionHandler()} annotation value 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 the default {@link NetworkMod#packetHandler()} annotation value 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 /** 232 * @return the default {@link NetworkMod#tinyPacketHandler()} annotation value 233 */ 234 private Object getTinyPacketHandlerDefaultValue() 235 { 236 try { 237 if (tinyPacketHandlerDefaultValue == null) 238 { 239 tinyPacketHandlerDefaultValue = NetworkMod.class.getMethod("tinyPacketHandler").getDefaultValue(); 240 } 241 return tinyPacketHandlerDefaultValue; 242 } 243 catch (NoSuchMethodException e) 244 { 245 throw new RuntimeException("Derp?", e); 246 } 247 } 248 /** 249 * @return the {@link NetworkMod#clientPacketHandlerSpec()} default annotation value 250 */ 251 private Object getClientHandlerSpecDefaultValue() 252 { 253 try { 254 if (clientHandlerDefaultValue == null) 255 { 256 clientHandlerDefaultValue = NetworkMod.class.getMethod("clientPacketHandlerSpec").getDefaultValue(); 257 } 258 return clientHandlerDefaultValue; 259 } 260 catch (NoSuchMethodException e) 261 { 262 throw new RuntimeException("Derp?", e); 263 } 264 } 265 /** 266 * @return the default {@link NetworkMod#serverPacketHandlerSpec()} annotation value 267 */ 268 private Object getServerHandlerSpecDefaultValue() 269 { 270 try { 271 if (serverHandlerDefaultValue == null) 272 { 273 serverHandlerDefaultValue = NetworkMod.class.getMethod("serverPacketHandlerSpec").getDefaultValue(); 274 } 275 return serverHandlerDefaultValue; 276 } 277 catch (NoSuchMethodException e) 278 { 279 throw new RuntimeException("Derp?", e); 280 } 281 } 282 public boolean requiresClientSide() 283 { 284 return mod.clientSideRequired(); 285 } 286 287 public boolean requiresServerSide() 288 { 289 return mod.serverSideRequired(); 290 } 291 292 public boolean acceptVersion(String version) 293 { 294 if (checkHandler != null) 295 { 296 try 297 { 298 return (Boolean)checkHandler.invoke(container.getMod(), version); 299 } 300 catch (Exception e) 301 { 302 FMLLog.log(Level.WARNING, e, "There was a problem invoking the checkhandler method %s for network mod id %s", checkHandler.getName(), container.getModId()); 303 return false; 304 } 305 } 306 307 if (acceptableRange!=null) 308 { 309 return acceptableRange.containsVersion(new DefaultArtifactVersion(version)); 310 } 311 312 return container.getVersion().equals(version); 313 } 314 315 public int getLocalId() 316 { 317 return localId; 318 } 319 320 public int getNetworkId() 321 { 322 return networkId; 323 } 324 325 public ModContainer getContainer() 326 { 327 return container; 328 } 329 330 public NetworkMod getMod() 331 { 332 return mod; 333 } 334 335 public boolean isNetworkMod() 336 { 337 return mod != null; 338 } 339 340 public void setNetworkId(int value) 341 { 342 this.networkId = value; 343 } 344 345 public boolean hasTinyPacketHandler() 346 { 347 return tinyPacketHandler != null; 348 } 349 public ITinyPacketHandler getTinyPacketHandler() 350 { 351 return tinyPacketHandler; 352 } 353}