    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    }