diff options
Diffstat (limited to 'src/main/java/gregtech/common')
-rw-r--r-- | src/main/java/gregtech/common/GT_Pollution.java | 178 | ||||
-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, 341 insertions, 181 deletions
diff --git a/src/main/java/gregtech/common/GT_Pollution.java b/src/main/java/gregtech/common/GT_Pollution.java index 7b1ad90d52..2a13d692b3 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) - * + * * 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 - * + * * Pollution producers (pollution/sec) * Bronze Boiler(20) * Lava Boiler(20) @@ -69,10 +74,10 @@ public class GT_Pollution { * 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 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 aWorld; + private final World world; public static int mPlayerPollution; private static int POLLUTIONPACKET_MINVALUE = 1000; @@ -80,13 +85,7 @@ public class GT_Pollution { 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); + this.world = world; if (EVENT_HANDLER == null) { EVENT_HANDLER = new GT_PollutionEventHandler(); @@ -105,7 +104,9 @@ public class GT_Pollution { private void tickPollutionInWorld(int aTickID){//called from method above //gen data set if(aTickID==0){ - pollutionList = new ArrayList<>(chunkData.keySet()); + // 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 @@ -114,10 +115,9 @@ public class GT_Pollution { 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()); //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 -= 2000;//This does not really matter... @@ -131,15 +131,14 @@ public class GT_Pollution { 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... + 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; tPollution -= tDiff; - chunkData.get(neighborPosition)[GTPOLLUTION] = neighborPollution; + neighbor.setAmount(neighborPollution); } } @@ -148,7 +147,7 @@ public class GT_Pollution { //Smog filter TODO 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,18 +193,18 @@ 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); } } @@ -266,19 +265,8 @@ public class GT_Pollution { } 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; + if(!GT_Mod.gregtechproxy.mPollution || aPollution == 0)return; + STORAGE.get(ch).changeAmount(aPollution); } public static int getPollution(IGregTechTileEntity te){ @@ -288,18 +276,19 @@ public class GT_Pollution { 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 @@ -310,15 +299,100 @@ public class GT_Pollution { addPollution(aWorld.getChunkFromBlockCoords(aPos.chunkPosX,aPos.chunkPosZ),aPollution); } - public class GT_PollutionEventHandler { + static void migrate(ChunkDataEvent.Load e) { + addPollution(e.getChunk(), e.getData().getInteger("GTPOLLUTION")); + } + + 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); |