001 package net.minecraftforge.client; 002 003 import java.io.File; 004 import java.io.IOException; 005 import java.lang.reflect.Field; 006 import java.util.logging.Level; 007 008 import cpw.mods.fml.client.FMLClientHandler; 009 import cpw.mods.fml.common.FMLLog; 010 011 import paulscode.sound.SoundSystemConfig; 012 import paulscode.sound.codecs.CodecIBXM; 013 014 import net.minecraft.client.Minecraft; 015 import net.minecraft.src.*; 016 017 public class ModCompatibilityClient 018 { 019 /** 020 * Trys to get the class for the specified name, will also try the 021 * net.minecraft.src package in case we are in MCP 022 * Returns null if not found. 023 * 024 * @param name The class name 025 * @return The Class, or null if not found 026 */ 027 private static Class getClass(String name) 028 { 029 try 030 { 031 return Class.forName(name); 032 } 033 catch (Exception e) 034 { 035 try 036 { 037 return Class.forName("net.minecraft.src." + name); 038 } 039 catch (Exception e2) 040 { 041 return null; 042 } 043 } 044 } 045 046 /************************************************************************************************ 047 * Risugami's AudioMod Compatibility 048 * http://www.minecraftforum.net/topic/75440- 049 * 050 * AudioMod adds a few extra codecs, loads audio from /resources/mods/*, 051 * introduces the concept of 'cave' sounds, which are determined by if 052 * the player is underneath a solid block. 053 * 054 * It also lowers the interval between background music songs to 6000 055 */ 056 public static SoundPool audioModSoundPoolCave; 057 058 /** 059 * Populates the sound pools with with sounds from the /resources/mods folder 060 * And sets the interval between background music to 6000 061 * 062 * @param mngr The SoundManager instance 063 */ 064 public static void audioModLoad(SoundManager mngr) 065 { 066 audioModSoundPoolCave = new SoundPool(); 067 audioModLoadModAudio("resources/mod/sound", mngr.soundPoolSounds); 068 audioModLoadModAudio("resources/mod/streaming", mngr.soundPoolStreaming); 069 audioModLoadModAudio("resources/mod/music", mngr.soundPoolMusic); 070 audioModLoadModAudio("resources/mod/cavemusic", audioModSoundPoolCave); 071 072 if (mngr.MUSIC_INTERVAL == 12000) 073 { 074 mngr.MUSIC_INTERVAL = 6000; 075 } 076 } 077 078 /** 079 * Walks the given path in the Minecraft app directory and adds audio to the SoundPool 080 * @param path The path to walk 081 * @param pool The pool to add sound to 082 */ 083 private static void audioModLoadModAudio(String path, SoundPool pool) 084 { 085 File folder = new File(Minecraft.getMinecraftDir(), path); 086 087 try 088 { 089 audioModWalkFolder(folder, folder, pool); 090 } 091 catch (IOException ex) 092 { 093 FMLLog.log(Level.FINE, ex, "Loading Mod audio failed for folder: %s", path); 094 ex.printStackTrace(); 095 } 096 } 097 098 /** 099 * Walks the folder path recursively and calls pool.addSound on any file it finds. 100 * 101 * @param base The base path for the folder, determines the name when calling addSound 102 * @param folder The current folder 103 * @param pool The SoundPool to add the sound to 104 * @throws IOException 105 */ 106 private static void audioModWalkFolder(File base, File folder, SoundPool pool) throws IOException 107 { 108 if (folder.exists() || folder.mkdirs()) 109 { 110 for (File file : folder.listFiles()) 111 { 112 if (!file.getName().startsWith(".")) 113 { 114 if (file.isDirectory()) 115 { 116 audioModWalkFolder(base, file, pool); 117 } 118 else if (file.isFile()) 119 { 120 String subpath = file.getPath().substring(base.getPath().length() + 1).replace('\\', '/'); 121 pool.addSound(subpath, file); 122 } 123 } 124 } 125 } 126 } 127 128 /** 129 * Adds the IBXM codec and associates it with .xm, .s3m, and .mod 130 */ 131 public static void audioModAddCodecs() 132 { 133 SoundSystemConfig.setCodec("xm", CodecIBXM.class); 134 SoundSystemConfig.setCodec("s3m", CodecIBXM.class); 135 SoundSystemConfig.setCodec("mod", CodecIBXM.class); 136 } 137 138 /** 139 * If the current player is underground, it picks a random song from the cave sound pool, 140 * if they are not it returns the passed in entry. 141 * 142 * @param soundManager The SoundManager instance 143 * @param current The currently selected entry 144 * @return A soundPool entry to be played as the background music 145 */ 146 public static SoundPoolEntry audioModPickBackgroundMusic(SoundManager soundManager, SoundPoolEntry current) 147 { 148 Minecraft mc = FMLClientHandler.instance().getClient(); 149 if (mc != null && mc.theWorld != null && audioModSoundPoolCave != null) 150 { 151 Entity ent = mc.renderViewEntity; 152 int x = MathHelper.truncateDoubleToInt(ent.posX); 153 int y = MathHelper.truncateDoubleToInt(ent.posY); 154 int z = MathHelper.truncateDoubleToInt(ent.posZ); 155 return (mc.theWorld.canBlockSeeTheSky(x, y, z) ? current : audioModSoundPoolCave.getRandomSound()); 156 } 157 return current; 158 } 159 160 /*********************************************************************************************************** 161 * SDK's ModLoaderMP 162 * http://www.minecraftforum.net/topic/86765- 163 * 164 * ModLoaderMP was supposed to be a reliable server side version of ModLoader, however it has 165 * gotten the reputation of being really slow to update. Never having bugfixes, breaking compatibility 166 * with the client side ModLoader. 167 * 168 * So we have replaced it with our own system called FML (Forge ModLoader) 169 * it is a stand alone mod, that Forge relies on, and that is open source/community driven. 170 * https://github.com/cpw/FML 171 * 172 * However, for compatibilities sake, we provide the ModLoaderMP's hooks so that the end user 173 * does not need to make a choice between the two on the client side. 174 **/ 175 private static int isMLMPInstalled = -1; 176 177 /** 178 * Determine if ModLoaderMP is installed by checking for the existence of the BaseModMp class. 179 * @return True if BaseModMp was installed (indicating the existance of MLMP) 180 */ 181 public static boolean isMLMPInstalled() 182 { 183 if (isMLMPInstalled == -1) 184 { 185 isMLMPInstalled = (getClass("ModLoaderMp") != null ? 1 : 0); 186 } 187 return isMLMPInstalled == 1; 188 } 189 190 /** 191 * Attempts to spawn a vehicle using ModLoaderMP's vehicle spawn registry, if MLMP is not installed 192 * it returns the passed in currentEntity 193 * 194 * @param type The Type ID of the vehicle 195 * @param world The current world 196 * @param x The spawn X position 197 * @param y The spawn Y position 198 * @param z The spawn Z position 199 * @param thrower The entity that spawned the vehicle {possibly null} 200 * @param currentEntity The current value to return if MLMP is not installed 201 * @return The new spawned entity 202 * @throws Exception 203 */ 204 public static Object mlmpVehicleSpawn(int type, World world, double x, double y, double z, Entity thrower, Object currentEntity) throws Exception 205 { 206 Class mlmp = getClass("ModLoaderMp"); 207 if (!isMLMPInstalled() || mlmp == null) 208 { 209 return currentEntity; 210 } 211 212 Object entry = mlmp.getDeclaredMethod("handleNetClientHandlerEntities", int.class).invoke(null, type); 213 if (entry == null) 214 { 215 return currentEntity; 216 } 217 218 Class entityClass = (Class)entry.getClass().getDeclaredField("entityClass").get(entry); 219 Object ret = (Entity)entityClass.getConstructor(World.class, Double.TYPE, Double.TYPE, Double.TYPE).newInstance(world, x, y, z); 220 221 if (entry.getClass().getDeclaredField("entityHasOwner").getBoolean(entry)) 222 { 223 Field owner = entityClass.getField("owner"); 224 225 if (!Entity.class.isAssignableFrom(owner.getType())) 226 { 227 throw new Exception(String.format("Entity\'s owner field must be of type Entity, but it is of type %s.", owner.getType())); 228 } 229 230 if (thrower == null) 231 { 232 System.out.println("Received spawn packet for entity with owner, but owner was not found."); 233 FMLLog.fine("Received spawn packet for entity with owner, but owner was not found."); 234 } 235 else 236 { 237 if (!owner.getType().isAssignableFrom(thrower.getClass())) 238 { 239 throw new Exception(String.format("Tried to assign an entity of type %s to entity owner, which is of type %s.", thrower.getClass(), owner.getType())); 240 } 241 242 owner.set(ret, thrower); 243 } 244 } 245 return ret; 246 } 247 248 /** 249 * Attempts to invoke ModLoaderMp.handleGUI if ModLoaderMP is installed. 250 * If not, it does nothing 251 * 252 * @param pkt The open window packet 253 */ 254 public static void mlmpOpenWindow(Packet100OpenWindow pkt) 255 { 256 Class mlmp = getClass("ModLoaderMp"); 257 if (!isMLMPInstalled() || mlmp == null) 258 { 259 return; 260 } 261 262 try 263 { 264 mlmp.getDeclaredMethod("handleGUI", Packet100OpenWindow.class).invoke(null, pkt); 265 } 266 catch (Exception e) 267 { 268 e.printStackTrace(); 269 } 270 } 271 }