package mekanism.client.sound; import java.io.File; import java.io.FileInputStream; import java.net.URL; import java.security.CodeSource; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import mekanism.api.Coord4D; import mekanism.client.HolidayManager; import mekanism.client.MekanismClient; import mekanism.common.Mekanism; import net.minecraft.client.Minecraft; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.ForgeSubscribe; import net.minecraftforge.event.world.ChunkEvent; import paulscode.sound.SoundSystem; import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; /** * SoundHandler - a class that handles all Sounds used by Mekanism. * Runs off of PaulsCode's SoundSystem. * @author AidanBrady * */ @SideOnly(Side.CLIENT) public class SoundHandler { /** All the sound references in the Minecraft game. */ public Map sounds = Collections.synchronizedMap(new HashMap()); /** The current base volume Minecraft is using. */ public float masterVolume = 0; public Minecraft mc = Minecraft.getMinecraft(); /** * SoundHandler -- a class that handles all Sounds used by Mekanism. */ public SoundHandler() { MinecraftForge.EVENT_BUS.register(this); System.out.println("[Mekanism] Successfully set up SoundHandler."); } public void preloadSounds() { CodeSource src = getClass().getProtectionDomain().getCodeSource(); String corePath = src.getLocation().getFile().split("/mekanism/client")[0]; List listings = listFiles(corePath.replace("%20", " ").replace(".jar!", ".jar").replace("file:", ""), "assets/mekanism/sound"); for(String s : listings) { if(s.contains("etc") || s.contains("holiday")) { continue; } if(s.contains("/mekanism/sound/")) { s = s.split("/mekanism/sound/")[1]; } preloadSound(s); } System.out.println("[Mekanism] Preloaded " + listings.size() + " object sounds."); listings = listFiles(corePath.replace("%20", " ").replace(".jar!", ".jar").replace("file:", ""), "assets/mekanism/sound/etc"); for(String s : listings) { if(s.contains("/mekanism/sound/etc/")) { s = s.split("/mekanism/sound/etc/")[1]; } mc.sndManager.addSound("mekanism:etc/" + s); } System.out.println("[Mekanism] Initialized " + listings.size() + " sound effects."); if(MekanismClient.holidays) { listings = listFiles(corePath.replace("%20", " ").replace(".jar!", ".jar").replace("file:", ""), "assets/mekanism/sound/holiday"); for(String s : listings) { if(s.contains("/mekanism/sound/")) { s = s.split("/mekanism/sound/")[1]; } if(!s.contains("holiday")) { s = "holiday/" + s; } preloadSound(s); } } } private List listFiles(String path, String s) { List names = new ArrayList(); File f = new File(path); if(!f.exists()) { return names; } if(!f.isDirectory()) { try { ZipInputStream zip = new ZipInputStream(new FileInputStream(path)); while(true) { ZipEntry e = zip.getNextEntry(); if(e == null) { break; } String name = e.getName(); if(name.contains(s) && name.endsWith(".ogg")) { names.add(name); } } zip.close(); } catch(Exception e) { e.printStackTrace(); } } else { f = new File(path + "/" + s); for(File file : f.listFiles()) { if(file.getPath().contains(s) && file.getName().endsWith(".ogg")) { names.add(file.getName()); } } } return names; } private void preloadSound(String sound) { String id = "pre_" + sound; URL url = getClass().getClassLoader().getResource("assets/mekanism/sound/" + sound); if(getSoundSystem() != null) { getSoundSystem().newSource(false, id, url, sound, true, 0, 0, 0, 0, 16F); getSoundSystem().activate(id); getSoundSystem().removeSource(id); } } /** * Ticks the sound handler. Should be called every Minecraft tick, or 20 times per second. */ public void onTick() { synchronized(sounds) { if(getSoundSystem() != null) { if(!Mekanism.proxy.isPaused()) { ArrayList soundsToRemove = new ArrayList(); World world = FMLClientHandler.instance().getClient().theWorld; for(Sound sound : sounds.values()) { if(FMLClientHandler.instance().getClient().thePlayer != null && world != null) { if(!sound.update(world)) { soundsToRemove.add(sound); continue; } } } for(Sound sound : soundsToRemove) { sound.remove(); } for(Sound sound : sounds.values()) { if(sound.isPlaying) { sound.updateVolume(); } } masterVolume = FMLClientHandler.instance().getClient().gameSettings.soundVolume; } else { for(Sound sound : sounds.values()) { if(sound.isPlaying) { sound.stopLoop(); } } } } else { Mekanism.proxy.unloadSoundHandler(); } } } /** * Gets a sound object from a specific TileEntity, null if there is none. * @param tileEntity - the holder of the sound * @return Sound instance */ public Sound getFrom(Object obj) { synchronized(sounds) { return sounds.get(obj); } } /** * Create and return an instance of a Sound. * @param tileEntity - the holder of this sound. * @return Sound instance */ public void register(Object obj) { if(obj instanceof TileEntity && !(obj instanceof IHasSound)) { return; } synchronized(sounds) { if(getFrom(obj) == null) { if(obj instanceof TileEntity) { new TileSound(getIdentifier(), HolidayManager.filterSound(((IHasSound)obj).getSoundPath()), (TileEntity)obj); } } } } /** * Get a unique identifier for a sound effect instance by combining the mod's name, * Mekanism, the new sound's unique position on the 'sounds' ArrayList, and a random * number between 0 and 10,000. Example: "Mekanism_6_6123" * @return unique identifier */ public String getIdentifier() { synchronized(sounds) { String toReturn = "Mekanism_" + sounds.size() + "_" + new Random().nextInt(10000); for(Sound sound : sounds.values()) { if(sound.identifier.equals(toReturn)) { return getIdentifier(); } } return toReturn; } } /** * Plays a sound in a specific location. * @param soundPath - sound path to play * @param world - world to play in * @param object - location to play */ public void quickPlay(String soundPath, World world, Coord4D object) { URL url = getClass().getClassLoader().getResource("assets/mekanism/sound/" + soundPath); if(url == null) { System.out.println("[Mekanism] Invalid sound file: " + soundPath); } String s = getSoundSystem().quickPlay(false, url, soundPath, false, object.xCoord, object.yCoord, object.zCoord, 0, 16F); getSoundSystem().setVolume(s, masterVolume); } public static SoundSystem getSoundSystem() { return Minecraft.getMinecraft().sndManager.sndSystem; } @ForgeSubscribe public void onChunkUnload(ChunkEvent.Unload event) { if(event.getChunk() != null) { for(Object obj : event.getChunk().chunkTileEntityMap.values()) { if(obj instanceof TileEntity) { TileEntity tileEntity = (TileEntity)obj; if(tileEntity instanceof IHasSound) { if(getFrom(tileEntity) != null) { if(sounds.containsKey(tileEntity)) { getFrom(tileEntity).remove(); } } } } } } } }