aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Robertz <dream-master@gmx.net>2021-11-16 15:58:07 +0100
committerGitHub <noreply@github.com>2021-11-16 15:58:07 +0100
commit2265c1f0d2038dd1345146a1b9aecadcf5f48d58 (patch)
tree5752aa2e16c341f2df3ed6a22adeb72e8c2892ba
parentd67f01dacdcf50d7ba7ce4fa48a3dda175669c69 (diff)
parent0fae19ee0ac73500b8a141c002789594f0fdd354 (diff)
downloadGT5-Unofficial-2265c1f0d2038dd1345146a1b9aecadcf5f48d58.tar.gz
GT5-Unofficial-2265c1f0d2038dd1345146a1b9aecadcf5f48d58.tar.bz2
GT5-Unofficial-2265c1f0d2038dd1345146a1b9aecadcf5f48d58.zip
Merge pull request #733 from GTNewHorizons/patch-uo
Underground oil and pollution persistence form rework
-rw-r--r--src/main/java/gregtech/GT_Mod.java1
-rw-r--r--src/main/java/gregtech/api/enums/GT_Values.java4
-rw-r--r--src/main/java/gregtech/api/objects/GT_UO_Dimension.java7
-rw-r--r--src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java435
-rw-r--r--src/main/java/gregtech/api/util/GT_Utility.java79
-rw-r--r--src/main/java/gregtech/common/GT_Pollution.java341
-rw-r--r--src/main/java/gregtech/common/GT_Proxy.java75
-rw-r--r--src/main/java/gregtech/common/GT_UndergroundOil.java267
-rw-r--r--src/main/java/gregtech/common/misc/GT_Command.java2
9 files changed, 931 insertions, 280 deletions
diff --git a/src/main/java/gregtech/GT_Mod.java b/src/main/java/gregtech/GT_Mod.java
index 864ef31c8c..44ee615f41 100644
--- a/src/main/java/gregtech/GT_Mod.java
+++ b/src/main/java/gregtech/GT_Mod.java
@@ -286,6 +286,7 @@ public class GT_Mod implements IGT_Mod {
GT_Values.debugBlockMiner = tMainConfig.get(aTextGeneral, "debugBlockMiner", false).getBoolean(false);
GT_Values.debugBlockPump = tMainConfig.get(aTextGeneral, "debugBlockPump", false).getBoolean(false);
GT_Values.debugEntityCramming = tMainConfig.get(aTextGeneral, "debugEntityCramming", false).getBoolean(false);
+ GT_Values.debugWorldData = tMainConfig.get(aTextGeneral, "debugWorldData", false).getBoolean(false);
GT_Values.oreveinPercentage = tMainConfig.get(aTextGeneral, "oreveinPercentage_100", 100).getInt(100);
GT_Values.oreveinAttempts = tMainConfig.get(aTextGeneral, "oreveinAttempts_64", 64).getInt(64);
GT_Values.oreveinMaxPlacementAttempts = tMainConfig.get(aTextGeneral, "oreveinMaxPlacementAttempts_8", 8).getInt(8);
diff --git a/src/main/java/gregtech/api/enums/GT_Values.java b/src/main/java/gregtech/api/enums/GT_Values.java
index f30360ca58..6505c34423 100644
--- a/src/main/java/gregtech/api/enums/GT_Values.java
+++ b/src/main/java/gregtech/api/enums/GT_Values.java
@@ -272,6 +272,10 @@ public class GT_Values {
*/
public static boolean debugEntityCramming = false;
/**
+ * Debug parameter for {@link gregtech.api.util.GT_ChunkAssociatedData}
+ */
+ public static boolean debugWorldData = false;
+ /**
* Number of ticks between sending sound packets to clients for electric machines. Default is 1.5 seconds. Trying to mitigate lag and FPS drops.
*/
public static int ticksBetweenSounds = 30;
diff --git a/src/main/java/gregtech/api/objects/GT_UO_Dimension.java b/src/main/java/gregtech/api/objects/GT_UO_Dimension.java
index 1c090eb9b5..0d05e6d229 100644
--- a/src/main/java/gregtech/api/objects/GT_UO_Dimension.java
+++ b/src/main/java/gregtech/api/objects/GT_UO_Dimension.java
@@ -39,4 +39,11 @@ public class GT_UO_Dimension {
return null;
}
+ public String getUOFluidKey(GT_UO_Fluid uoFluid) {
+ return fFluids.inverse().get(uoFluid);
+ }
+
+ public GT_UO_Fluid getUOFluid(String key) {
+ return fFluids.get(key);
+ }
}
diff --git a/src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java b/src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java
new file mode 100644
index 0000000000..b28cb429b4
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java
@@ -0,0 +1,435 @@
+package gregtech.api.util;
+
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.world.WorldEvent;
+import org.apache.commons.io.FileUtils;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
+import java.nio.file.AtomicMoveNotSupportedException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * A utility to save all kinds of data that is a function of any chunk.
+ * <p>
+ * GregTech takes care of saving and loading the data from disk, and an efficient mechanism to locate it.
+ * Subclass only need to define the exact scheme of each element data (by overriding the three protected abstract method)
+ * <p>
+ * Oh, there is no limit on how large your data is, though you'd not have the familiar NBT interface, but DataOutput
+ * should be reasonably common anyway.
+ * <p>
+ * It should be noted this class is NOT thread safe.
+ * <p>
+ * Element cannot be null.
+ * <p>
+ * TODO: Implement automatic region unloading.
+ *
+ * @param <T> data element type
+ * @author glease
+ */
+@ParametersAreNonnullByDefault
+public abstract class GT_ChunkAssociatedData<T extends GT_ChunkAssociatedData.IData> {
+ private static final Map<String, GT_ChunkAssociatedData<?>> instances = new ConcurrentHashMap<>();
+ private static final int IO_PARALLELISM = Math.min(8, Math.max(1, Runtime.getRuntime().availableProcessors()) * 2 / 3);
+ private static final ExecutorService IO_WORKERS = Executors.newWorkStealingPool(IO_PARALLELISM);
+ private static final Pattern FILE_PATTERN = Pattern.compile("(.+)\\.(-?\\d+)\\.(-?\\d+)\\.dat");
+
+ static {
+ // register event handler
+ new EventHandler();
+ }
+
+ protected final String mId;
+ protected final Class<T> elementtype;
+ private final int regionLength;
+ private final int version;
+ private final boolean saveDefaults;
+ /**
+ * Data is stored as a `(world id -> (super region id -> super region data))` hash map.
+ * where super region's size is determined by regionSize.
+ * Here it is called super region, to not confuse with vanilla's regions.
+ */
+ private final Map<Integer, Map<ChunkCoordIntPair, SuperRegion>> masterMap = new ConcurrentHashMap<>();
+
+ /**
+ * Initialize this instance.
+ *
+ * @param aId An arbitrary, but globally unique identifier for what this data is
+ * @param elementType The class of this element type. Used to create arrays.
+ * @param regionLength The length of one super region. Each super region will contain {@code regionLength * regionLength} chunks
+ * @param version An integer marking the version of this data. Useful later when the data's serialized form changed.
+ */
+ protected GT_ChunkAssociatedData(String aId, Class<T> elementType, int regionLength, byte version, boolean saveDefaults) {
+ if (regionLength * regionLength > Short.MAX_VALUE || regionLength <= 0)
+ throw new IllegalArgumentException("Region invalid: " + regionLength);
+ if (!IData.class.isAssignableFrom(elementType))
+ throw new IllegalArgumentException("Data type invalid");
+ if (aId.contains("."))
+ throw new IllegalArgumentException("ID cannot contains dot");
+ this.mId = aId;
+ this.elementtype = elementType;
+ this.regionLength = regionLength;
+ this.version = version;
+ this.saveDefaults = saveDefaults;
+ if (instances.putIfAbsent(aId, this) != null)
+ throw new IllegalArgumentException("Duplicate GT_ChunkAssociatedData: " + aId);
+ }
+
+ private ChunkCoordIntPair getRegionID(int aChunkX, int aChunkZ) {
+ return new ChunkCoordIntPair(aChunkX / regionLength, aChunkZ / regionLength);
+ }
+
+ /**
+ * Get a reference to data of the chunk that tile entity is in.
+ * The returned reference should be mutable.
+ */
+ public final T get(IGregTechTileEntity tileEntity) {
+ return get(tileEntity.getWorld(), tileEntity.getXCoord() >> 4, tileEntity.getZCoord() >> 4);
+ }
+
+ public final T get(Chunk chunk) {
+ return get(chunk.worldObj, chunk.xPosition, chunk.zPosition);
+ }
+
+ public final T get(World world, ChunkCoordIntPair coord) {
+ return get(world, coord.chunkXPos, coord.chunkZPos);
+ }
+
+ public final T get(World world, int chunkX, int chunkZ) {
+ SuperRegion region = masterMap.computeIfAbsent(world.provider.dimensionId, ignored -> new ConcurrentHashMap<>())
+ .computeIfAbsent(getRegionID(chunkX, chunkZ), c -> new SuperRegion(world, c));
+ return region.get(chunkX % regionLength, chunkZ % regionLength);
+ }
+
+ protected final void set(World world, int chunkX, int chunkZ, T data) {
+ SuperRegion region = masterMap.computeIfAbsent(world.provider.dimensionId, ignored -> new ConcurrentHashMap<>())
+ .computeIfAbsent(getRegionID(chunkX, chunkZ), c -> new SuperRegion(world, c));
+ region.set(chunkX % regionLength, chunkZ % regionLength, data);
+ }
+
+ protected final boolean isCreated(int dimId, int chunkX, int chunkZ) {
+ Map<ChunkCoordIntPair, SuperRegion> dimData = masterMap.getOrDefault(dimId, null);
+ if (dimData == null) return false;
+
+ SuperRegion region = dimData.getOrDefault(getRegionID(chunkX, chunkZ), null);
+ if (region == null) return false;
+
+ return region.isCreated(chunkX % regionLength, chunkZ % regionLength);
+ }
+
+ public void clear() {
+ if (GT_Values.debugWorldData) {
+ long dirtyRegionCount = masterMap.values().stream()
+ .flatMap(m -> m.values().stream())
+ .filter(SuperRegion::isDirty)
+ .count();
+ if (dirtyRegionCount > 0)
+ GT_Log.out.println("Clearing ChunkAssociatedData with " + dirtyRegionCount + " regions dirty. Data might have been lost!");
+ }
+ masterMap.clear();
+ }
+
+ public void save() {
+ saveRegions(masterMap.values().stream().flatMap(m -> m.values().stream()));
+ }
+
+ public void save(World world) {
+ saveRegions(masterMap.get(world.provider.dimensionId).values().stream());
+ }
+
+ private void saveRegions(Stream<SuperRegion> stream) {
+ stream.filter(r -> !r.isDirty())
+ .map(c -> (Runnable) c::save)
+ .map(r -> CompletableFuture.runAsync(r, IO_WORKERS))
+ .reduce(CompletableFuture::allOf)
+ .ifPresent(f -> {
+ try {
+ f.get();
+ } catch (Exception e) {
+ GT_Log.err.println("Data save error: " + mId);
+ e.printStackTrace(GT_Log.err);
+ }
+ });
+ }
+
+ protected abstract void writeElement(DataOutput output, T element, World world, int chunkX, int chunkZ) throws IOException;
+
+ protected abstract T readElement(DataInput input, int version, World world, int chunkX, int chunkZ) throws IOException;
+
+ protected abstract T createElement(World world, int chunkX, int chunkZ);
+
+ /**
+ * Clear all mappings, regardless of whether they are dirty
+ */
+ public static void clearAll() {
+ for (GT_ChunkAssociatedData<?> d : instances.values()) d.clear();
+ }
+
+ /**
+ * Save all mappings
+ */
+ public static void saveAll() {
+ for (GT_ChunkAssociatedData<?> d : instances.values()) d.save();
+ }
+
+ /**
+ * Load data for all chunks for a given world.
+ * Current data for that world will be discarded. If this is what you intended, call {@link #save(World)} beforehand.
+ * <p>
+ * Be aware of the memory consumption though.
+ */
+ protected void loadAll(World w) {
+ if (GT_Values.debugWorldData && masterMap.containsKey(w.provider.dimensionId))
+ GT_Log.err.println("Reloading ChunkAssociatedData " + mId + " for world " + w.provider.dimensionId + " discards old data!");
+ if (!getSaveDirectory(w).isDirectory())
+ // nothing to load...
+ return;
+ try {
+ Map<ChunkCoordIntPair, SuperRegion> worldData = Files.list(getSaveDirectory(w).toPath())
+ .map(f -> {
+ Matcher matcher = FILE_PATTERN.matcher(f.getFileName().toString());
+ return matcher.matches() ? matcher : null;
+ })
+ .filter(Objects::nonNull)
+ .filter(m -> mId.equals(m.group(1)))
+ .map(m -> CompletableFuture.supplyAsync(() -> new SuperRegion(w, Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3))), IO_WORKERS))
+ .map(f -> {
+ try {
+ return f.get();
+ } catch (Exception e) {
+ GT_Log.err.println("Error loading region");
+ e.printStackTrace(GT_Log.err);
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toMap(SuperRegion::getCoord, Function.identity()));
+ masterMap.put(w.provider.dimensionId, worldData);
+ } catch (IOException e) {
+ GT_Log.err.println("Error loading all region");
+ e.printStackTrace(GT_Log.err);
+ }
+ }
+
+ protected File getSaveDirectory(World w) {
+ return new File(w.getSaveHandler().getWorldDirectory(), "gregtech");
+ }
+
+ public interface IData {
+ /**
+ * @return Whether the data is different from chunk default
+ */
+ boolean isSameAsDefault();
+ }
+
+ protected final class SuperRegion {
+ private final T[] data = createData();
+ private final File backingStorage;
+ private final WeakReference<World> world;
+ private final ChunkCoordIntPair coord;
+
+ private SuperRegion(World world, int chunkX, int chunkZ) {
+ this.world = new WeakReference<>(world);
+ this.coord = new ChunkCoordIntPair(chunkX, chunkZ);
+ backingStorage = new File(getSaveDirectory(world), String.format("%s.%d.%d.dat", mId, chunkX, chunkZ));
+ if (backingStorage.isFile())
+ load();
+ }
+
+ private SuperRegion(World world, ChunkCoordIntPair coord) {
+ this.world = new WeakReference<>(world);
+ this.coord = coord;
+ backingStorage = new File(getSaveDirectory(world), String.format("%s.%d.%d.dat", mId, coord.chunkXPos, coord.chunkZPos));
+ if (backingStorage.isFile())
+ load();
+ }
+
+ @SuppressWarnings("unchecked")
+ private T[] createData() {
+ return (T[]) Array.newInstance(elementtype, regionLength * regionLength);
+ }
+
+ public T get(int subRegionX, int subRegionZ) {
+ int index = getIndex(subRegionX, subRegionZ);
+ T datum = data[index];
+ if (datum == null) {
+ World world = Objects.requireNonNull(this.world.get());
+ T newElem = createElement(world, coord.chunkXPos * regionLength + subRegionX, coord.chunkZPos * regionLength + subRegionZ);
+ data[index] = newElem;
+ return newElem;
+ }
+ return datum;
+ }
+
+ public void set(int subRegionX, int subRegionZ, T data) {
+ this.data[getIndex(subRegionX, subRegionZ)] = data;
+ }
+
+ public boolean isCreated(int subRegionX, int subRegionZ) {
+ return this.data[getIndex(subRegionX, subRegionZ)] == null;
+ }
+
+ public ChunkCoordIntPair getCoord() {
+ return coord;
+ }
+
+ private int getIndex(int subRegionX, int subRegionY) {
+ return subRegionX * regionLength + subRegionY;
+ }
+
+ private int getChunkX(int index) {
+ return index / regionLength + coord.chunkXPos;
+ }
+
+ private int getChunkZ(int index) {
+ return index % regionLength + coord.chunkZPos;
+ }
+
+ public boolean isDirty() {
+ for (T datum : data) {
+ if (datum != null && datum.isSameAsDefault())
+ return true;
+ }
+ return false;
+ }
+
+ public void save() {
+ try {
+ save0();
+ } catch (IOException e) {
+ GT_Log.err.println("Error saving data " + backingStorage.getPath());
+ e.printStackTrace(GT_Log.err);
+ }
+ }
+
+ private void save0() throws IOException {
+ if (!isDirty())
+ return;
+ //noinspection ResultOfMethodCallIgnored
+ backingStorage.getParentFile().mkdirs();
+ File tmpFile = getTmpFile();
+ World world = Objects.requireNonNull(this.world.get(), "Attempting to save region of another world!");
+ try (DataOutputStream output = new DataOutputStream(new FileOutputStream(tmpFile))) {
+ int ptr = 0;
+ boolean nullRange = data[0] == null;
+ // write a magic byte as storage format version
+ output.writeByte(0);
+ // write a magic byte as data format version
+ output.writeByte(version);
+ output.writeBoolean(nullRange);
+ while (ptr < data.length) {
+ // work out how long is this range
+ int rangeStart = ptr;
+ while (ptr < data.length && (data[ptr] == null || (!saveDefaults && data[ptr].isSameAsDefault())) == nullRange)
+ ptr++;
+ // write range length
+ output.writeShort(ptr - rangeStart);
+ if (!nullRange)
+ // write element data
+ for (int i = rangeStart; i < ptr; i++)
+ writeElement(output, data[i], world, getChunkX(ptr), getChunkZ(ptr));
+ // or not
+ nullRange = !nullRange;
+ }
+ }
+ // first try to replace the destination file
+ // since atomic operation, no need to keep the backup in place
+ try {
+ Files.move(tmpFile.toPath(), backingStorage.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ } catch (AtomicMoveNotSupportedException ignored) {
+ // in case some dumb system/jre combination would cause this
+ // or if **somehow** two file inside the same directory belongs two separate filesystem
+ FileUtils.copyFile(tmpFile, backingStorage);
+ }
+ }
+
+ public void load() {
+ try {
+ loadFromFile(backingStorage);
+ } catch (IOException | RuntimeException e) {
+ GT_Log.err.println("Primary storage file broken in " + backingStorage.getPath());
+ e.printStackTrace(GT_Log.err);
+ // in case the primary storage is broken
+ try {
+ loadFromFile(getTmpFile());
+ } catch (IOException | RuntimeException e2) {
+ GT_Log.err.println("Backup storage file broken in " + backingStorage.getPath());
+ e2.printStackTrace(GT_Log.err);
+ }
+ }
+ }
+
+ private void loadFromFile(File file) throws IOException {
+ World world = Objects.requireNonNull(this.world.get(), "Attempting to load region of another world!");
+ try (DataInputStream input = new DataInputStream(new FileInputStream(file))) {
+ boolean nullRange = input.readBoolean();
+ int ptr = 0;
+ while (ptr != data.length) {
+ int rangeEnd = ptr + input.readUnsignedShort();
+ if (!nullRange) {
+ for (; ptr < rangeEnd; ptr++) {
+ data[ptr] = readElement(input, version, world, getChunkX(ptr), getChunkZ(ptr));
+ }
+ } else {
+ Arrays.fill(data, ptr, rangeEnd, null);
+ ptr = rangeEnd;
+ }
+ }
+ }
+ }
+
+ private File getTmpFile() {
+ return new File(backingStorage.getParentFile(), backingStorage.getName() + ".tmp");
+ }
+ }
+
+ public static class EventHandler {
+ private EventHandler() {
+ MinecraftForge.EVENT_BUS.register(this);
+ }
+
+ @SubscribeEvent
+ public void onWorldSave(WorldEvent.Save e) {
+ for (GT_ChunkAssociatedData<?> d : instances.values()) {
+ d.save(e.world);
+ }
+ }
+
+ @SubscribeEvent
+ public void onWorldUnload(WorldEvent.Unload e) {
+ for (GT_ChunkAssociatedData<?> d : instances.values()) {
+ // there is no need to explicitly do a save here
+ // forge will send a WorldEvent.Save on server thread before this event is distributed
+ d.masterMap.remove(e.world.provider.dimensionId);
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java
index 16d22be9ef..2ae09b6adf 100644
--- a/src/main/java/gregtech/api/util/GT_Utility.java
+++ b/src/main/java/gregtech/api/util/GT_Utility.java
@@ -12,13 +12,24 @@ import gregtech.api.GregTech_API;
import gregtech.api.damagesources.GT_DamageSources;
import gregtech.api.damagesources.GT_DamageSources.DamageSourceHotItem;
import gregtech.api.enchants.Enchantment_Radioactivity;
-import gregtech.api.enums.*;
+import gregtech.api.enums.GT_Values;
+import gregtech.api.enums.ItemList;
+import gregtech.api.enums.Materials;
+import gregtech.api.enums.OrePrefixes;
+import gregtech.api.enums.SubTag;
+import gregtech.api.enums.Textures;
+import gregtech.api.enums.ToolDictNames;
import gregtech.api.events.BlockScanningEvent;
import gregtech.api.interfaces.IBlockContainer;
import gregtech.api.interfaces.IDebugableBlock;
import gregtech.api.interfaces.IProjectileItem;
import gregtech.api.interfaces.ITexture;
-import gregtech.api.interfaces.tileentity.*;
+import gregtech.api.interfaces.tileentity.IBasicEnergyContainer;
+import gregtech.api.interfaces.tileentity.ICoverable;
+import gregtech.api.interfaces.tileentity.IGregTechDeviceInformation;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.interfaces.tileentity.IMachineProgress;
+import gregtech.api.interfaces.tileentity.IUpgradableMachine;
import gregtech.api.items.GT_EnergyArmor_Item;
import gregtech.api.items.GT_Generic_Item;
import gregtech.api.items.GT_MetaGenerated_Tool;
@@ -28,7 +39,7 @@ import gregtech.api.objects.GT_ItemStack;
import gregtech.api.objects.ItemData;
import gregtech.api.threads.GT_Runnable_Sound;
import gregtech.api.util.extensions.ArrayExt;
-import gregtech.common.GT_Proxy;
+import gregtech.common.GT_Pollution;
import gregtech.common.blocks.GT_Block_Ores_Abstract;
import ic2.api.recipe.IRecipeInput;
import ic2.api.recipe.RecipeInputItemStack;
@@ -37,7 +48,11 @@ import ic2.api.recipe.RecipeOutput;
import net.minecraft.block.Block;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
-import net.minecraft.entity.*;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityList;
+import net.minecraft.entity.EntityLiving;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.EnumCreatureAttribute;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
@@ -59,9 +74,14 @@ import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
-import net.minecraft.util.*;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.ChatComponentText;
+import net.minecraft.util.DamageSource;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
+import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot;
@@ -70,8 +90,13 @@ import net.minecraftforge.common.util.FakePlayerFactory;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.world.BlockEvent;
-import net.minecraftforge.fluids.*;
+import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry.FluidContainerData;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
import net.minecraftforge.oredict.OreDictionary;
import javax.annotation.Nullable;
@@ -79,16 +104,34 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.NumberFormat;
-import java.text.DecimalFormatSymbols;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import static gregtech.GT_Mod.GT_FML_LOGGER;
-import static gregtech.api.enums.GT_Values.*;
-import static gregtech.common.GT_Proxy.GTPOLLUTION;
+import static gregtech.api.enums.GT_Values.D1;
+import static gregtech.api.enums.GT_Values.DW;
+import static gregtech.api.enums.GT_Values.E;
+import static gregtech.api.enums.GT_Values.GT;
+import static gregtech.api.enums.GT_Values.L;
+import static gregtech.api.enums.GT_Values.M;
+import static gregtech.api.enums.GT_Values.NW;
+import static gregtech.api.enums.GT_Values.V;
+import static gregtech.api.enums.GT_Values.W;
import static gregtech.common.GT_UndergroundOil.undergroundOilReadInformation;
/**
@@ -2186,23 +2229,19 @@ public class GT_Utility {
}
}
+ Chunk currentChunk = aWorld.getChunkFromBlockCoords(aX, aZ);
if (aPlayer.capabilities.isCreativeMode) {
- FluidStack tFluid = undergroundOilReadInformation(aWorld.getChunkFromBlockCoords(aX,aZ));//-# to only read
+ FluidStack tFluid = undergroundOilReadInformation(currentChunk);//-# to only read
if (tFluid!=null)
tList.add(EnumChatFormatting.GOLD + tFluid.getLocalizedName() + EnumChatFormatting.RESET + ": " + EnumChatFormatting.YELLOW + formatNumbers(tFluid.amount) + EnumChatFormatting.RESET + " L");
else
tList.add(EnumChatFormatting.GOLD + trans("201","Nothing") + EnumChatFormatting.RESET + ": " + EnumChatFormatting.YELLOW + '0' + EnumChatFormatting.RESET + " L");
}
// if(aPlayer.capabilities.isCreativeMode){
- int[] chunkData = GT_Proxy.dimensionWiseChunkData.get(aWorld.provider.dimensionId).get(aWorld.getChunkFromBlockCoords(aX,aZ).getChunkCoordIntPair());
- if(chunkData != null){
- if(chunkData[GTPOLLUTION]>0){
- tList.add(trans("202","Pollution in Chunk: ") + EnumChatFormatting.RED + formatNumbers(chunkData[GTPOLLUTION]) + EnumChatFormatting.RESET + trans("203"," gibbl"));
- }else{
- tList.add(EnumChatFormatting.GREEN+trans("204","No Pollution in Chunk! HAYO!")+EnumChatFormatting.RESET);
- }
- }else{
- tList.add(EnumChatFormatting.GREEN+trans("204","No Pollution in Chunk! HAYO!")+EnumChatFormatting.RESET);
+ if (GT_Pollution.hasPollution(currentChunk)) {
+ tList.add(trans("202", "Pollution in Chunk: ") + EnumChatFormatting.RED + formatNumbers(GT_Pollution.getPollution(currentChunk)) + EnumChatFormatting.RESET + trans("203", " gibbl"));
+ } else {
+ tList.add(EnumChatFormatting.GREEN + trans("204", "No Pollution in Chunk! HAYO!") + EnumChatFormatting.RESET);
}
try {
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,