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