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 * Tries 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 * 081 * @param path The path to walk 082 * @param pool The pool to add sound to 083 */ 084 private static void audioModLoadModAudio(String path, SoundPool pool) 085 { 086 File folder = new File(Minecraft.getMinecraftDir(), path); 087 088 try 089 { 090 audioModWalkFolder(folder, folder, pool); 091 } 092 catch (IOException ex) 093 { 094 FMLLog.log(Level.FINE, ex, "Loading Mod audio failed for folder: %s", path); 095 ex.printStackTrace(); 096 } 097 } 098 099 /** 100 * Walks the folder path recursively and calls pool.addSound on any file it finds. 101 * 102 * @param base The base path for the folder, determines the name when calling addSound 103 * @param folder The current folder 104 * @param pool The SoundPool to add the sound to 105 * @throws IOException 106 */ 107 private static void audioModWalkFolder(File base, File folder, SoundPool pool) throws IOException 108 { 109 if (folder.exists() || folder.mkdirs()) 110 { 111 for (File file : folder.listFiles()) 112 { 113 if (!file.getName().startsWith(".")) 114 { 115 if (file.isDirectory()) 116 { 117 audioModWalkFolder(base, file, pool); 118 } 119 else if (file.isFile()) 120 { 121 String subpath = file.getPath().substring(base.getPath().length() + 1).replace('\\', '/'); 122 pool.addSound(subpath, file); 123 } 124 } 125 } 126 } 127 } 128 129 /** 130 * Adds the IBXM codec and associates it with .xm, .s3m, and .mod 131 */ 132 public static void audioModAddCodecs() 133 { 134 SoundSystemConfig.setCodec("xm", CodecIBXM.class); 135 SoundSystemConfig.setCodec("s3m", CodecIBXM.class); 136 SoundSystemConfig.setCodec("mod", CodecIBXM.class); 137 } 138 139 /** 140 * If the current player is underground, it picks a random song from the cave sound pool, 141 * if they are not it returns the passed in entry. 142 * 143 * @param soundManager The SoundManager instance 144 * @param current The currently selected entry 145 * @return A soundPool entry to be played as the background music 146 */ 147 public static SoundPoolEntry audioModPickBackgroundMusic(SoundManager soundManager, SoundPoolEntry current) 148 { 149 Minecraft mc = FMLClientHandler.instance().getClient(); 150 if (mc != null && mc.theWorld != null && audioModSoundPoolCave != null) 151 { 152 Entity ent = mc.renderViewEntity; 153 int x = MathHelper.truncateDoubleToInt(ent.posX); 154 int y = MathHelper.truncateDoubleToInt(ent.posY); 155 int z = MathHelper.truncateDoubleToInt(ent.posZ); 156 return (mc.theWorld.canBlockSeeTheSky(x, y, z) ? current : audioModSoundPoolCave.getRandomSound()); 157 } 158 return current; 159 } 160 161 /*********************************************************************************************************** 162 * SDK's ModLoaderMP 163 * http://www.minecraftforum.net/topic/86765- 164 * 165 * ModLoaderMP was supposed to be a reliable server side version of ModLoader, however it has 166 * gotten the reputation of being really slow to update. Never having bugfixes, breaking compatibility 167 * with the client side ModLoader. 168 * 169 * So we have replaced it with our own system called FML (Forge ModLoader) 170 * it is a stand alone mod, that Forge relies on, and that is open source/community driven. 171 * https://github.com/cpw/FML 172 * 173 * However, for compatibilities sake, we provide the ModLoaderMP's hooks so that the end user 174 * does not need to make a choice between the two on the client side. 175 **/ 176 private static int isMLMPInstalled = -1; 177 178 /** 179 * Determine if ModLoaderMP is installed by checking for the existence of the BaseModMp class. 180 * @return True if BaseModMp was installed (indicating the existance of MLMP) 181 */ 182 public static boolean isMLMPInstalled() 183 { 184 if (isMLMPInstalled == -1) 185 { 186 isMLMPInstalled = (getClass("ModLoaderMp") != null ? 1 : 0); 187 } 188 return isMLMPInstalled == 1; 189 } 190 191 /** 192 * Attempts to spawn a vehicle using ModLoaderMP's vehicle spawn registry, if MLMP is not installed 193 * it returns the passed in currentEntity 194 * 195 * @param type The Type ID of the vehicle 196 * @param world The current world 197 * @param x The spawn X position 198 * @param y The spawn Y position 199 * @param z The spawn Z position 200 * @param thrower The entity that spawned the vehicle {possibly null} 201 * @param currentEntity The current value to return if MLMP is not installed 202 * @return The new spawned entity 203 * @throws Exception 204 */ 205 public static Object mlmpVehicleSpawn(int type, World world, double x, double y, double z, Entity thrower, Object currentEntity) throws Exception 206 { 207 Class mlmp = getClass("ModLoaderMp"); 208 if (!isMLMPInstalled() || mlmp == null) 209 { 210 return currentEntity; 211 } 212 213 Object entry = mlmp.getDeclaredMethod("handleNetClientHandlerEntities", int.class).invoke(null, type); 214 if (entry == null) 215 { 216 return currentEntity; 217 } 218 219 Class entityClass = (Class)entry.getClass().getDeclaredField("entityClass").get(entry); 220 Object ret = (Entity)entityClass.getConstructor(World.class, Double.TYPE, Double.TYPE, Double.TYPE).newInstance(world, x, y, z); 221 222 if (entry.getClass().getDeclaredField("entityHasOwner").getBoolean(entry)) 223 { 224 Field owner = entityClass.getField("owner"); 225 226 if (!Entity.class.isAssignableFrom(owner.getType())) 227 { 228 throw new Exception(String.format("Entity\'s owner field must be of type Entity, but it is of type %s.", owner.getType())); 229 } 230 231 if (thrower == null) 232 { 233 System.out.println("Received spawn packet for entity with owner, but owner was not found."); 234 FMLLog.fine("Received spawn packet for entity with owner, but owner was not found."); 235 } 236 else 237 { 238 if (!owner.getType().isAssignableFrom(thrower.getClass())) 239 { 240 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())); 241 } 242 243 owner.set(ret, thrower); 244 } 245 } 246 return ret; 247 } 248 249 /** 250 * Attempts to invoke ModLoaderMp.handleGUI if ModLoaderMP is installed. 251 * If not, it does nothing 252 * 253 * @param pkt The open window packet 254 */ 255 public static void mlmpOpenWindow(Packet100OpenWindow pkt) 256 { 257 Class mlmp = getClass("ModLoaderMp"); 258 if (!isMLMPInstalled() || mlmp == null) 259 { 260 return; 261 } 262 263 try 264 { 265 mlmp.getDeclaredMethod("handleGUI", Packet100OpenWindow.class).invoke(null, pkt); 266 } 267 catch (Exception e) 268 { 269 e.printStackTrace(); 270 } 271 } 272 }