/* * Copyright (c) CovertJaguar, 2014 http://railcraft.info * * This code is the property of CovertJaguar * and may only be used with explicit written * permission unless otherwise specified on the * license page at http://railcraft.info/wiki/info:license. */ package gtPlusPlus.api.objects.minecraft; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import cpw.mods.fml.common.Mod.EventHandler; import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import gregtech.api.GregTech_API; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gtPlusPlus.GTplusplus; import gtPlusPlus.api.objects.Logger; import gtPlusPlus.api.objects.data.ReverseAutoMap; import gtPlusPlus.api.objects.data.Triplet; import gtPlusPlus.core.util.Utils; import gtPlusPlus.core.util.math.MathUtils; import gtPlusPlus.xmod.gregtech.common.tileentities.machines.basic.GregtechMetaTileEntityChunkLoader; import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.ForgeChunkManager.*; import net.minecraftforge.event.entity.EntityEvent; /** * @author CovertJaguar <http://www.railcraft.info> */ public class ChunkManager implements LoadingCallback, OrderedLoadingCallback, ForgeChunkManager.PlayerOrderedLoadingCallback { private static final ChunkManager instance; private static ReverseAutoMap<String> mIdToUUIDMap = new ReverseAutoMap<String>(); public Timer mChunkQueue; public static ConcurrentHashMap<String, Triplet<Integer, GregtechMetaTileEntityChunkLoader, DimChunkPos>> mChunkLoaderManagerMap = new ConcurrentHashMap<String, Triplet<Integer, GregtechMetaTileEntityChunkLoader, DimChunkPos>>(); private static long mInternalTickCounter = 0; static { instance = new ChunkManager(); Logger.REFLECTION("Created ChunkManager object."); } private ChunkManager() { Utils.registerEvent(this); } public static void clearInternalMaps() { mIdToUUIDMap.clear(); mChunkLoaderManagerMap.clear(); } public static boolean setIdAndUniqueString(int id, String blockposString) { if (mIdToUUIDMap.injectCleanDataToAutoMap(id, blockposString)) { Logger.INFO("Found Cached ID from NBT, cleanly injected into ChunkManager."); return true; } else { Logger.INFO("ID in use, best try a few times to get a free ID."); for (int u=0;u<mIdToUUIDMap.size()+1;u++) { if (mIdToUUIDMap.injectCleanDataToAutoMap(u, blockposString)) { Logger.INFO("Success! Cleanly injected into ChunkManager."); return true; } } for (int u=0;u<50;u++) { if (mIdToUUIDMap.injectCleanDataToAutoMap(MathUtils.randInt(0, 250000), blockposString)) { Logger.INFO("Success! Cleanly injected into ChunkManager."); return true; } } return false; } } public static int getIdFromUniqueString(String blockposString) { if (mIdToUUIDMap.containsValue(blockposString)) { return mIdToUUIDMap.get(blockposString); } else { return mIdToUUIDMap.putToInternalMap(blockposString); } } public static String getUniqueStringFromID(int id) { if (mIdToUUIDMap.containsKey(id)) { return mIdToUUIDMap.get(id); } else { return "0@0@0@0"; } } @EventHandler public void serverStarting(final FMLServerStartingEvent event) { //Chunk Loading ChunkManager.getInstance().mChunkQueue = ChunkManager.createChunkQueue(); } @SubscribeEvent public void serverTick(final TickEvent.ServerTickEvent e){ mInternalTickCounter++; try { if (mInternalTickCounter % (20*15) == 0) { for (String g : mChunkLoaderManagerMap.keySet()) { BlockPos i = BlockPos.generateBlockPos(g); if (i == null) { mChunkLoaderManagerMap.remove(g); Logger.MACHINE_INFO("Bad Mapping: "+g); continue; } else { Logger.MACHINE_INFO("Good Mapping: "+i.getLocationString()); } Block mBlock = i.world.getBlock(i.xPos, i.yPos, i.zPos); TileEntity mTile = i.world.getTileEntity(i.xPos, i.yPos, i.zPos); IGregTechTileEntity mGTile = null; boolean remove = false; if (((mTile = i.world.getTileEntity(i.xPos, i.yPos, i.zPos)) != null) && (mTile instanceof IGregTechTileEntity)){ mGTile = (IGregTechTileEntity) mTile; //943-945 if (mGTile instanceof GregtechMetaTileEntityChunkLoader || mGTile.getMetaTileID() == 943 || mGTile.getMetaTileID() == 944 || mGTile.getMetaTileID() == 945) { Logger.MACHINE_INFO("Found Valid Chunk Loader Entity."); continue; } else { Logger.MACHINE_INFO("Found Valid GT Tile which was not a Chunk Loader Entity."); remove = true; } } else if ((mTile = i.world.getTileEntity(i.xPos, i.yPos, i.zPos)) != null){ Logger.MACHINE_INFO("Found Valid Tile Entity."); remove = true; } else { mTile = null; remove = true; } if (mBlock == null || mGTile == null || mBlock != GregTech_API.sBlockMachines) { remove = true; } if (remove) { //1 if (mBlock != null) { Logger.MACHINE_INFO("Found Block."); //2 if (mBlock == GregTech_API.sBlockMachines) { Logger.MACHINE_INFO("Found GT Machine."); //3 if (mTile != null) { //4 if (GregtechMetaTileEntityChunkLoader.class.isInstance(mTile.getClass())) { Logger.MACHINE_INFO("Found Valid Chunk Loader."); } else { Logger.MACHINE_INFO("Tile Entity was not a Chunk Loader."); }//4 } else { Logger.MACHINE_INFO("Tile Entity was Null though."); }//3 } else { Logger.MACHINE_INFO("Found Block that was not a GT Machine."); }//2 } else { Logger.MACHINE_INFO("Found Null Block."); }//1 } mChunkLoaderManagerMap.remove(i.getUniqueIdentifier()); Logger.INFO("Removing invalid Chunk Loader. Mapping: "+i.getUniqueIdentifier()); continue; } } } catch (Throwable t) { t.printStackTrace(); } } public static ChunkManager getInstance() { return instance; } @SubscribeEvent public void entityEnteredChunk(EntityEvent.EnteringChunk event) { } /** * Returns a Set of ChunkCoordIntPair containing the chunks between the * start and end chunks. * <p/> * One of the pairs of start/end coords need to be equal. * <p/> * Coordinates are in chunk coordinates, not world coordinates. * * @param xChunkA Start Chunk x-Coord * @param zChunkA Start Chunk z-Coord * @param xChunkB End Chunk x-Coord * @param zChunkB End Chunk z-Coord * @param max Max number of chunks to return * @return A set of chunks. */ public Set<ChunkCoordIntPair> getChunksBetween(int xChunkA, int zChunkA, int xChunkB, int zChunkB, int max) { Set<ChunkCoordIntPair> chunkList = new HashSet<ChunkCoordIntPair>(); if (xChunkA != xChunkB && zChunkA != zChunkB) { return chunkList; } int xStart = Math.min(xChunkA, xChunkB); int xEnd = Math.max(xChunkA, xChunkB); int zStart = Math.min(zChunkA, zChunkB); int zEnd = Math.max(zChunkA, zChunkB); for (int xx = xStart; xx <= xEnd; xx++) { for (int zz = zStart; zz <= zEnd; zz++) { chunkList.add(new ChunkCoordIntPair(xx, zz)); if (chunkList.size() >= max) { return chunkList; } } } return chunkList; } /** * Returns a Set of ChunkCoordIntPair containing the chunks around point [x, * z]. Coordinates are in chunk coordinates, not world coordinates. * * @param xChunk Chunk x-Coord * @param zChunk Chunk z-Coord * @param radius Distance from [x, z] to include, in number of chunks. * @return A set of chunks. */ public Set<ChunkCoordIntPair> getChunksAround(int xChunk, int zChunk, int radius) { Set<ChunkCoordIntPair> chunkList = new HashSet<ChunkCoordIntPair>(); for (int xx = xChunk - radius; xx <= xChunk + radius; xx++) { for (int zz = zChunk - radius; zz <= zChunk + radius; zz++) { chunkList.add(new ChunkCoordIntPair(xx, zz)); } } return chunkList; } /** * Returns a Set of ChunkCoordIntPair containing the chunks around point [x, * z]. Coordinates are in world coordinates, not chunk coordinates. * * @param xWorld World x-Coord * @param zWorld World z-Coord * @param radius Distance from [x, z] to include, in blocks. * @return A set of chunks. */ public Set<ChunkCoordIntPair> getBufferAround(int xWorld, int zWorld, int radius) { int minX = (xWorld - radius) >> 4; int maxX = (xWorld + radius) >> 4; int minZ = (zWorld - radius) >> 4; int maxZ = (zWorld + radius) >> 4; Set<ChunkCoordIntPair> chunkList = new HashSet<ChunkCoordIntPair>(); for (int xx = minX; xx <= maxX; xx++) { for (int zz = minZ; zz <= maxZ; zz++) { chunkList.add(new ChunkCoordIntPair(xx, zz)); } } return chunkList; } private void printAnchor(String type, int x, int y, int z) { Logger.INFO("[Chunk Loader] "+type+" @ [x: "+x+"][y: "+y+"][z: "+z+"]"); } @Override public void ticketsLoaded(List<Ticket> tickets, World world) { if (world.isRemote) return; System.out.println("Callback 2"); for (Ticket ticket : tickets) { if (ticket.isPlayerTicket()) continue; Entity entity = ticket.getEntity(); if (entity == null) { int x = ticket.getModData().getInteger("xCoord"); int y = ticket.getModData().getInteger("yCoord"); int z = ticket.getModData().getInteger("zCoord"); if (y >= 0) { BlockPos tile = new BlockPos(x, y, z, world); Ticket H = tryForceLoadChunk(new DimChunkPos(world, tile).getChunk()); int jhg = 0; while (jhg < 50) { jhg++; } if (!mChunkLoaderManagerMap.isEmpty()) { GregtechMetaTileEntityChunkLoader f = mChunkLoaderManagerMap.get(tile.getUniqueIdentifier()).getValue_2(); int timeout = 0; while (f == null) { if (timeout > 5000) { Logger.INFO("[Chunk Loader] Timed out"); break; } else { GregtechMetaTileEntityChunkLoader g; if (!mChunkLoaderManagerMap.isEmpty()) { g = mChunkLoaderManagerMap.get(tile.getUniqueIdentifier()).getValue_2(); if (g == null) { timeout++; } else { Logger.INFO("[Chunk Loader]Tile became Valid"); f = g; break; } } } } try { if (f != null) { if (H != null) { ForgeChunkManager.releaseTicket(H); } f.forceChunkLoading(f.getBaseMetaTileEntity(), ticket); printAnchor("Force Chunk Loading. Chunk Loader has ID of "+f.getUUID().toString()+". ",x,y,z); } else { Logger.INFO("Tile Entity is null."); } } catch (Throwable t) { t.printStackTrace(); Logger.INFO("Mild problem with chunk loading, nothing to worry about."); } if (H != null) { ForgeChunkManager.releaseTicket(H); } } /*if (tile instanceof IGregTechTileEntity) { final IGregTechTileEntity tGregTechTileEntity = (IGregTechTileEntity) tile; IGregTechTileEntity anchor = (IGregTechTileEntity) tile; GregtechMetaTileEntityChunkLoader jun = (GregtechMetaTileEntityChunkLoader) anchor; jun.forceChunkLoading(ticket); //printAnchor(anchor.getName(), x, y, z); }*/ } } } } @Override public List<Ticket> ticketsLoaded(List<Ticket> tickets, World world, int maxTicketCount) { // System.out.println("Callback 1"); Set<Ticket> adminTickets = new HashSet<Ticket>(); Set<Ticket> worldTickets = new HashSet<Ticket>(); Set<Ticket> cartTickets = new HashSet<Ticket>(); for (Ticket ticket : tickets) { Entity entity = ticket.getEntity(); if (entity == null) { int x = ticket.getModData().getInteger("xCoord"); int y = ticket.getModData().getInteger("yCoord"); int z = ticket.getModData().getInteger("zCoord"); if (y >= 0) { worldTickets.add(ticket); } } } List<Ticket> claimedTickets = new LinkedList<Ticket>(); claimedTickets.addAll(cartTickets); claimedTickets.addAll(adminTickets); claimedTickets.addAll(worldTickets); return claimedTickets; } @Override public ListMultimap<String, Ticket> playerTicketsLoaded(ListMultimap<String, Ticket> tickets, World world) { return LinkedListMultimap.create(); } public static Timer createChunkQueue() { return ChunkTimerLoader(); } public static Ticket tryForceLoadChunk(Chunk c) { Ticket T = getTicketFromForge(c.worldObj); if (T == null) { Logger.INFO("[Chunk Loader] Trying to force load a chunk that holds a chunkloader, however the Chunk Loading Ticket was null."); return null; } ForgeChunkManager.forceChunk(T, c.getChunkCoordIntPair()); Logger.INFO("[Chunk Loader] Trying to force load a chunk that holds a chunkloader."); return T; } public static Ticket getTicketFromForge(World world) { return ForgeChunkManager.requestTicket(GTplusplus.instance, world, Type.NORMAL); } static Timer ChunkTimerLoader() { Timer timer; timer = new Timer(); timer.schedule(new ChunkCache(), 20 * 500); timer.scheduleAtFixedRate(new ChunkCache(), 15000, 60000); return timer; } //Timer Task for notifying the player. static class ChunkCache extends TimerTask { public ChunkCache() { } @Override public void run() { if (mChunkLoaderManagerMap.size() > 0) { for (Triplet<Integer, GregtechMetaTileEntityChunkLoader, DimChunkPos> j : mChunkLoaderManagerMap.values()) { Ticket T; Chunk C; T = j.getValue_2().getTicketFromForge(j.getValue_2().getBaseMetaTileEntity()); C = j.getValue_3().getChunk(); ForgeChunkManager.forceChunk(T, C.getChunkCoordIntPair()); Logger.INFO("[Chunk Loader] Trying to force load a chunk that holds a chunkloader. [Timer]"); } } else { Logger.INFO("[Chunk Loader] No chunks to try to force load chunks that hold chunkloaders."); } } } }