diff options
Diffstat (limited to 'src/main/java/gregtech/common/GT_UndergroundOil.java')
| -rw-r--r-- | src/main/java/gregtech/common/GT_UndergroundOil.java | 267 | 
1 files changed, 207 insertions, 60 deletions
| 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; +        } +    }  } | 
