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