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 }