    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    }