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