From 199ec48f853f55a78124a5ccbcd00f521de2d3aa Mon Sep 17 00:00:00 2001 From: Glease <4586901+Glease@users.noreply.github.com> Date: Tue, 16 Nov 2021 02:00:00 +0800 Subject: underground oil and pollution persistence form rework Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> --- .../gregtech/api/util/GT_ChunkAssociatedData.java | 435 +++++++++++++++++++++ src/main/java/gregtech/api/util/GT_Utility.java | 79 +++- 2 files changed, 494 insertions(+), 20 deletions(-) create mode 100644 src/main/java/gregtech/api/util/GT_ChunkAssociatedData.java (limited to 'src/main/java/gregtech/api/util') 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. + *

+ * 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) + *

+ * 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. + *

+ * It should be noted this class is NOT thread safe. + *

+ * Element cannot be null. + *

+ * TODO: Implement automatic region unloading. + * + * @param data element type + * @author glease + */ +@ParametersAreNonnullByDefault +public abstract class GT_ChunkAssociatedData { + private static final Map> 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 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> 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 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 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 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. + *

+ * 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 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; + 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 { -- cgit From 3f29e8c504ed163b7df9c4fb02a58d51ce2b878c Mon Sep 17 00:00:00 2001 From: Glease <4586901+Glease@users.noreply.github.com> Date: Tue, 16 Nov 2021 05:32:32 +0800 Subject: convert cover data storage to a full nbt tag Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> --- .../java/gregtech/api/util/GT_CoverBehavior.java | 202 +++++++-- .../gregtech/api/util/GT_CoverBehavior_New.java | 480 +++++++++++++++++++++ src/main/java/gregtech/api/util/GT_Utility.java | 2 +- .../gregtech/api/util/ISerializableObject.java | 107 +++++ 4 files changed, 749 insertions(+), 42 deletions(-) create mode 100644 src/main/java/gregtech/api/util/GT_CoverBehavior_New.java create mode 100644 src/main/java/gregtech/api/util/ISerializableObject.java (limited to 'src/main/java/gregtech/api/util') diff --git a/src/main/java/gregtech/api/util/GT_CoverBehavior.java b/src/main/java/gregtech/api/util/GT_CoverBehavior.java index 14c8cc1308..db68a50cd2 100644 --- a/src/main/java/gregtech/api/util/GT_CoverBehavior.java +++ b/src/main/java/gregtech/api/util/GT_CoverBehavior.java @@ -3,21 +3,179 @@ package gregtech.api.util; import gregtech.api.enums.GT_Values; import gregtech.api.interfaces.tileentity.ICoverable; import gregtech.api.net.GT_Packet_TileEntityCoverGUI; -import gregtech.api.objects.GT_ItemStack; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; +import net.minecraft.world.World; import net.minecraftforge.fluids.Fluid; import static gregtech.api.enums.GT_Values.E; /** - * For Covers with a special behavior. + * For Covers with a special behavior. Has fixed storage format of 4 byte. Not very convenient... */ -public abstract class GT_CoverBehavior { +public abstract class GT_CoverBehavior extends GT_CoverBehavior_New { public EntityPlayer lastPlayer = null; + public GT_CoverBehavior() { + super(ISerializableObject.LegacyCoverData.class); + } + + private static int convert(ISerializableObject.LegacyCoverData data) { + return data == null ? 0 : data.get(); + } + + // region bridge the parent call to legacy calls + + @Override + public final ISerializableObject.LegacyCoverData createDataObject() { + return new ISerializableObject.LegacyCoverData(); + } + + @Override + public ISerializableObject.LegacyCoverData createDataObject(int aLegacyData) { + return new ISerializableObject.LegacyCoverData(aLegacyData); + } + + @Override + protected boolean isRedstoneSensitiveImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) { + return isRedstoneSensitive(aSide, aCoverID, aCoverVariable.get(), aTileEntity, aTimer); + } + + @Override + protected ISerializableObject.LegacyCoverData doCoverThingsImpl(byte aSide, byte aInputRedstone, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, long aTimer) { + if (aCoverVariable == null) + aCoverVariable = new ISerializableObject.LegacyCoverData(); + aCoverVariable.set(doCoverThings(aSide, aInputRedstone, aCoverID, aCoverVariable.get(), aTileEntity, aTimer)); + return aCoverVariable; + } + + @Override + protected boolean onCoverRightClickImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return onCoverRightclick(aSide, aCoverID, convert(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ); + } + + @Override + protected ISerializableObject.LegacyCoverData onCoverScrewdriverClickImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + if (aCoverVariable == null) + aCoverVariable = new ISerializableObject.LegacyCoverData(); + aCoverVariable.set(onCoverScrewdriverclick(aSide, aCoverID, convert(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ)); + return aCoverVariable; + } + + @Override + protected boolean onCoverShiftRightClickImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer) { + return onCoverShiftRightclick(aSide, aCoverID, convert(aCoverVariable), aTileEntity, aPlayer); + } + + @Override + protected Object getClientGUIImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, World aWorld) { + return getClientGUI(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean onCoverRemovalImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity, boolean aForced) { + return onCoverRemoval(aSide, aCoverID, convert(aCoverVariable), aTileEntity, aForced); + } + + @Override + protected String getDescriptionImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getDescription(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected float getBlastProofLevelImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getBlastProofLevel(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsRedstoneGoInImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoIn(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsRedstoneGoOutImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoOut(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsFibreGoInImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsFibreGoIn(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsFibreGoOutImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsFibreGoOut(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsEnergyInImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsEnergyIn(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsEnergyOutImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return letsEnergyOut(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean letsFluidInImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return letsFluidIn(aSide, aCoverID, convert(aCoverVariable), aFluid, aTileEntity); + } + + @Override + protected boolean letsFluidOutImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return letsFluidOut(aSide, aCoverID, convert(aCoverVariable), aFluid, aTileEntity); + } + + @Override + protected boolean letsItemsInImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) { + return letsItemsIn(aSide, aCoverID, convert(aCoverVariable), aSlot, aTileEntity); + } + + @Override + protected boolean letsItemsOutImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, int aSlot, ICoverable aTileEntity) { + return letsItemsOut(aSide, aCoverID, convert(aCoverVariable), aSlot, aTileEntity); + } + + @Override + protected boolean isGUIClickableImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return isGUIClickable(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean manipulatesSidedRedstoneOutputImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return manipulatesSidedRedstoneOutput(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected boolean alwaysLookConnectedImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return alwaysLookConnected(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected byte getRedstoneInputImpl(byte aSide, byte aInputRedstone, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getRedstoneInput(aSide, aInputRedstone, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected int getTickRateImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getTickRate(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected byte getLensColorImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getLensColor(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + @Override + protected ItemStack getDropImpl(byte aSide, int aCoverID, ISerializableObject.LegacyCoverData aCoverVariable, ICoverable aTileEntity) { + return getDrop(aSide, aCoverID, convert(aCoverVariable), aTileEntity); + } + + // endregion + public boolean isRedstoneSensitive(byte aSide, int aCoverID, int aCoverVariable, ICoverable aTileEntity, long aTimer) { return true; } @@ -38,15 +196,6 @@ public abstract class GT_CoverBehavior { return false; } - /** - * Called when someone rightclicks this Cover Client Side - *

- * return true, if something actually happens. - */ - public boolean onCoverRightclickClient(byte aSide, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { - return false; - } - /** * Called when someone rightclicks this Cover with a Screwdriver. Doesn't call @onCoverRightclick in this Case. *

@@ -68,21 +217,10 @@ public abstract class GT_CoverBehavior { return false; } - public boolean hasCoverGUI() { - return false; - } - public Object getClientGUI(byte aSide, int aCoverID, int coverData, ICoverable aTileEntity) { return null; } - /** - * Checks if the Cover can be placed on this. - */ - public boolean isCoverPlaceable(byte aSide, GT_ItemStack aStack, ICoverable aTileEntity) { - return true; - } - /** * Removes the Cover if this returns true, or if aForced is true. * Doesn't get called when the Machine Block is getting broken, only if you break the Cover away from the Machine. @@ -218,13 +356,6 @@ public abstract class GT_CoverBehavior { return 0; } - /** - * If this is a simple Cover, which can also be used on Bronze Machines and similar. - */ - public boolean isSimpleCover() { - return false; - } - /** * The MC Color of this Lens. -1 for no Color (meaning this isn't a Lens then). */ @@ -238,15 +369,4 @@ public abstract class GT_CoverBehavior { public ItemStack getDrop(byte aSide, int aCoverID, int aCoverVariable, ICoverable aTileEntity) { return GT_OreDictUnificator.get(true, aTileEntity.getCoverItemAtSide(aSide)); } - - /** - * @return sets the Cover upon placement. - */ - public void placeCover(byte aSide, ItemStack aCover, ICoverable aTileEntity) { - aTileEntity.setCoverIDAtSide(aSide, GT_Utility.stackToInt(aCover)); - } - - public String trans(String aNr, String aEnglish){ - return GT_LanguageManager.addStringLocalization("Interaction_DESCRIPTION_Index_"+aNr, aEnglish, false); - } } diff --git a/src/main/java/gregtech/api/util/GT_CoverBehavior_New.java b/src/main/java/gregtech/api/util/GT_CoverBehavior_New.java new file mode 100644 index 0000000000..4dafcbe931 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_CoverBehavior_New.java @@ -0,0 +1,480 @@ +package gregtech.api.util; + +import gregtech.api.enums.GT_Values; +import gregtech.api.interfaces.tileentity.ICoverable; +import gregtech.api.net.GT_Packet_TileEntityCoverGUI; +import gregtech.api.objects.GT_ItemStack; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; + +import static gregtech.api.enums.GT_Values.E; + +/** + * For Covers with a special behavior. + */ +public abstract class GT_CoverBehavior_New { + + public EntityPlayer lastPlayer = null; + private final Class typeToken; + + protected GT_CoverBehavior_New(Class typeToken) { + this.typeToken = typeToken; + } + + public abstract T createDataObject(int aLegacyData); + + public abstract T createDataObject(); + + public T createDataObject(NBTBase aNBT) { + T ret = createDataObject(); + ret.loadDataFromNBT(aNBT); + return ret; + } + + public final T cast(ISerializableObject aData) { + if (typeToken.isInstance(aData)) + return forceCast(aData); + return null; + } + + private T forceCast(ISerializableObject aData) { + return typeToken.cast(aData); + } + + // region facade + + public final boolean isRedstoneSensitive(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, long aTimer) { + return isRedstoneSensitiveImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aTimer); + } + + /** + * Called by updateEntity inside the covered TileEntity. aCoverVariable is the Value you returned last time. + */ + public final T doCoverThings(byte aSide, byte aInputRedstone, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, long aTimer) { + return doCoverThingsImpl(aSide, aInputRedstone, aCoverID, forceCast(aCoverVariable), aTileEntity, aTimer); + } + + /** + * Called when someone rightclicks this Cover. + *

+ * return true, if something actually happens. + */ + public final boolean onCoverRightClick(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return onCoverRightClickImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ); + } + + /** + * Called when someone rightclicks this Cover with a Screwdriver. Doesn't call @onCoverRightclick in this Case. + *

+ * return the new Value of the Cover Variable + */ + public final T onCoverScrewdriverClick(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return onCoverScrewdriverClickImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer, aX, aY, aZ); + } + + /** + * Called when someone shift-rightclicks this Cover with no tool. Doesn't call @onCoverRightclick in this Case. + */ + public final boolean onCoverShiftRightClick(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer) { + return onCoverShiftRightClickImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer); + } + + public final Object getClientGUI(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, World aWorld) { + return getClientGUIImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aPlayer, aWorld); + } + + /** + * Removes the Cover if this returns true, or if aForced is true. + * Doesn't get called when the Machine Block is getting broken, only if you break the Cover away from the Machine. + */ + public final boolean onCoverRemoval(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, boolean aForced) { + return onCoverRemovalImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aForced); + } + + /** + * Gives a small Text for the status of the Cover. + */ + public final String getDescription(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getDescriptionImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * How Blast Proof the Cover is. 30 is normal. + */ + public final float getBlastProofLevel(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getBlastProofLevelImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets RS-Signals into the Block + *

+ * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + public final boolean letsRedstoneGoIn(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoInImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets RS-Signals out of the Block + */ + public final boolean letsRedstoneGoOut(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoOutImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Fibre-Signals into the Block + *

+ * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + public final boolean letsFibreGoIn(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return letsFibreGoInImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Fibre-Signals out of the Block + */ + public final boolean letsFibreGoOut(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return letsFibreGoOutImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Energy into the Block + */ + public final boolean letsEnergyIn(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return letsEnergyInImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Energy out of the Block + */ + public final boolean letsEnergyOut(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return letsEnergyOutImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * If it lets Liquids into the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + public final boolean letsFluidIn(byte aSide, int aCoverID, ISerializableObject aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return letsFluidInImpl(aSide, aCoverID, forceCast(aCoverVariable), aFluid, aTileEntity); + } + + /** + * If it lets Liquids out of the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + public final boolean letsFluidOut(byte aSide, int aCoverID, ISerializableObject aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return letsFluidOutImpl(aSide, aCoverID, forceCast(aCoverVariable), aFluid, aTileEntity); + } + + /** + * If it lets Items into the Block, aSlot = -1 means if it is generally accepting Items (return false for no eraction at all), aSlot = -2 means if it would accept for all Slots Impl(return true to skip the Checks for each Slot). + */ + public final boolean letsItemsIn(byte aSide, int aCoverID, ISerializableObject aCoverVariable, int aSlot, ICoverable aTileEntity) { + return letsItemsInImpl(aSide, aCoverID, forceCast(aCoverVariable), aSlot, aTileEntity); + } + + /** + * If it lets Items out of the Block, aSlot = -1 means if it is generally accepting Items (return false for no eraction at all), aSlot = -2 means if it would accept for all Slots Impl(return true to skip the Checks for each Slot). + */ + public final boolean letsItemsOut(byte aSide, int aCoverID, ISerializableObject aCoverVariable, int aSlot, ICoverable aTileEntity) { + return letsItemsOutImpl(aSide, aCoverID, forceCast(aCoverVariable), aSlot, aTileEntity); + } + + /** + * If it lets you rightclick the Machine normally + */ + public final boolean isGUIClickable(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return isGUIClickableImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Needs to return true for Covers, which have a Redstone Output on their Facing. + */ + public final boolean manipulatesSidedRedstoneOutput(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return manipulatesSidedRedstoneOutputImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * if this Cover should let Pipe Connections look connected even if it is not the case. + */ + public final boolean alwaysLookConnected(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return alwaysLookConnectedImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Called to determine the incoming Redstone Signal of a Machine. + * Returns the original Redstone per default. + * The Cover should @letsRedstoneGoIn or the aInputRedstone Parameter is always 0. + */ + public final byte getRedstoneInput(byte aSide, byte aInputRedstone, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getRedstoneInputImpl(aSide, aInputRedstone, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * Gets the Tick Rate for doCoverThings of the Cover + *

+ * 0 = No Ticks! Yes, 0 is Default, you have to override this + */ + public final int getTickRate(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getTickRateImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + + /** + * The MC Color of this Lens. -1 for no Color (meaning this isn't a Lens then). + */ + public final byte getLensColor(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getLensColorImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + + /** + * @return the ItemStack dropped by this Cover + */ + public final ItemStack getDrop(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) { + return getDropImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity); + } + // endregion + + // region impl + + protected boolean isRedstoneSensitiveImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, long aTimer) { + return true; + } + + /** + * Called by updateEntity inside the covered TileEntity. aCoverVariable is the Value you returned last time. + */ + protected T doCoverThingsImpl(byte aSide, byte aInputRedstone, int aCoverID, T aCoverVariable, ICoverable aTileEntity, long aTimer) { + return aCoverVariable; + } + + /** + * Called when someone rightclicks this Cover. + *

+ * return true, if something actually happens. + */ + protected boolean onCoverRightClickImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return false; + } + + /** + * Called when someone rightclicks this Cover with a Screwdriver. Doesn't call @onCoverRightclick in this Case. + *

+ * return the new Value of the Cover Variable + */ + protected T onCoverScrewdriverClickImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return aCoverVariable; + } + + /** + * Called when someone shift-rightclicks this Cover with no tool. Doesn't call @onCoverRightclick in this Case. + */ + protected boolean onCoverShiftRightClickImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer) { + if (hasCoverGUI() && aPlayer instanceof EntityPlayerMP) { + lastPlayer = aPlayer; + GT_Values.NW.sendToPlayer(new GT_Packet_TileEntityCoverGUI(aSide, aCoverID, aCoverVariable, aTileEntity, (EntityPlayerMP) aPlayer), (EntityPlayerMP) aPlayer); + return true; + } + return false; + } + + protected Object getClientGUIImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, EntityPlayer aPlayer, World aWorld) { + return null; + } + + /** + * Removes the Cover if this returns true, or if aForced is true. + * Doesn't get called when the Machine Block is getting broken, only if you break the Cover away from the Machine. + */ + protected boolean onCoverRemovalImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, boolean aForced) { + return true; + } + + /** + * Gives a small Text for the status of the Cover. + */ + protected String getDescriptionImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return E; + } + + /** + * How Blast Proof the Cover is. 30 is normal. + */ + protected float getBlastProofLevelImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return 10.0F; + } + + /** + * If it lets RS-Signals into the Block + *

+ * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + protected boolean letsRedstoneGoInImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets RS-Signals out of the Block + */ + protected boolean letsRedstoneGoOutImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Fibre-Signals into the Block + *

+ * This is just Informative so that Machines know if their Redstone Input is blocked or not + */ + protected boolean letsFibreGoInImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Fibre-Signals out of the Block + */ + protected boolean letsFibreGoOutImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Energy into the Block + */ + protected boolean letsEnergyInImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Energy out of the Block + */ + protected boolean letsEnergyOutImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Liquids into the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + protected boolean letsFluidInImpl(byte aSide, int aCoverID, T aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Liquids out of the Block, aFluid can be null meaning if this is generally allowing Fluids or not. + */ + protected boolean letsFluidOutImpl(byte aSide, int aCoverID, T aCoverVariable, Fluid aFluid, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Items into the Block, aSlot = -1 means if it is generally accepting Items (return false for no Interaction at all), aSlot = -2 means if it would accept for all Slots (return true to skip the Checks for each Slot). + */ + protected boolean letsItemsInImpl(byte aSide, int aCoverID, T aCoverVariable, int aSlot, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets Items out of the Block, aSlot = -1 means if it is generally accepting Items (return false for no Interaction at all), aSlot = -2 means if it would accept for all Slots (return true to skip the Checks for each Slot). + */ + protected boolean letsItemsOutImpl(byte aSide, int aCoverID, T aCoverVariable, int aSlot, ICoverable aTileEntity) { + return false; + } + + /** + * If it lets you rightclick the Machine normally + */ + protected boolean isGUIClickableImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * Needs to return true for Covers, which have a Redstone Output on their Facing. + */ + protected boolean manipulatesSidedRedstoneOutputImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * if this Cover should let Pipe Connections look connected even if it is not the case. + */ + protected boolean alwaysLookConnectedImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return false; + } + + /** + * Called to determine the incoming Redstone Signal of a Machine. + * Returns the original Redstone per default. + * The Cover should @letsRedstoneGoIn or the aInputRedstone Parameter is always 0. + */ + protected byte getRedstoneInputImpl(byte aSide, byte aInputRedstone, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return letsRedstoneGoIn(aSide, aCoverID, aCoverVariable, aTileEntity) ? aInputRedstone : 0; + } + + /** + * Gets the Tick Rate for doCoverThings of the Cover + *

+ * 0 = No Ticks! Yes, 0 is Default, you have to override this + */ + protected int getTickRateImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return 0; + } + + + /** + * The MC Color of this Lens. -1 for no Color (meaning this isn't a Lens then). + */ + protected byte getLensColorImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return -1; + } + + /** + * @return the ItemStack dropped by this Cover + */ + protected ItemStack getDropImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) { + return GT_OreDictUnificator.get(true, aTileEntity.getCoverItemAtSide(aSide)); + } + + //endregion + + // region no data + + /** + * Checks if the Cover can be placed on this. + */ + public boolean isCoverPlaceable(byte aSide, GT_ItemStack aStack, ICoverable aTileEntity) { + return true; + } + + public boolean hasCoverGUI() { + return false; + } + + /** + * Called when someone rightclicks this Cover Client Side + *

+ * return true, if something actually happens. + */ + public boolean onCoverRightclickClient(byte aSide, ICoverable aTileEntity, EntityPlayer aPlayer, float aX, float aY, float aZ) { + return false; + } + + /** + * If this is a simple Cover, which can also be used on Bronze Machines and similar. + */ + public boolean isSimpleCover() { + return false; + } + + /** + * sets the Cover upon placement. + */ + public void placeCover(byte aSide, ItemStack aCover, ICoverable aTileEntity) { + aTileEntity.setCoverIDAtSide(aSide, GT_Utility.stackToInt(aCover)); + } + + public String trans(String aNr, String aEnglish) { + return GT_LanguageManager.addStringLocalization("Interaction_DESCRIPTION_Index_" + aNr, aEnglish, false); + } + // endregion +} diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java index 2ae09b6adf..08b10e4fdf 100644 --- a/src/main/java/gregtech/api/util/GT_Utility.java +++ b/src/main/java/gregtech/api/util/GT_Utility.java @@ -2157,7 +2157,7 @@ public class GT_Utility { try { if (tTileEntity instanceof ICoverable) { rEUAmount += 300; - String tString = ((ICoverable) tTileEntity).getCoverBehaviorAtSide((byte) aSide).getDescription((byte) aSide, ((ICoverable) tTileEntity).getCoverIDAtSide((byte) aSide), ((ICoverable) tTileEntity).getCoverDataAtSide((byte) aSide), (ICoverable) tTileEntity); + String tString = ((ICoverable) tTileEntity).getCoverBehaviorAtSideNew((byte) aSide).getDescription((byte) aSide, ((ICoverable) tTileEntity).getCoverIDAtSide((byte) aSide), ((ICoverable) tTileEntity).getCoverDataAtSideNew((byte) aSide), (ICoverable) tTileEntity); if (tString != null && !tString.equals(E)) tList.add(tString); } } catch (Throwable e) { diff --git a/src/main/java/gregtech/api/util/ISerializableObject.java b/src/main/java/gregtech/api/util/ISerializableObject.java new file mode 100644 index 0000000000..1a38aee5f7 --- /dev/null +++ b/src/main/java/gregtech/api/util/ISerializableObject.java @@ -0,0 +1,107 @@ +package gregtech.api.util; + +import com.google.common.io.ByteArrayDataInput; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagInt; + +import javax.annotation.Nonnull; + +/** + * We could well have used {@link java.io.Serializable}, but that's too slow and should generally be avoided + */ +public interface ISerializableObject { + + @Nonnull + ISerializableObject copy(); + + @Nonnull + NBTBase saveDataToNBT(); + + /** + * Write data to given ByteBuf + * The data saved this way is intended to be stored for short amount of time over network. + * DO NOT store it to disks. + */ + // the NBT is an unfortunate piece of tech. everything uses it but its API is not as efficient as could be + void writeToByteBuf(ByteBuf aBuf); + + void loadDataFromNBT(NBTBase aNBT); + + /** + * Read data from given parameter and return this. + * The data read this way is intended to be stored for short amount of time over network. + */ + // the NBT is an unfortunate piece of tech. everything uses it but its API is not as efficient as could be + @Nonnull + ISerializableObject readFromPacket(ByteArrayDataInput aBuf, EntityPlayerMP aPlayer); + + final class LegacyCoverData implements ISerializableObject { + private int mData; + + public LegacyCoverData() { + } + + public LegacyCoverData(int mData) { + this.mData = mData; + } + + @Override + @Nonnull + public ISerializableObject copy() { + return new LegacyCoverData(mData); + } + + @Override + @Nonnull + public NBTBase saveDataToNBT() { + return new NBTTagInt(mData); + } + + @Override + public void writeToByteBuf(ByteBuf aBuf) { + aBuf.writeInt(mData); + } + + @Override + public void loadDataFromNBT(NBTBase aNBT) { + mData = aNBT instanceof NBTBase.NBTPrimitive ? ((NBTBase.NBTPrimitive) aNBT).func_150287_d() : 0; + } + + @Override + @Nonnull + public ISerializableObject readFromPacket(ByteArrayDataInput aBuf, EntityPlayerMP aPlayer) { + mData = aBuf.readInt(); + return this; + } + + public int get() { + return mData; + } + + public void set(int mData) { + this.mData = mData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LegacyCoverData that = (LegacyCoverData) o; + + return mData == that.mData; + } + + @Override + public int hashCode() { + return mData; + } + + @Override + public String toString() { + return String.valueOf(mData); + } + } +} -- cgit From ea9df088740cf44d8f77275b17f519127750bf4f Mon Sep 17 00:00:00 2001 From: Glease <4586901+Glease@users.noreply.github.com> Date: Tue, 16 Nov 2021 06:46:29 +0800 Subject: convert ItemFilter to use NBTTagCompound storage Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> --- .../gregtech/api/util/ISerializableObject.java | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'src/main/java/gregtech/api/util') diff --git a/src/main/java/gregtech/api/util/ISerializableObject.java b/src/main/java/gregtech/api/util/ISerializableObject.java index 1a38aee5f7..4ca5b1e543 100644 --- a/src/main/java/gregtech/api/util/ISerializableObject.java +++ b/src/main/java/gregtech/api/util/ISerializableObject.java @@ -3,10 +3,16 @@ package gregtech.api.util; import com.google.common.io.ByteArrayDataInput; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTSizeTracker; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagInt; import javax.annotation.Nonnull; +import java.io.IOException; /** * We could well have used {@link java.io.Serializable}, but that's too slow and should generally be avoided @@ -37,6 +43,41 @@ public interface ISerializableObject { @Nonnull ISerializableObject readFromPacket(ByteArrayDataInput aBuf, EntityPlayerMP aPlayer); + /** + * Reverse engineered and adapted {@link cpw.mods.fml.common.network.ByteBufUtils#readTag(ByteBuf)} + * Given buffer must contain a serialized NBTTagCompound in minecraft encoding + */ + static NBTTagCompound readCompoundTagFromGreggyByteBuf(ByteArrayDataInput aBuf) { + short size = aBuf.readShort(); + if (size < 0) + return null; + else { + byte[] buf = new byte[size]; // this is shit, how many copies have we been doing? + aBuf.readFully(buf); + try { + return CompressedStreamTools.func_152457_a(buf, new NBTSizeTracker(2097152L)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Reverse engineered and adapted {@link cpw.mods.fml.common.network.ByteBufUtils#readItemStack(ByteBuf)} + * Given buffer must contain a serialized ItemStack in minecraft encoding + */ + static ItemStack readItemStackFromGreggyByteBuf(ByteArrayDataInput aBuf) { + ItemStack stack = null; + short id = aBuf.readShort(); + if (id >= 0) { + byte size = aBuf.readByte(); + short meta = aBuf.readShort(); + stack = new ItemStack(Item.getItemById(id), size, meta); + stack.stackTagCompound = readCompoundTagFromGreggyByteBuf(aBuf); + } + return stack; + } + final class LegacyCoverData implements ISerializableObject { private int mData; -- cgit From 44b9f91081c628b28f8ca6dd8f2ac94b7d52d374 Mon Sep 17 00:00:00 2001 From: Glease <4586901+Glease@users.noreply.github.com> Date: Tue, 16 Nov 2021 07:03:45 +0800 Subject: put my names on it somehow Signed-off-by: Glease <4586901+Glease@users.noreply.github.com> --- src/main/java/gregtech/api/util/GT_CoverBehavior_New.java | 2 ++ src/main/java/gregtech/api/util/ISerializableObject.java | 2 ++ 2 files changed, 4 insertions(+) (limited to 'src/main/java/gregtech/api/util') diff --git a/src/main/java/gregtech/api/util/GT_CoverBehavior_New.java b/src/main/java/gregtech/api/util/GT_CoverBehavior_New.java index 4dafcbe931..3e2c2f856f 100644 --- a/src/main/java/gregtech/api/util/GT_CoverBehavior_