package micdoodle8.mods.galacticraft.planets.asteroids.tick; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import micdoodle8.mods.galacticraft.api.vector.BlockVec3; import micdoodle8.mods.galacticraft.core.entities.player.GCPlayerStats; import micdoodle8.mods.galacticraft.core.util.GCCoreUtil; import micdoodle8.mods.galacticraft.core.util.GCLog; import micdoodle8.mods.galacticraft.core.util.WorldUtil; import micdoodle8.mods.galacticraft.planets.asteroids.dimension.ShortRangeTelepadHandler; import micdoodle8.mods.galacticraft.planets.asteroids.entities.EntityAstroMiner; import micdoodle8.mods.galacticraft.planets.asteroids.tile.TileEntityMinerBase; import net.minecraft.server.MinecraftServer; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraft.world.WorldProvider; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; import net.minecraftforge.fml.common.gameevent.TickEvent.WorldTickEvent; public class AsteroidsTickHandlerServer { public static ShortRangeTelepadHandler spaceRaceData = null; public static List<EntityAstroMiner> activeMiners = new ArrayList<>(); public static AtomicBoolean loadingSavedChunks = new AtomicBoolean(); private static Field droppedChunks = null; public static void restart() { spaceRaceData = null; activeMiners.clear(); } @SubscribeEvent public void onServerTick(TickEvent.ServerTickEvent event) { MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); //Prevent issues when clients switch to LAN servers if (server == null) { return; } if (event.phase == TickEvent.Phase.START) { TileEntityMinerBase.checkNewMinerBases(); if (AsteroidsTickHandlerServer.spaceRaceData == null) { World world = server.worldServerForDimension(0); AsteroidsTickHandlerServer.spaceRaceData = (ShortRangeTelepadHandler) world.getMapStorage().loadData(ShortRangeTelepadHandler.class, ShortRangeTelepadHandler.saveDataID); if (AsteroidsTickHandlerServer.spaceRaceData == null) { AsteroidsTickHandlerServer.spaceRaceData = new ShortRangeTelepadHandler(ShortRangeTelepadHandler.saveDataID); world.getMapStorage().setData(ShortRangeTelepadHandler.saveDataID, AsteroidsTickHandlerServer.spaceRaceData); } } int index = -1; for(EntityAstroMiner miner : activeMiners) { index ++; if (miner.isDead) { // minerIt.remove(); Don't remove it, we want the index number to be static for the others continue; } if (miner.playerMP != null) { GCPlayerStats stats = GCPlayerStats.get(miner.playerMP); if (stats != null) { List<BlockVec3> list = stats.getActiveAstroMinerChunks(); boolean inListAlready = false; Iterator<BlockVec3> it = list.iterator(); while (it.hasNext()) { BlockVec3 data = it.next(); if (data.sideDoneBits == index) //SideDoneBits won't be saved to NBT, but during an active server session we can use it as a cross-reference to the index here - it's a 4th data int hidden inside a BlockVec3 { if (miner.isDead) { it.remove(); //Player stats should not save position of dead AstroMiner entity (probably broken by player deliberately breaking it) } else { data.x = miner.chunkCoordX; data.z = miner.chunkCoordZ; } inListAlready = true; break; } } if (!inListAlready && !miner.isDead) { BlockVec3 data = new BlockVec3(miner.chunkCoordX, miner.dimension, miner.chunkCoordZ); data.sideDoneBits = index; list.add(data); } } } } } } @SubscribeEvent public void onWorldTick(WorldTickEvent event) { if (event.phase == Phase.END) { int index = -1; for(EntityAstroMiner miner : activeMiners) { index ++; if (miner.isDead) { // minerIt.remove(); Don't remove it, we want the index number to be static for the others continue; } if (miner.playerMP != null && miner.worldObj == event.world) { try { if (droppedChunks == null) { Class clazz = ((WorldServer)miner.worldObj).theChunkProviderServer.getClass(); droppedChunks = clazz.getDeclaredField(GCCoreUtil.isDeobfuscated() ? "droppedChunksSet" : "field_73248_b"); } Set<Long> undrop = (Set<Long>) droppedChunks.get(((WorldServer)miner.worldObj).theChunkProviderServer); undrop.remove(ChunkCoordIntPair.chunkXZ2Int(miner.chunkCoordX, miner.chunkCoordZ)); } catch (Exception ignore) { } } } } } public static void removeChunkData(GCPlayerStats stats, EntityAstroMiner entityToRemove) { int index = 0; for(EntityAstroMiner miner : activeMiners) { if (miner == entityToRemove) //Found it in the list here { List<BlockVec3> list = stats.getActiveAstroMinerChunks(); Iterator<BlockVec3> it = list.iterator(); while (it.hasNext()) { BlockVec3 data = it.next(); if (data.sideDoneBits == index) //Found it in the player's stats { it.remove(); return; } } return; } index++; } } /** * How this works: every spawned or saved (in player stats) miner is added to the * activeMiners list here. * Once per server tick its position will be saved to player stats. * When the player quits, the saved miner positions will be saved with the player's stats NBT * When the player next loads, loadAstroChunkList will force load those chunks, therefore * reactivating AstroMiners if those chunks weren't already loaded. */ public static void loadAstroChunkList(List<BlockVec3> activeChunks) { List<BlockVec3> copyList = new LinkedList<>(activeChunks); activeChunks.clear(); if (!(AsteroidsTickHandlerServer.loadingSavedChunks.getAndSet(true))) { for (BlockVec3 data : copyList) { WorldProvider p = WorldUtil.getProviderForDimensionServer(data.y); if (p != null && p.worldObj != null) { GCLog.debug("Loading chunk " + data.y + ": " + data.x + "," + data.z + " - should contain a miner!"); ((WorldServer)p.worldObj).theChunkProviderServer.loadChunk(data.x, data.z); } } AsteroidsTickHandlerServer.loadingSavedChunks.set(false); } } public static int monitorMiner(EntityAstroMiner entityAstroMiner) { int result = activeMiners.size(); activeMiners.add(entityAstroMiner); GCLog.debug("Monitoring miner " + result); return result; } }