diff options
Diffstat (limited to 'src/main/java/gregtech/common')
| -rw-r--r-- | src/main/java/gregtech/common/GT_Pollution.java | 341 | ||||
| -rw-r--r-- | src/main/java/gregtech/common/GT_Proxy.java | 75 | ||||
| -rw-r--r-- | src/main/java/gregtech/common/GT_UndergroundOil.java | 267 | ||||
| -rw-r--r-- | src/main/java/gregtech/common/misc/GT_Command.java | 2 | 
4 files changed, 425 insertions, 260 deletions
| diff --git a/src/main/java/gregtech/common/GT_Pollution.java b/src/main/java/gregtech/common/GT_Pollution.java index 7b1ad90d52..865fa83f7e 100644 --- a/src/main/java/gregtech/common/GT_Pollution.java +++ b/src/main/java/gregtech/common/GT_Pollution.java @@ -7,6 +7,7 @@ import gregtech.GT_Mod;  import gregtech.api.enums.GT_Values;  import gregtech.api.interfaces.tileentity.IGregTechTileEntity;  import gregtech.api.net.GT_Packet_Pollution; +import gregtech.api.util.GT_ChunkAssociatedData;  import gregtech.api.util.GT_Utility;  import net.minecraft.block.Block;  import net.minecraft.block.material.Material; @@ -20,30 +21,34 @@ import net.minecraft.world.ChunkCoordIntPair;  import net.minecraft.world.ChunkPosition;  import net.minecraft.world.World;  import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.DimensionManager;  import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkDataEvent;  import net.minecraftforge.event.world.ChunkWatchEvent; +import net.minecraftforge.event.world.WorldEvent; +import javax.annotation.ParametersAreNonnullByDefault; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException;  import java.util.ArrayList; -import java.util.HashMap;  import java.util.List;  import static gregtech.api.objects.XSTR.XSTR_INSTANCE; -import static gregtech.common.GT_Proxy.GTPOLLUTION; -import static gregtech.common.GT_Proxy.dimensionWiseChunkData;  import static gregtech.common.GT_Proxy.dimensionWisePollution; -import static gregtech.common.GT_Proxy.getDefaultChunkDataOnCreation;  public class GT_Pollution { +	private static final Storage STORAGE = new Storage();  	/**  	 * Pollution dispersion until effects start:  	 * Calculation: ((Limit * 0.01) + 2000) * (4 <- spreading rate) -	 *  +	 * <p>  	 * SMOG(500k) 466.7 pollution/sec  	 * Poison(750k) 633,3 pollution/sec  	 * Dying Plants(1mio) 800 pollution/sec  	 * Sour Rain(1.5mio) 1133.3 pollution/sec -	 *  +	 * <p>  	 * Pollution producers (pollution/sec)  	 * Bronze Boiler(20)  	 * Lava Boiler(20) @@ -52,7 +57,7 @@ public class GT_Pollution {  	 * Diesel Generator(40/80/160)  	 * Gas Turbine(20/40/80)  	 * Charcoal Pile(100) -	 *  +	 * <p>  	 * Large Diesel Engine(320)  	 * Electric Blast Furnace(100)  	 * Implosion Compressor(2000) @@ -60,33 +65,27 @@ public class GT_Pollution {  	 * Large Gas Turbine(160)  	 * Multi Smelter(100)  	 * Pyrolyse Oven(400) -	 *  +	 * <p>  	 * Machine Explosion(100,000) -	 *  +	 * <p>  	 * Other Random Shit: lots and lots -	 *  +	 * <p>  	 * Muffler Hatch Pollution reduction:  ** inaccurate **  	 * LV (0%), MV (30%), HV (52%), EV (66%), IV (76%), LuV (84%), ZPM (89%), UV (92%), MAX (95%)  	 */  	private List<ChunkCoordIntPair> pollutionList = new ArrayList<>();//chunks left to process -	private HashMap<ChunkCoordIntPair,int[]> chunkData;//link to chunk data that is saved/loaded -	private int operationsPerTick=0;//how much chunks should be processed in each cycle -	private static final short cycleLen=1200; -	private final World aWorld; +	private final List<ChunkCoordIntPair> chunkData = new ArrayList<>();//link to chunk data that is saved/loaded +	private int operationsPerTick = 0;//how much chunks should be processed in each cycle +	private static final short cycleLen = 1200; +	private final World world;  	public static int mPlayerPollution;  	private static int POLLUTIONPACKET_MINVALUE = 1000;  	private static GT_PollutionEventHandler EVENT_HANDLER; -	public GT_Pollution(World world){ -		aWorld=world; -		chunkData=dimensionWiseChunkData.get(aWorld.provider.dimensionId); -		if(chunkData==null){ -			chunkData=new HashMap<>(1024); -			dimensionWiseChunkData.put(world.provider.dimensionId,chunkData); -		} -		dimensionWisePollution.put(aWorld.provider.dimensionId,this); +	public GT_Pollution(World world) { +		this.world = world;  		if (EVENT_HANDLER == null) {  			EVENT_HANDLER = new GT_PollutionEventHandler(); @@ -94,61 +93,61 @@ public class GT_Pollution {  		}  	} -	public static void onWorldTick(TickEvent.WorldTickEvent aEvent){//called from proxy +	public static void onWorldTick(TickEvent.WorldTickEvent aEvent) {//called from proxy  		//return if pollution disabled -		if(!GT_Mod.gregtechproxy.mPollution) return; +		if (!GT_Mod.gregtechproxy.mPollution) return;  		final GT_Pollution pollutionInstance = dimensionWisePollution.get(aEvent.world.provider.dimensionId); -		if(pollutionInstance==null)return; -		pollutionInstance.tickPollutionInWorld((int)(aEvent.world.getTotalWorldTime()%cycleLen)); +		if (pollutionInstance == null) return; +		pollutionInstance.tickPollutionInWorld((int) (aEvent.world.getTotalWorldTime() % cycleLen));  	} -	private void tickPollutionInWorld(int aTickID){//called from method above +	private void tickPollutionInWorld(int aTickID) {//called from method above  		//gen data set -		if(aTickID==0){ -			pollutionList = new ArrayList<>(chunkData.keySet()); +		if (aTickID == 0) { +			// make a snapshot of what to work on +			// counterintuitive as it seems, but this is the fastest way java collections framework offers us. +			pollutionList = new ArrayList<>(chunkData);  			//set operations per tick -			if(pollutionList.size()>0) operationsPerTick =(pollutionList.size()/cycleLen); -			else operationsPerTick=0;//SANity +			if (pollutionList.size() > 0) operationsPerTick = (pollutionList.size() / cycleLen); +			else operationsPerTick = 0;//SANity  		} -		for(int chunksProcessed=0;chunksProcessed<=operationsPerTick;chunksProcessed++){ -			if(pollutionList.size()==0)break;//no more stuff to do -			ChunkCoordIntPair actualPos=pollutionList.remove(pollutionList.size()-1);//faster -			//add default data if missing -			if(!chunkData.containsKey(actualPos)) chunkData.put(actualPos,getDefaultChunkDataOnCreation()); +		for (int chunksProcessed = 0; chunksProcessed <= operationsPerTick; chunksProcessed++) { +			if (pollutionList.size() == 0) break;//no more stuff to do +			ChunkCoordIntPair actualPos = pollutionList.remove(pollutionList.size() - 1);//faster  			//get pollution -			int tPollution = chunkData.get(actualPos)[GTPOLLUTION]; +			ChunkData currentData = STORAGE.get(world, actualPos); +			int tPollution = currentData.getAmount();  			//remove some -			tPollution = (int)(0.9945f*tPollution); +			tPollution = (int) (0.9945f * tPollution);  			//tPollution -= 2000;//This does not really matter... -			if(tPollution<=0) tPollution = 0;//SANity check -			else if(tPollution>400000){//Spread Pollution +			if (tPollution <= 0) tPollution = 0;//SANity check +			else if (tPollution > 400000) {//Spread Pollution  				ChunkCoordIntPair[] tNeighbors = new ChunkCoordIntPair[4];//array is faster -				tNeighbors[0]=(new ChunkCoordIntPair(actualPos.chunkXPos+1,actualPos.chunkZPos)); -				tNeighbors[1]=(new ChunkCoordIntPair(actualPos.chunkXPos-1,actualPos.chunkZPos)); -				tNeighbors[2]=(new ChunkCoordIntPair(actualPos.chunkXPos,actualPos.chunkZPos+1)); -				tNeighbors[3]=(new ChunkCoordIntPair(actualPos.chunkXPos,actualPos.chunkZPos-1)); -				for(ChunkCoordIntPair neighborPosition : tNeighbors){ -					if(!chunkData.containsKey(neighborPosition)) chunkData.put(neighborPosition,getDefaultChunkDataOnCreation()); - -					int neighborPollution = chunkData.get(neighborPosition)[GTPOLLUTION]; -					if(neighborPollution*6 < tPollution*5){//METHEMATICS... +				tNeighbors[0] = (new ChunkCoordIntPair(actualPos.chunkXPos + 1, actualPos.chunkZPos)); +				tNeighbors[1] = (new ChunkCoordIntPair(actualPos.chunkXPos - 1, actualPos.chunkZPos)); +				tNeighbors[2] = (new ChunkCoordIntPair(actualPos.chunkXPos, actualPos.chunkZPos + 1)); +				tNeighbors[3] = (new ChunkCoordIntPair(actualPos.chunkXPos, actualPos.chunkZPos - 1)); +				for (ChunkCoordIntPair neighborPosition : tNeighbors) { +					ChunkData neighbor = STORAGE.get(world, neighborPosition); +					int neighborPollution = neighbor.getAmount(); +					if (neighborPollution * 6 < tPollution * 5) {//MATHEMATICS...  						int tDiff = tPollution - neighborPollution; -						tDiff = tDiff/20; -						neighborPollution = GT_Utility.safeInt((long)neighborPollution+tDiff);//tNPol += tDiff; +						tDiff = tDiff / 20; +						neighborPollution = GT_Utility.safeInt((long) neighborPollution + tDiff);//tNPol += tDiff;  						tPollution -= tDiff; -						chunkData.get(neighborPosition)[GTPOLLUTION] = neighborPollution; +						neighbor.setAmount(neighborPollution);  					}  				}  				//Create Pollution effects  				//Smog filter TODO -				if(tPollution > GT_Mod.gregtechproxy.mPollutionSmogLimit) { +				if (tPollution > GT_Mod.gregtechproxy.mPollutionSmogLimit) {  					AxisAlignedBB chunk = AxisAlignedBB.getBoundingBox(actualPos.chunkXPos << 4, 0, actualPos.chunkZPos << 4, (actualPos.chunkXPos << 4) + 16, 256, (actualPos.chunkZPos << 4) + 16); -					List<EntityLivingBase> tEntitys = aWorld.getEntitiesWithinAABB(EntityLivingBase.class, chunk); +					List<EntityLivingBase> tEntitys = world.getEntitiesWithinAABB(EntityLivingBase.class, chunk);  					for (EntityLivingBase tEnt : tEntitys) {  						if (tEnt instanceof EntityPlayerMP && ((EntityPlayerMP) tEnt).capabilities.isCreativeMode)  							continue; @@ -194,131 +193,211 @@ public class GT_Pollution {  								int x = (actualPos.chunkXPos << 4) + XSTR_INSTANCE.nextInt(16);  								int y = 60 + (-f + XSTR_INSTANCE.nextInt(f * 2 + 1));  								int z = (actualPos.chunkZPos << 4) + XSTR_INSTANCE.nextInt(16); -								damageBlock(aWorld, x, y, z, tPollution > GT_Mod.gregtechproxy.mPollutionSourRainLimit); +								damageBlock(world, x, y, z, tPollution > GT_Mod.gregtechproxy.mPollutionSourRainLimit);  							}  						}  					}  				}  			}  			//Write new pollution to Hashmap !!! -			chunkData.get(actualPos)[GTPOLLUTION] = tPollution; +			currentData.setAmount(tPollution);  			//Send new value to players nearby  			if (tPollution > POLLUTIONPACKET_MINVALUE) { -				NetworkRegistry.TargetPoint point = new NetworkRegistry.TargetPoint(aWorld.provider.dimensionId, (actualPos.chunkXPos << 4), 64, (actualPos.chunkZPos << 4), 256); +				NetworkRegistry.TargetPoint point = new NetworkRegistry.TargetPoint(world.provider.dimensionId, (actualPos.chunkXPos << 4), 64, (actualPos.chunkZPos << 4), 256);  				GT_Values.NW.sendToAllAround(new GT_Packet_Pollution(actualPos, tPollution), point);  			}  		}  	} -	private static void damageBlock(World world, int x, int y, int z, boolean sourRain){ -		if (world.isRemote)	return; +	private static void damageBlock(World world, int x, int y, int z, boolean sourRain) { +		if (world.isRemote) return;  		Block tBlock = world.getBlock(x, y, z);  		int tMeta = world.getBlockMetadata(x, y, z); -		if (tBlock == Blocks.air || tBlock == Blocks.stone || tBlock == Blocks.sand|| tBlock == Blocks.deadbush)return; - -			if (tBlock == Blocks.leaves || tBlock == Blocks.leaves2 || tBlock.getMaterial() == Material.leaves) -				world.setBlockToAir(x, y, z); -			if (tBlock == Blocks.reeds) { -				tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); -				world.setBlockToAir(x, y, z); -			} -			if (tBlock == Blocks.tallgrass) -				world.setBlock(x, y, z, Blocks.deadbush); -			if (tBlock == Blocks.vine) { -				tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); -				world.setBlockToAir(x, y, z); -			} -			if (tBlock == Blocks.waterlily || tBlock == Blocks.wheat || tBlock == Blocks.cactus || +		if (tBlock == Blocks.air || tBlock == Blocks.stone || tBlock == Blocks.sand || tBlock == Blocks.deadbush) +			return; + +		if (tBlock == Blocks.leaves || tBlock == Blocks.leaves2 || tBlock.getMaterial() == Material.leaves) +			world.setBlockToAir(x, y, z); +		if (tBlock == Blocks.reeds) { +			tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); +			world.setBlockToAir(x, y, z); +		} +		if (tBlock == Blocks.tallgrass) +			world.setBlock(x, y, z, Blocks.deadbush); +		if (tBlock == Blocks.vine) { +			tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); +			world.setBlockToAir(x, y, z); +		} +		if (tBlock == Blocks.waterlily || tBlock == Blocks.wheat || tBlock == Blocks.cactus ||  				tBlock.getMaterial() == Material.cactus || tBlock == Blocks.melon_block || tBlock == Blocks.melon_stem) { -				tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); -				world.setBlockToAir(x, y, z); -			} -			if (tBlock == Blocks.red_flower || tBlock == Blocks.yellow_flower || tBlock == Blocks.carrots || +			tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); +			world.setBlockToAir(x, y, z); +		} +		if (tBlock == Blocks.red_flower || tBlock == Blocks.yellow_flower || tBlock == Blocks.carrots ||  				tBlock == Blocks.potatoes || tBlock == Blocks.pumpkin || tBlock == Blocks.pumpkin_stem) { -				tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); -				world.setBlockToAir(x, y, z); -			} -			if (tBlock == Blocks.sapling || tBlock.getMaterial() == Material.plants) -				world.setBlock(x, y, z, Blocks.deadbush); -			if (tBlock == Blocks.cocoa) { -				tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); -				world.setBlockToAir(x, y, z); -			} -			if (tBlock == Blocks.mossy_cobblestone) +			tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); +			world.setBlockToAir(x, y, z); +		} +		if (tBlock == Blocks.sapling || tBlock.getMaterial() == Material.plants) +			world.setBlock(x, y, z, Blocks.deadbush); +		if (tBlock == Blocks.cocoa) { +			tBlock.dropBlockAsItem(world, x, y, z, tMeta, 0); +			world.setBlockToAir(x, y, z); +		} +		if (tBlock == Blocks.mossy_cobblestone) +			world.setBlock(x, y, z, Blocks.cobblestone); +		if (tBlock == Blocks.grass || tBlock.getMaterial() == Material.grass) +			world.setBlock(x, y, z, Blocks.dirt); +		if (tBlock == Blocks.farmland || tBlock == Blocks.dirt) { +			world.setBlock(x, y, z, Blocks.sand); +		} + +		if (sourRain && world.isRaining() && (tBlock == Blocks.stone || tBlock == Blocks.gravel || tBlock == Blocks.cobblestone) && +				world.getBlock(x, y + 1, z) == Blocks.air && world.canBlockSeeTheSky(x, y, z)) { +			if (tBlock == Blocks.stone) {  				world.setBlock(x, y, z, Blocks.cobblestone); -			if (tBlock == Blocks.grass || tBlock.getMaterial() == Material.grass ) -				world.setBlock(x, y, z, Blocks.dirt); -			if(tBlock == Blocks.farmland || tBlock == Blocks.dirt){ +			} else if (tBlock == Blocks.cobblestone) { +				world.setBlock(x, y, z, Blocks.gravel); +			} else if (tBlock == Blocks.gravel) {  				world.setBlock(x, y, z, Blocks.sand);  			} - -			if(sourRain && world.isRaining() && (tBlock == Blocks.stone || tBlock == Blocks.gravel || tBlock == Blocks.cobblestone) && -				world.getBlock(x, y+1, z) == Blocks.air && world.canBlockSeeTheSky(x, y, z)){ -				if(tBlock == Blocks.stone){world.setBlock(x, y, z, Blocks.cobblestone);	} -				else if(tBlock == Blocks.cobblestone){world.setBlock(x, y, z, Blocks.gravel);	} -				else if(tBlock == Blocks.gravel){world.setBlock(x, y, z, Blocks.sand);	} -			} +		}  	} -	public static void addPollution(IGregTechTileEntity te, int aPollution){ -		addPollution(te.getWorld().getChunkFromBlockCoords(te.getXCoord(),te.getZCoord()), aPollution); +	public static void addPollution(IGregTechTileEntity te, int aPollution) { +		addPollution(te.getWorld().getChunkFromBlockCoords(te.getXCoord(), te.getZCoord()), aPollution);  	} -	public static void addPollution(Chunk ch, int aPollution){ -		if(!GT_Mod.gregtechproxy.mPollution)return; -		HashMap<ChunkCoordIntPair,int[]> dataMap=dimensionWiseChunkData.get(ch.worldObj.provider.dimensionId); -		if(dataMap==null){ -			dataMap=new HashMap<>(1024); -			dimensionWiseChunkData.put(ch.worldObj.provider.dimensionId,dataMap); -		} -		int[] dataArr=dataMap.get(ch.getChunkCoordIntPair()); -		if(dataArr==null){ -			dataArr=getDefaultChunkDataOnCreation(); -			dataMap.put(ch.getChunkCoordIntPair(),dataArr); -		} -		dataArr[GTPOLLUTION]+=aPollution; -		if(dataArr[GTPOLLUTION]<0)dataArr[GTPOLLUTION]=0; +	public static void addPollution(Chunk ch, int aPollution) { +		if (!GT_Mod.gregtechproxy.mPollution || aPollution == 0) return; +		STORAGE.get(ch).changeAmount(aPollution);  	} -	public static int getPollution(IGregTechTileEntity te){ -		return getPollution(te.getWorld().getChunkFromBlockCoords(te.getXCoord(),te.getZCoord())); +	public static int getPollution(IGregTechTileEntity te) { +		return getPollution(te.getWorld().getChunkFromBlockCoords(te.getXCoord(), te.getZCoord()));  	} -	public static int getPollution(Chunk ch){ -		if(!GT_Mod.gregtechproxy.mPollution) +	public static int getPollution(Chunk ch) { +		if (!GT_Mod.gregtechproxy.mPollution)  			return 0; -		HashMap<ChunkCoordIntPair,int[]> dataMap=dimensionWiseChunkData.get(ch.worldObj.provider.dimensionId); -		if(dataMap==null || dataMap.get(ch.getChunkCoordIntPair())==null) return 0; -		return dataMap.get(ch.getChunkCoordIntPair())[GTPOLLUTION]; +		return STORAGE.get(ch).getAmount(); +	} + +	public static boolean hasPollution(Chunk ch) { +		if (!GT_Mod.gregtechproxy.mPollution) +			return false; +		return STORAGE.isCreated(ch.worldObj, ch.getChunkCoordIntPair()) && STORAGE.get(ch).getAmount() > 0;  	}  	public static int getPollution(ChunkCoordIntPair aCh, int aDim) {  		if (!GT_Mod.gregtechproxy.mPollution)  			return 0; -		HashMap<ChunkCoordIntPair, int[]> dataMap = dimensionWiseChunkData.get(aDim); -		if (dataMap == null || dataMap.get(aCh) == null) -			return 0; -		return dataMap.get(aCh)[GTPOLLUTION]; +		return STORAGE.get(DimensionManager.getWorld(aDim), aCh.chunkXPos, aCh.chunkZPos).getAmount();  	}  	//Add compatibility with old code  	@Deprecated /*Don't use it... too weird way of passing position*/ -	public static void addPollution(World aWorld, ChunkPosition aPos, int aPollution){ +	public static void addPollution(World aWorld, ChunkPosition aPos, int aPollution) {  		//The abuse of ChunkPosition to store block position and dim...   		//is just bad especially when that is both used to store ChunkPos and BlockPos depending on context -		addPollution(aWorld.getChunkFromBlockCoords(aPos.chunkPosX,aPos.chunkPosZ),aPollution); +		addPollution(aWorld.getChunkFromBlockCoords(aPos.chunkPosX, aPos.chunkPosZ), aPollution); +	} + +	static void migrate(ChunkDataEvent.Load e) { +		addPollution(e.getChunk(), e.getData().getInteger("GTPOLLUTION"));  	} -	public class GT_PollutionEventHandler { +	public static class GT_PollutionEventHandler {  		@SubscribeEvent  		public void chunkWatch(ChunkWatchEvent.Watch event) { -            if(!GT_Mod.gregtechproxy.mPollution) return; -			if (chunkData.containsKey(event.chunk)) { -				int pollution = chunkData.get(event.chunk)[GTPOLLUTION]; +			if (!GT_Mod.gregtechproxy.mPollution) return; +			World world = event.player.worldObj; +			if (STORAGE.isCreated(world, event.chunk)) { +				int pollution = STORAGE.get(world, event.chunk).getAmount();  				if (pollution > POLLUTIONPACKET_MINVALUE)  					GT_Values.NW.sendToPlayer(new GT_Packet_Pollution(event.chunk, pollution), event.player);  			}  		} + +		@SubscribeEvent +		public void onWorldLoad(WorldEvent.Load e) { +			// super class loads everything lazily. We force it to load them all. +			STORAGE.loadAll(e.world); +		} +	} + +	@ParametersAreNonnullByDefault +	private static final class Storage extends GT_ChunkAssociatedData<ChunkData> { +		private Storage() { +			super("Pollution", ChunkData.class, 64, (byte) 0, false); +		} + +		@Override +		protected void writeElement(DataOutput output, ChunkData element, World world, int chunkX, int chunkZ) throws IOException { +			output.writeInt(element.getAmount()); +		} + +		@Override +		protected ChunkData readElement(DataInput input, int version, World world, int chunkX, int chunkZ) throws IOException { +			ChunkData data = new ChunkData(input.readInt()); +			getChunkData(world).add(new ChunkCoordIntPair(chunkX, chunkZ)); +			return data; +		} + +		private List<ChunkCoordIntPair> getChunkData(World world) { +			return dimensionWisePollution.computeIfAbsent(world.provider.dimensionId, i -> new GT_Pollution(world)).chunkData; +		} + +		@Override +		protected ChunkData createElement(World world, int chunkX, int chunkZ) { +			return new ChunkData(); +		} + +		@Override +		public void loadAll(World w) { +			super.loadAll(w); +		} + +		public void set(World world, ChunkCoordIntPair coord, ChunkData data) { +			set(world, coord.chunkXPos, coord.chunkZPos, data); +			getChunkData(world).add(coord); +		} + +		public boolean isCreated(World world, ChunkCoordIntPair coord) { +			return isCreated(world.provider.dimensionId, coord.chunkXPos, coord.chunkZPos); +		} +	} + +	private static final class ChunkData implements GT_ChunkAssociatedData.IData { +		public int amount; + +		private ChunkData() { +			this(0); +		} + +		private ChunkData(int amount) { +			this.amount = amount; +		} + +		/** +		 * Current pollution amount. +		 */ +		public int getAmount() { +			return amount; +		} + +		public void setAmount(int amount) { +			this.amount = Math.max(amount, 0); +		} + +		public void changeAmount(int delta) { +			this.amount = Math.max(amount + delta, 0); +		} + +		@Override +		public boolean isSameAsDefault() { +			return amount == 0; +		}  	}  } diff --git a/src/main/java/gregtech/common/GT_Proxy.java b/src/main/java/gregtech/common/GT_Proxy.java index ed99c1e3c0..a1706ee6e9 100644 --- a/src/main/java/gregtech/common/GT_Proxy.java +++ b/src/main/java/gregtech/common/GT_Proxy.java @@ -709,8 +709,8 @@ public abstract class GT_Proxy implements IGT_Mod, IGuiHandler, IFuelHandler {      }      public void onServerAboutToStart(){ -        dimensionWiseChunkData.clear();//!!! IMPORTANT for map switching...          dimensionWisePollution.clear();//!!! IMPORTANT for map switching... +        GT_ChunkAssociatedData.clearAll();      }      public void onServerStarting() { @@ -776,6 +776,7 @@ public abstract class GT_Proxy implements IGT_Mod, IGuiHandler, IFuelHandler {              } catch (Throwable e) {e.printStackTrace(GT_Log.err);}          }          this.mUniverse = null; +        //GT_ChunkAssociatedData.saveAll(); todo: figure out if this is needed      }      @SubscribeEvent @@ -1981,87 +1982,25 @@ public abstract class GT_Proxy implements IGT_Mod, IGuiHandler, IFuelHandler {      } +    @Deprecated      public static final HashMap<Integer,HashMap<ChunkCoordIntPair,int []>> dimensionWiseChunkData = new HashMap<>(16);//stores chunk data that is loaded/saved      public static final HashMap<Integer,GT_Pollution> dimensionWisePollution = new HashMap<>(16);//stores GT_Polluttors objects  	public static final byte GTOIL=3,GTOILFLUID=2,GTPOLLUTION=1,GTMETADATA=0,NOT_LOADED=0,LOADED=1;//consts -    //@Deprecated -	//public static final HashMap<ChunkPosition, int[]>  chunkData = new HashMap<>(0); - -    private static final byte oilVer=(byte)20;//non zero plz      //TO get default's fast +    @Deprecated      public static int[] getDefaultChunkDataOnCreation(){          return new int[]{NOT_LOADED,0,-1,-1};      } +    @Deprecated      public static int[] getDefaultChunkDataOnLoad(){          return new int[]{LOADED,0,-1,-1};      }      @SubscribeEvent -    public void handleChunkSaveEvent(ChunkDataEvent.Save event) {//ALWAYS SAVE FROM THE HASH MAP DATA -        HashMap<ChunkCoordIntPair,int []> chunkData=dimensionWiseChunkData.get(event.world.provider.dimensionId); -        if(chunkData==null) return;//no dim info stored - -        int[] tInts = chunkData.get(event.getChunk().getChunkCoordIntPair()); -        if(tInts==null) return;//no chunk data stored -        //assuming len of this array 4 -        if(tInts[3]>=0)event.getData().setInteger("GTOIL", tInts[GTOIL]); -        else event.getData().removeTag("GTOIL"); -        if(tInts[2]>=0)event.getData().setInteger("GTOILFLUID", tInts[GTOILFLUID]); -        else event.getData().removeTag("GTOILFLUID"); -        if(tInts[1]>0)event.getData().setInteger("GTPOLLUTION", tInts[GTPOLLUTION]); -        else event.getData().removeTag("GTPOLLUTION"); -        event.getData().setByte("GTOILVER", oilVer);//version mark -    } - -    @SubscribeEvent      public void handleChunkLoadEvent(ChunkDataEvent.Load event) { -        final int worldID=event.world.provider.dimensionId; -        HashMap<ChunkCoordIntPair, int[]> chunkData = dimensionWiseChunkData.computeIfAbsent(worldID, k -> new HashMap<>(1024)); -        if (dimensionWisePollution.get(worldID) == null) -            dimensionWisePollution.put(worldID, new GT_Pollution(event.world)); - -        int[] tInts = chunkData.get(event.getChunk().getChunkCoordIntPair()); -        if (tInts == null) { -            //NOT LOADED and NOT PROCESSED by pollution algorithms -            //regular load -            tInts = getDefaultChunkDataOnLoad(); - -            if (event.getData().getByte("GTOILVER") == oilVer) { -                if (event.getData().hasKey("GTOIL")) -                    tInts[GTOIL] = event.getData().getInteger("GTOIL"); -                if (event.getData().hasKey("GTOILFLUID")) -                    tInts[GTOILFLUID] = event.getData().getInteger("GTOILFLUID"); -            } - -            tInts[GTPOLLUTION] = event.getData().getInteger("GTPOLLUTION");//Defaults to 0 - -            //store in HASH MAP if has useful data -            if (tInts[GTPOLLUTION] > 0 || tInts[GTOIL] >= 0 || tInts[GTOILFLUID] >= 0) -                chunkData.put(event.getChunk().getChunkCoordIntPair(), tInts); -        } else if (tInts[GTMETADATA] == NOT_LOADED) {//was NOT loaded from chunk save game data -            //NOT LOADED but generated -            //append load -            if (event.getData().getByte("GTOILVER") == oilVer) { -                if (tInts[GTOIL] < 0 && event.getData().hasKey("GTOIL"))//if was not yet initialized -                    tInts[GTOIL] = event.getData().getInteger("GTOIL"); - -                if (tInts[GTOILFLUID] < 0 && event.getData().hasKey("GTOILFLUID"))//if was not yet initialized -                    tInts[GTOILFLUID] = event.getData().getInteger("GTOILFLUID"); -            } else { -                tInts[GTOIL] = -1; -                tInts[GTOILFLUID] = -1; -            } - -            tInts[GTPOLLUTION] += event.getData().getInteger("GTPOLLUTION");//Defaults to 0, add stored pollution to data -            tInts[GTMETADATA] = LOADED;//mark as = loaded -            //store in HASHMAP - -            chunkData.put(event.getChunk().getChunkCoordIntPair(), tInts); -        }//else if(tInts[0]==1){ -        ////Already loaded chunk data -        ////DO NOTHING - this chunk data was already loaded and stored in hash map -        //} +        GT_UndergroundOil.migrate(event); +        GT_Pollution.migrate(event);      }      @SubscribeEvent diff --git a/src/main/java/gregtech/common/GT_UndergroundOil.java b/src/main/java/gregtech/common/GT_UndergroundOil.java index 1f45b5fca9..cc6771dc90 100644 --- a/src/main/java/gregtech/common/GT_UndergroundOil.java +++ b/src/main/java/gregtech/common/GT_UndergroundOil.java @@ -5,22 +5,29 @@ import gregtech.api.interfaces.tileentity.IGregTechTileEntity;  import gregtech.api.objects.GT_UO_Dimension;  import gregtech.api.objects.GT_UO_Fluid;  import gregtech.api.objects.XSTR; -import net.minecraft.world.ChunkCoordIntPair; +import gregtech.api.util.GT_ChunkAssociatedData;  import net.minecraft.world.World;  import net.minecraft.world.chunk.Chunk; -import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.event.world.ChunkDataEvent; +import net.minecraftforge.fluids.Fluid;  import net.minecraftforge.fluids.FluidStack; -import java.util.HashMap; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Objects; +import java.util.WeakHashMap;  import static gregtech.api.objects.XSTR.XSTR_INSTANCE; -import static gregtech.common.GT_Proxy.*;  /**   * Created by Tec on 29.04.2017.   */  public class GT_UndergroundOil {      public static final short DIVIDER=5000; +    private static final GT_UndergroundOilStore STORAGE = new GT_UndergroundOilStore();      public static FluidStack undergroundOilReadInformation(IGregTechTileEntity te){          return undergroundOil(te.getWorld().getChunkFromBlockCoords(te.getXCoord(),te.getZCoord()),-1); @@ -37,81 +44,221 @@ public class GT_UndergroundOil {      //Returns whole content for information purposes -> when drainSpeedCoefficient < 0      //Else returns extracted fluidStack if amount > 0, or null otherwise      public static FluidStack undergroundOil(Chunk chunk, float readOrDrainCoefficient) { -        World aWorld = chunk.worldObj; -        int dimensionId=aWorld.provider.dimensionId; -        GT_UO_Dimension dimension=GT_Mod.gregtechproxy.mUndergroundOil.GetDimension(dimensionId); -        if(dimension==null) return null; - -        //Read hash map -        HashMap<ChunkCoordIntPair, int[]> chunkData = dimensionWiseChunkData.get(dimensionId); -        if(chunkData==null){ -            chunkData=new HashMap<>(1024); -            dimensionWiseChunkData.put(dimensionId,chunkData); -        } - -        int[] tInts = chunkData.get(chunk.getChunkCoordIntPair()); - -        if(tInts==null) tInts=getDefaultChunkDataOnCreation();//init if null -        else if(tInts[GTOIL]==0){//FAST stop -            //can return 0 amount stack for info :D -            return readOrDrainCoefficient>=0 ? null : new FluidStack(FluidRegistry.getFluid(tInts[GTOILFLUID]),0); -        } - -        //GEN IT TO GET OBJECT... -        final XSTR tRandom = new XSTR(aWorld.getSeed() + dimensionId * 2 + -                       (chunk.getChunkCoordIntPair().chunkXPos>>3) + -                8267 * (chunk.getChunkCoordIntPair().chunkZPos>>3)); - -        GT_UO_Fluid uoFluid = dimension.getRandomFluid(tRandom); - -        //Fluid stack holder -        FluidStack fluidInChunk; - -        //Set fluid stack from uoFluid -        if (uoFluid == null || uoFluid.getFluid()==null){ -            tInts[GTOILFLUID]=Integer.MAX_VALUE;//null fluid pointer... kind of -            tInts[GTOIL]=0; -            chunkData.put(chunk.getChunkCoordIntPair(),tInts);//update hash map +        ChunkData chunkData = STORAGE.get(chunk); +        if (chunkData.getVein() == null || chunkData.getFluid() == null) // nothing here...              return null; -        } else { -            if(tInts[GTOILFLUID]== uoFluid.getFluid().getID()){//if stored fluid matches uoFluid -                fluidInChunk = new FluidStack(uoFluid.getFluid(),tInts[GTOIL]); -            }else{ -                fluidInChunk  = new FluidStack(uoFluid.getFluid(), uoFluid.getRandomAmount(tRandom)); -                fluidInChunk.amount=(int)((float)fluidInChunk.amount*(0.75f+(XSTR_INSTANCE.nextFloat()/2f)));//Randomly change amounts by +/- 25% -            } -            tInts[GTOIL]=fluidInChunk.amount; -            tInts[GTOILFLUID]=fluidInChunk.getFluidID(); -        } -          //do stuff on it if needed +        FluidStack fluidInChunk = new FluidStack(chunkData.getFluid(), 0);          if(readOrDrainCoefficient>=0){ -            int fluidExtracted=(int)Math.floor(fluidInChunk.amount * (double) readOrDrainCoefficient / DIVIDER); -            double averageDecrease=uoFluid.DecreasePerOperationAmount * (double)readOrDrainCoefficient; +            int fluidExtracted = (int) Math.floor(chunkData.getAmount() * (double) readOrDrainCoefficient / DIVIDER); +            double averageDecrease = chunkData.getVein().DecreasePerOperationAmount * (double) readOrDrainCoefficient;              int decrease=(int)Math.ceil(averageDecrease); -            if(fluidExtracted<=0 || fluidInChunk.amount<=decrease){//decrease - here it is max value of extraction for easy check -                fluidInChunk=null; -                tInts[GTOIL]=0;//so in next access it will stop way above +            if (fluidExtracted <= 0 || chunkData.amount <= decrease) {//decrease - here it is max value of extraction for easy check +                chunkData.setAmount(0);              }else{                  fluidInChunk.amount = fluidExtracted;//give appropriate amount -                if(XSTR_INSTANCE.nextFloat()<(decrease-averageDecrease)) decrease--;//use XSTR_INSTANCE to "subtract double from int" +                if (XSTR_INSTANCE.nextFloat() < (decrease - averageDecrease)) +                    decrease--;//use XSTR_INSTANCE to "subtract double from int"                  //ex.                  // averageDecrease=3.9                  // decrease= ceil from 3.9 = 4                  // decrease-averageDecrease=0.1 -> chance to subtract 1                  // if XSTR_INSTANCE is < chance then subtract 1 -                tInts[GTOIL]-=decrease;//diminish amount, "randomly" adjusted to double value (averageDecrease) +                chunkData.changeAmount(-decrease);//diminish amount, "randomly" adjusted to double value (averageDecrease)              }          }else{//just get info              if(fluidInChunk.amount<=DIVIDER){                  fluidInChunk.amount=0;//return informative stack -                tInts[GTOIL]=0;//so in next access it will stop way above +                chunkData.setAmount(0);              }else{                  fluidInChunk.amount=fluidInChunk.amount/DIVIDER;//give moderate extraction speed              }          } - -        chunkData.put(chunk.getChunkCoordIntPair(),tInts);//update hash map          return fluidInChunk;      } + +    static void migrate(ChunkDataEvent.Load e) { +        if (e.getData().hasKey("GTOIL") && e.getData().hasKey("GTOILFLUID")) { +            ChunkData chunkData = STORAGE.get(e.getChunk()); +            Fluid fluid = chunkData.getFluid(); +            if (fluid != null && fluid.getID() == e.getData().getInteger("GTOIL")) +                chunkData.setAmount(Math.min(chunkData.getAmount(), e.getData().getInteger("GTOILFLUID"))); +        } +    } + +    /** +     * Revamped UO store. +     * <p> +     * Primary functionality: +     * +     * <ul> +     *     <li>Decouple data storage with chunk, making it possible to pump oil from unloaded chunks</li> +     *     <li>Regen detection. If fluid generation config is changed, chunk fluid will be regenerated.</li> +     * </ul> +     * +     * <h2>Serialized form</h2> +     * <p> +     * Since the exact file layout is controlled by the super class, here we only concern how each chunk's data is written. +     * <h3>Form A: Empty Chunk</h3> +     * <ol> +     *     <li>4 bytes of 0 </li> +     * </ol> +     * +     * <h3>Form B: Normal Chunk</h3> +     * <ol> +     *     <li>4 bytes unsigned integer. Vein Hash.</li> +     *     <li>UTF string. Vein Key.</li> +     *     <li>4 bytes signed integer. Fluid amount.</li> +     * </ol> +     * +     * @author glease +     */ +    @ParametersAreNonnullByDefault +    private static class GT_UndergroundOilStore extends GT_ChunkAssociatedData<ChunkData> { +        private static final GT_UndergroundOil.ChunkData NIL_FLUID_STACK = new GT_UndergroundOil.ChunkData(-1, null, null, false); +        private static final WeakHashMap<GT_UO_Fluid, Integer> hashes = new WeakHashMap<>(); + +        private GT_UndergroundOilStore() { +            super("UO", GT_UndergroundOil.ChunkData.class, 64, (byte) 0, false); +        } + +        @Override +        protected void writeElement(DataOutput output, ChunkData element, World world, int chunkX, int chunkZ) throws IOException { +            /* see class javadoc for explanation */ +            output.writeInt(element.getVeinHash()); +            if (element.getVeinKey() == null) return; +            output.writeUTF(element.getVeinKey()); +            if (element.getAmount() > 0 && element.getFluid() != null) { +                output.writeInt(element.getAmount()); +            } else { +                output.writeInt(-1); +            } +        } + +        @Override +        protected GT_UndergroundOil.ChunkData readElement(DataInput input, int version, World world, int chunkX, int chunkZ) throws IOException { +            /* see class javadoc for explanation */ +            if (version != 0) +                throw new IOException("Region file corrupted"); +            GT_UndergroundOil.ChunkData pristine = createElement(world, chunkX, chunkZ); +            int hash = input.readInt(); +            String veinKey = hash != 0 ? input.readUTF() : null; +            int amount = hash != 0 ? input.readInt() : -1; +            if (hash != pristine.veinHash || !Objects.equals(veinKey, pristine.getVeinKey())) { +                // vein config changed. use regen-ed data. +                return pristine; +            } +            if (hash == 0) +                return NIL_FLUID_STACK; +            return new GT_UndergroundOil.ChunkData(amount, GT_Mod.gregtechproxy.mUndergroundOil.GetDimension(world.provider.dimensionId).getUOFluid(veinKey), veinKey); +        } + +        @Override +        protected GT_UndergroundOil.ChunkData createElement(World world, int chunkX, int chunkZ) { +            int dimensionId = world.provider.dimensionId; +            GT_UO_Dimension dimension = GT_Mod.gregtechproxy.mUndergroundOil.GetDimension(dimensionId); +            if (dimension == null) return NIL_FLUID_STACK; +            // prepare RNG 🙏 🙏 🙏 +            final XSTR tRandom = new XSTR(world.getSeed() + dimensionId * 2L + (chunkX >> 3) + 8267L * (chunkZ >> 3)); +            GT_UO_Fluid uoFluid = dimension.getRandomFluid(tRandom); +            // nothing here :( +            if (uoFluid == null || uoFluid.getFluid() == null) return NIL_FLUID_STACK; +            // offset each chunk's fluid amount by +-25% +            int amount = (int) ((float) uoFluid.getRandomAmount(tRandom) * (0.75f + (XSTR_INSTANCE.nextFloat() / 2f))); +            return new GT_UndergroundOil.ChunkData(amount, uoFluid, dimension.getUOFluidKey(uoFluid), false); +        } + +        private static int hash(@Nullable GT_UO_Fluid fluid) { +            if (fluid == null) +                return 0; +            int result = fluid.Registry.hashCode(); +            result = 31 * result + fluid.MaxAmount; +            result = 31 * result + fluid.MinAmount; +            result = 31 * result + fluid.Chance; +            result = 31 * result + fluid.DecreasePerOperationAmount; +            return result == 0 ? 1 : result; +        } + +    } + +    /** +     * Represent the amount of fluid in a given chunk. +     */ +    private static final class ChunkData implements GT_ChunkAssociatedData.IData { +        private final Fluid fluid; +        @Nullable +        private final GT_UO_Fluid vein; +        private final String veinKey; +        private final int veinHash; +        private int amount; +        private boolean dirty; + +        private ChunkData(int amount, GT_UO_Fluid veinKey, String veinID) { +            this(amount, veinKey, veinID, true); +        } + +        private ChunkData(int amount, @Nullable GT_UO_Fluid vein, @Nullable String veinKey, boolean dirty) { +            this.amount = amount; +            this.vein = vein; +            this.dirty = dirty; +            if (vein == null) { +                fluid = null; +                this.veinKey = null; +                veinHash = 0; +            } else { +                fluid = vein.getFluid(); +                this.veinKey = veinKey; +                veinHash = GT_UndergroundOilStore.hashes.computeIfAbsent(vein, GT_UndergroundOilStore::hash); +            } +        } + +        /** +         * The current fluid type. {@code null} if vein is generated to be empty. +         */ +        @Nullable +        public Fluid getFluid() { +            return fluid; +        } + +        /** +         * Current fluid amount. Might be 0 if empty. Cannot be negative +         */ +        public int getAmount() { +            return amount; +        } + +        public void setAmount(int amount) { +            if (this.amount != amount) +                dirty = true; +            this.amount = Math.max(0, amount); +        } + +        public void changeAmount(int delta) { +            if (delta != 0) +                dirty = true; +            this.amount = Math.max(0, amount - delta); +        } + +        @Nullable +        public GT_UO_Fluid getVein() { +            return vein; +        } + +        /** +         * The vein ID. Might be null if generated to be empty. +         */ +        @Nullable +        public String getVeinKey() { +            return veinKey; +        } + +        public int getVeinHash() { +            return veinHash; +        } + +        @Override +        public boolean isSameAsDefault() { +            return dirty; +        } +    }  } diff --git a/src/main/java/gregtech/common/misc/GT_Command.java b/src/main/java/gregtech/common/misc/GT_Command.java index 13e26353d8..4f6f77ea70 100644 --- a/src/main/java/gregtech/common/misc/GT_Command.java +++ b/src/main/java/gregtech/common/misc/GT_Command.java @@ -61,7 +61,7 @@ public final class GT_Command extends CommandBase {          } else if (test.equals("toggle")) {              String test1 = ss[1].trim();              Stream.of("D1", "D2", "debugCleanroom", "debugDriller", "debugBlockPump", "debugBlockMiner", "debugWorldGen", "debugEntityCramming", -                    "debugOrevein", "debugSmallOres", "debugStones", "debugChunkloaders", "debugMulti") +                    "debugOrevein", "debugSmallOres", "debugStones", "debugChunkloaders", "debugMulti", "debugWorldData")                      .filter(s -> test1.isEmpty() || s.startsWith(test1))                      .forEach(l::add); | 
