package com.xcompwiz.lookingglass.client.proxyworld; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.WeakHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.ChunkCoordinates; import net.minecraftforge.common.DimensionManager; import com.xcompwiz.lookingglass.client.render.FrameBufferContainer; import com.xcompwiz.lookingglass.entity.EntityCamera; import com.xcompwiz.lookingglass.log.LoggerUtils; import com.xcompwiz.lookingglass.network.LookingGlassPacketManager; import com.xcompwiz.lookingglass.network.packet.PacketCreateView; import com.xcompwiz.lookingglass.proxyworld.ModConfigs; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) public class ProxyWorldManager { private static Map<Integer, WorldClient> proxyworlds = new HashMap<Integer, WorldClient>(); private static Collection<WorldClient> proxyworldset = Collections.unmodifiableCollection(proxyworlds.values()); /** We actually populate this with weak sets. This allows for the world views to be freed without us needing to do anything. */ private static Map<Integer, Collection<WorldView>> worldviewsets = new HashMap<Integer, Collection<WorldView>>(); /** * This is a complex bit. As we want to reuse the current client world when rendering, if possible, we need to handle when that world changes. We could * simply destroy all the views pointing to the existing proxy world, but that would be annoying to mods using the API. Instead, we replace our proxy world * with the new client world. This should only be called by LookingGlass, and only from the handling of the client world change detection. * @param world The new client world */ public static void handleWorldChange(WorldClient world) { if (ModConfigs.disabled) return; if (world == null) return; int dimid = world.provider.dimensionId; if (!proxyworlds.containsKey(dimid)) return; //BEST CASE! We don't have to do anything! proxyworlds.put(dimid, world); Collection<WorldView> worldviews = worldviewsets.get(dimid); for (WorldView view : worldviews) { // Handle the change on the view object view.replaceWorldObject(world); } } public static synchronized void detectFreedWorldViews() { FrameBufferContainer.detectFreedWorldViews(); //TODO: closeViewConnection(worldviewID); HashSet<Integer> emptyLists = new HashSet<Integer>(); for (Map.Entry<Integer, Collection<WorldView>> entry : worldviewsets.entrySet()) { if (entry.getValue().isEmpty()) emptyLists.add(entry.getKey()); } for (Integer dimId : emptyLists) { unloadProxyWorld(dimId); } } public static synchronized WorldClient getProxyworld(int dimid) { if (ModConfigs.disabled) return null; WorldClient proxyworld = proxyworlds.get(dimid); if (proxyworld == null) { if (!DimensionManager.isDimensionRegistered(dimid)) return null; // We really don't want to be doing this during a render cycle if (Minecraft.getMinecraft().thePlayer instanceof EntityCamera) return null; //TODO: This check probably needs to be altered WorldClient theWorld = Minecraft.getMinecraft().theWorld; if (theWorld != null && theWorld.provider.dimensionId == dimid) proxyworld = theWorld; if (proxyworld == null) proxyworld = new ProxyWorld(dimid); proxyworlds.put(dimid, proxyworld); worldviewsets.put(dimid, Collections.newSetFromMap(new WeakHashMap<WorldView, Boolean>())); } return proxyworld; } private static void unloadProxyWorld(int dimId) { Collection<WorldView> set = worldviewsets.remove(dimId); if (set != null && set.size() > 0) LoggerUtils.warn("Unloading ProxyWorld with live views"); WorldClient proxyworld = proxyworlds.remove(dimId); WorldClient theWorld = Minecraft.getMinecraft().theWorld; if (theWorld != null && theWorld == proxyworld) return; if (proxyworld != null) net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Unload(proxyworld)); } public static void clearProxyworlds() { while (!proxyworlds.isEmpty()) { unloadProxyWorld(proxyworlds.keySet().iterator().next()); } } public static Collection<WorldClient> getProxyworlds() { return proxyworldset; } public static Collection<WorldView> getWorldViews(int dimid) { Collection<WorldView> set = worldviewsets.get(dimid); if (set == null) return Collections.emptySet(); return Collections.unmodifiableCollection(set); } public static WorldView createWorldView(int dimid, ChunkCoordinates spawn, int width, int height) { if (ModConfigs.disabled) return null; if (!DimensionManager.isDimensionRegistered(dimid)) return null; WorldClient proxyworld = ProxyWorldManager.getProxyworld(dimid); if (proxyworld == null) return null; Collection<WorldView> worldviews = worldviewsets.get(dimid); if (worldviews == null) return null; WorldView view = new WorldView(proxyworld, spawn, width, height); // Initialize the view rendering system Minecraft mc = Minecraft.getMinecraft(); EntityLivingBase backup = mc.renderViewEntity; mc.renderViewEntity = view.camera; view.getRenderGlobal().setWorldAndLoadRenderers(proxyworld); mc.renderViewEntity = backup; // Inform the server of the new view LookingGlassPacketManager.bus.sendToServer(PacketCreateView.createPacket(view)); worldviews.add(view); return view; } //TODO: private static void closeViewConnection(long worldviewID) { //LookingGlassPacketManager.bus.sendToServer(PacketCloseView.createPacket(worldviewID)); //} /** * Handles explicit shutdown of a world view. Tells the view to clean itself up and removes it from the tracked world views here (encouraging the world to unload). * @param view The view to kill */ public static void destroyWorldView(WorldView view) { Collection<WorldView> set = worldviewsets.get(view.getWorldObj().provider.dimensionId); if (set != null) set.remove(view); //TODO: closeViewConnection(worldviewID); view.cleanup(); } }