aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util
diff options
context:
space:
mode:
authorGlease <4586901+Glease@users.noreply.github.com>2022-01-23 21:28:13 +0800
committerGitHub <noreply@github.com>2022-01-23 14:28:13 +0100
commit0bb36cbb957053d4d91053d152ebddd6f0ed1009 (patch)
tree6d967b5509828981e71b20ff78b9996d8534920b /src/main/java/gregtech/api/util
parentb5b8478b20bfd01bc20ff0a175ea53a4da4983a9 (diff)
downloadGT5-Unofficial-0bb36cbb957053d4d91053d152ebddd6f0ed1009.tar.gz
GT5-Unofficial-0bb36cbb957053d4d91053d152ebddd6f0ed1009.tar.bz2
GT5-Unofficial-0bb36cbb957053d4d91053d152ebddd6f0ed1009.zip
AE2 Cable facade as cover (#887)
* initial work on facade covers * fix colorMultiplier also removed derp * Clean up drop cover texture reset code What was I thinking actually? Send a packet to reset client states? * Fix cover display stack
Diffstat (limited to 'src/main/java/gregtech/api/util')
-rw-r--r--src/main/java/gregtech/api/util/GT_BlockMap.java133
-rw-r--r--src/main/java/gregtech/api/util/GT_BlockSet.java38
-rw-r--r--src/main/java/gregtech/api/util/GT_Config.java13
-rw-r--r--src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java94
-rw-r--r--src/main/java/gregtech/api/util/GT_RenderingWorld.java191
-rw-r--r--src/main/java/gregtech/api/util/ISerializableObject.java4
6 files changed, 472 insertions, 1 deletions
diff --git a/src/main/java/gregtech/api/util/GT_BlockMap.java b/src/main/java/gregtech/api/util/GT_BlockMap.java
new file mode 100644
index 0000000000..cd98aae2bd
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_BlockMap.java
@@ -0,0 +1,133 @@
+package gregtech.api.util;
+
+import gnu.trove.map.TByteObjectMap;
+import gnu.trove.map.hash.TByteObjectHashMap;
+import net.minecraft.block.Block;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+public class GT_BlockMap<V> {
+ public static final byte WILDCARD = -1;
+ private final Map<Block, TByteObjectMap<V>> backing = new HashMap<>();
+ private int size = 0;
+
+ private TByteObjectMap<V> getSubmap(Block block) {
+ return backing.computeIfAbsent(block, b -> new TByteObjectHashMap<>());
+ }
+
+ /**
+ * Associate a value with that union key
+ *
+ * @param block block
+ * @param meta meta
+ * @return old mapping, or null if that doesn't exist
+ */
+ public V put(Block block, byte meta, V value) {
+ V v = getSubmap(block).put(meta, value);
+ if (v == null) size++;
+ return v;
+ }
+
+ /**
+ * Associate a value with that union key ONLY IF there isn't a prior EXACT mapping
+ *
+ * @param block block
+ * @param meta meta
+ * @return old mapping, or null if that doesn't exist
+ */
+ public V putIfAbsent(Block block, byte meta, V value) {
+ V v = getSubmap(block).putIfAbsent(meta, value);
+ if (v == null) size++;
+ return v;
+ }
+
+ /**
+ * Associate a value with that union key ONLY IF there isn't a prior EXACT mapping
+ *
+ * @param block block
+ * @param meta meta
+ * @return old mapping, or null if that doesn't exist
+ */
+ public V computeIfAbsent(Block block, byte meta, BiFunction<Block, Byte, V> function) {
+ TByteObjectMap<V> submap = getSubmap(block);
+ V v = submap.get(meta);
+ if (v == null) {
+ v = function.apply(block, meta);
+ submap.put(meta, v);
+ size++;
+ }
+ return v;
+ }
+
+ /**
+ * Contains an associated value
+ *
+ * @param block block
+ * @param meta meta
+ * @return current mapping OR wildcard of that mapping exists
+ */
+ public boolean containsKey(Block block, byte meta) {
+ TByteObjectMap<V> submap = backing.get(block);
+ if (submap == null) return false;
+ return submap.containsKey(meta) || submap.containsKey(WILDCARD);
+ }
+
+ /**
+ * Get the associated value
+ *
+ * @param block block
+ * @param meta meta
+ * @return current mapping OR wildcard of that block. null if neither exists
+ */
+ public V get(Block block, byte meta) {
+ TByteObjectMap<V> submap = backing.get(block);
+ if (submap == null) return null;
+ V v = submap.get(meta);
+ if (v != null) return v;
+ return submap.get(WILDCARD);
+ }
+
+ /**
+ * Remove a mapping
+ *
+ * @param block block
+ * @param meta meta
+ * @return old value, or null if none
+ */
+ public V remove(Block block, byte meta) {
+ TByteObjectMap<V> submap = backing.get(block);
+ if (submap == null) return null;
+ V v = submap.remove(meta);
+ if (v != null) {
+ size--;
+ if (submap.isEmpty()) backing.remove(block);
+ }
+ return v;
+ }
+
+ /**
+ * Size of all mappings
+ *
+ * @return size
+ */
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ GT_BlockMap<?> that = (GT_BlockMap<?>) o;
+
+ return backing.equals(that.backing);
+ }
+
+ @Override
+ public int hashCode() {
+ return backing.hashCode();
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_BlockSet.java b/src/main/java/gregtech/api/util/GT_BlockSet.java
new file mode 100644
index 0000000000..3c9905a757
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_BlockSet.java
@@ -0,0 +1,38 @@
+package gregtech.api.util;
+
+import net.minecraft.block.Block;
+
+public class GT_BlockSet {
+ private final GT_BlockMap<Object> backing = new GT_BlockMap<>();
+
+ public boolean add(Block block, byte meta) {
+ return backing.put(block, meta, this) != this;
+ }
+
+ public boolean contains(Block block, byte meta) {
+ return backing.get(block, meta) == this;
+ }
+
+ public boolean remove(Block block, byte meta) {
+ return backing.remove(block, meta) == this;
+ }
+
+ public int size() {
+ return backing.size();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ GT_BlockSet that = (GT_BlockSet) o;
+
+ return backing.equals(that.backing);
+ }
+
+ @Override
+ public int hashCode() {
+ return backing.hashCode();
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GT_Config.java b/src/main/java/gregtech/api/util/GT_Config.java
index 6849988a77..f78abc52fb 100644
--- a/src/main/java/gregtech/api/util/GT_Config.java
+++ b/src/main/java/gregtech/api/util/GT_Config.java
@@ -92,6 +92,19 @@ public class GT_Config implements Runnable {
return rResult;
}
+ public String[] get(Object aCategory, ItemStack aStack, String... aDefault) {
+ return get(aCategory, getStackConfigName(aStack), aDefault);
+ }
+
+ public String[] get(Object aCategory, String aName, String... aDefault) {
+ if (GT_Utility.isStringInvalid(aName)) return aDefault;
+ Property tProperty = mConfig.get(aCategory.toString().replaceAll("\\|", "_"), aName.replaceAll("\\|", "_"), aDefault);
+ String[] rResult = tProperty.getStringList();
+ if (!tProperty.wasRead() && GregTech_API.sPostloadFinished) mConfig.save();
+ return rResult;
+ }
+
+
@Override
public void run() {
mConfig.save();
diff --git a/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java b/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java
index a6970ed4bd..6f3c098899 100644
--- a/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java
+++ b/src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java
@@ -1,9 +1,11 @@
package gregtech.api.util;
import gregtech.api.enums.GT_Values;
+import gregtech.api.interfaces.ITexture;
import gregtech.api.interfaces.tileentity.ICoverable;
import gregtech.api.net.GT_Packet_TileEntityCoverGUI;
import gregtech.api.objects.GT_ItemStack;
+import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
@@ -59,6 +61,63 @@ public abstract class GT_CoverBehaviorBase<T extends ISerializableObject> {
// region facade
+ /**
+ * Get target facade block. Does not affect rendering of **this** block. It is only used as a hint for other block
+ * in case of CTM
+ * @return null if none, otherwise return facade target block
+ */
+ public final Block getFacadeBlock(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return getFacadeBlockImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity);
+ }
+
+ /**
+ * Get target facade block. Does not affect rendering of **this** block. It is only used as a hint for other block
+ * in case of CTM
+ * @return 0 if none, otherwise return facade target meta
+ */
+ public final int getFacadeMeta(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return getFacadeMetaImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity);
+ }
+
+ /**
+ * Get the display stack. Default to {@code int2Stack(aCoverID)}
+ */
+ public final ItemStack getDisplayStack(int aCoverID, ISerializableObject aCoverVariable) {
+ return getDisplayStackImpl(aCoverID,forceCast(aCoverVariable));
+ }
+
+ /**
+ * Get the special cover texture associated with this cover. Return null if one should use the texture passed to
+ * {@link gregtech.api.GregTech_API#registerCover(ItemStack, ITexture, GT_CoverBehaviorBase)} or its overloads.
+ */
+ public final ITexture getSpecialCoverTexture(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return getSpecialCoverTextureImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity);
+ }
+
+ /**
+ * Return whether cover data needs to be synced to client upon tile entity creation or cover placement.
+ *
+ * Note if you want to sync the data afterwards you will have to manually do it by calling {@link ICoverable#issueCoverUpdate(byte)}
+ * This option only affects the initial sync.
+ */
+ public final boolean isDataNeededOnClient(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ return isDataNeededOnClientImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity);
+ }
+
+ /**
+ * Called upon receiving data from network. Use {@link ICoverable#isClientSide()} to determine the side.
+ */
+ public final void onDataChanged(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ onDataChangedImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity);
+ }
+
+ /**
+ * Called upon cover being removed. Called on both server and client.
+ */
+ public final void onDropped(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity) {
+ onDroppedImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity);
+ }
+
public final boolean isRedstoneSensitive(byte aSide, int aCoverID, ISerializableObject aCoverVariable, ICoverable aTileEntity, long aTimer) {
return isRedstoneSensitiveImpl(aSide, aCoverID, forceCast(aCoverVariable), aTileEntity, aTimer);
}
@@ -252,6 +311,33 @@ public abstract class GT_CoverBehaviorBase<T extends ISerializableObject> {
// region impl
+ protected Block getFacadeBlockImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {
+ return null;
+ }
+
+ protected int getFacadeMetaImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {
+ return 0;
+ }
+
+ protected ItemStack getDisplayStackImpl(int aCoverID, T aCoverVariable) {
+ return GT_Utility.intToStack(aCoverID);
+ }
+
+ protected ITexture getSpecialCoverTextureImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {
+ return null;
+ }
+
+ protected boolean isDataNeededOnClientImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {
+ return false;
+ }
+
+ protected void onDataChangedImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {
+ }
+
+
+ protected void onDroppedImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity) {
+ }
+
protected boolean isRedstoneSensitiveImpl(byte aSide, int aCoverID, T aCoverVariable, ICoverable aTileEntity, long aTimer) {
return true;
}
@@ -454,6 +540,14 @@ public abstract class GT_CoverBehaviorBase<T extends ISerializableObject> {
/**
* Checks if the Cover can be placed on this.
*/
+ public boolean isCoverPlaceable(byte aSide, ItemStack aStack, ICoverable aTileEntity) {
+ return isCoverPlaceable(aSide, new GT_ItemStack(aStack), aTileEntity);
+ }
+
+ /**
+ * Checks if the Cover can be placed on this. You will probably want to call {@link #isCoverPlaceable(byte, ItemStack, ICoverable)} instead.
+ */
+ @Deprecated
public boolean isCoverPlaceable(byte aSide, GT_ItemStack aStack, ICoverable aTileEntity) {
return true;
}
diff --git a/src/main/java/gregtech/api/util/GT_RenderingWorld.java b/src/main/java/gregtech/api/util/GT_RenderingWorld.java
new file mode 100644
index 0000000000..920ce76357
--- /dev/null
+++ b/src/main/java/gregtech/api/util/GT_RenderingWorld.java
@@ -0,0 +1,191 @@
+package gregtech.api.util;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.eventhandler.EventPriority;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.common.gameevent.TickEvent;
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.ChunkPosition;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.event.world.ChunkEvent;
+import net.minecraftforge.event.world.WorldEvent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provide a fake IBlockAccess to support CTM. Facade are supposed to set these when they are placed/received by client.
+ */
+public class GT_RenderingWorld implements IBlockAccess {
+ private static final GT_RenderingWorld INSTANCE = new GT_RenderingWorld();
+ /*
+ * I do not think this map would ever grow too huge, so I won't go too overcomplicated on this one
+ */
+ private final Map<ChunkPosition, BlockInfo> infos = new HashMap<>();
+ private final Map<ChunkCoordIntPair, Set<ChunkPosition>> index = new HashMap<>();
+ private IBlockAccess mWorld = Minecraft.getMinecraft().theWorld;
+
+ private GT_RenderingWorld() {
+ new FMLEventHandler();
+ new ForgeEventHandler();
+ }
+
+ public static GT_RenderingWorld getInstance() {
+ return INSTANCE;
+ }
+
+ public static GT_RenderingWorld getInstance(IBlockAccess aWorld) {
+ if (aWorld == INSTANCE)
+ return INSTANCE;
+ if (aWorld == null)
+ INSTANCE.mWorld = Minecraft.getMinecraft().theWorld;
+ else
+ INSTANCE.mWorld = aWorld;
+ return INSTANCE;
+ }
+
+ private void setWorld(IBlockAccess aWorld) {
+ if (aWorld == null)
+ mWorld = Minecraft.getMinecraft().theWorld;
+ else
+ mWorld = aWorld;
+ }
+
+ public void register(int x, int y, int z, Block block, int meta) {
+ ChunkPosition key = new ChunkPosition(x, y, z);
+ infos.put(key, new BlockInfo(block, meta));
+ index.computeIfAbsent(new ChunkCoordIntPair(x >> 4, z >> 4), p -> new HashSet<>()).add(key);
+ }
+
+ public void unregister(int x, int y, int z, Block block, int meta) {
+ ChunkPosition key = new ChunkPosition(x, y, z);
+ if (infos.remove(key, new BlockInfo(block, meta))) {
+ ChunkCoordIntPair chunkKey = new ChunkCoordIntPair(x >> 4, z >> 4);
+ Set<ChunkPosition> set = index.get(chunkKey);
+ set.remove(key);
+ if (set.isEmpty())
+ index.remove(chunkKey);
+ }
+ }
+
+ @Override
+ public Block getBlock(int p_147439_1_, int p_147439_2_, int p_147439_3_) {
+ BlockInfo blockInfo = infos.get(new ChunkPosition(p_147439_1_, p_147439_2_, p_147439_3_));
+ return blockInfo != null ? blockInfo.block : mWorld.getBlock(p_147439_1_, p_147439_2_, p_147439_3_);
+ }
+
+ @Override
+ public TileEntity getTileEntity(int p_147438_1_, int p_147438_2_, int p_147438_3_) {
+ return mWorld.getTileEntity(p_147438_1_, p_147438_2_, p_147438_3_);
+ }
+
+ @Override
+ public int getLightBrightnessForSkyBlocks(int p_72802_1_, int p_72802_2_, int p_72802_3_, int p_72802_4_) {
+ return mWorld.getLightBrightnessForSkyBlocks(p_72802_1_, p_72802_2_, p_72802_3_, p_72802_4_);
+ }
+
+ @Override
+ public int getBlockMetadata(int p_72805_1_, int p_72805_2_, int p_72805_3_) {
+ BlockInfo blockInfo = infos.get(new ChunkPosition(p_72805_1_, p_72805_2_, p_72805_3_));
+ return blockInfo != null ? blockInfo.meta : mWorld.getBlockMetadata(p_72805_1_, p_72805_2_, p_72805_3_);
+ }
+
+ @Override
+ public int isBlockProvidingPowerTo(int p_72879_1_, int p_72879_2_, int p_72879_3_, int p_72879_4_) {
+ return mWorld.isBlockProvidingPowerTo(p_72879_1_, p_72879_2_, p_72879_3_, p_72879_4_);
+ }
+
+ @Override
+ public boolean isAirBlock(int p_147437_1_, int p_147437_2_, int p_147437_3_) {
+ return getBlock(p_147437_1_, p_147437_2_, p_147437_3_).isAir(mWorld, p_147437_1_, p_147437_2_, p_147437_3_);
+ }
+
+ @Override
+ public BiomeGenBase getBiomeGenForCoords(int p_72807_1_, int p_72807_2_) {
+ return mWorld.getBiomeGenForCoords(p_72807_1_, p_72807_2_);
+ }
+
+ @Override
+ public int getHeight() {
+ return mWorld.getHeight();
+ }
+
+ @Override
+ public boolean extendedLevelsInChunkCache() {
+ return mWorld.extendedLevelsInChunkCache();
+ }
+
+ @Override
+ public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) {
+ return getBlock(x, y, z).isSideSolid(this, x, y, z, side);
+ }
+
+ public class FMLEventHandler {
+ public FMLEventHandler() {
+ FMLCommonHandler.instance().bus().register(this);
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void onRenderTickStart(TickEvent.RenderTickEvent e) {
+ if (e.phase == TickEvent.Phase.START)
+ mWorld = Minecraft.getMinecraft().theWorld;
+ }
+ }
+
+ public class ForgeEventHandler {
+ private ForgeEventHandler() {
+ MinecraftForge.EVENT_BUS.register(this);
+ }
+
+ @SubscribeEvent
+ public void onChunkUnloaded(ChunkEvent.Unload e) {
+ if (!e.world.isRemote) return;
+ Set<ChunkPosition> set = index.remove(e.getChunk().getChunkCoordIntPair());
+ if (set != null)
+ infos.keySet().removeAll(set);
+ }
+
+ @SubscribeEvent
+ public void onWorldUnloaded(WorldEvent.Unload e) {
+ if (!e.world.isRemote) return;
+ infos.clear();
+ index.clear();
+ }
+ }
+
+ private static class BlockInfo {
+ private final Block block;
+ private final int meta;
+
+ public BlockInfo(Block block, int meta) {
+ this.block = block;
+ this.meta = meta;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BlockInfo blockInfo = (BlockInfo) o;
+
+ if (meta != blockInfo.meta) return false;
+ return block != null ? block.equals(blockInfo.block) : blockInfo.block == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = block != null ? block.hashCode() : 0;
+ result = 31 * result + meta;
+ return result;
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/ISerializableObject.java b/src/main/java/gregtech/api/util/ISerializableObject.java
index b31abaa843..536aea7545 100644
--- a/src/main/java/gregtech/api/util/ISerializableObject.java
+++ b/src/main/java/gregtech/api/util/ISerializableObject.java
@@ -12,6 +12,7 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.io.IOException;
/**
@@ -45,10 +46,11 @@ public interface ISerializableObject {
/**
* 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.
+ * @param aPlayer the player who is sending this packet to server. null if it's client reading data.
*/
// 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);
+ ISerializableObject readFromPacket(ByteArrayDataInput aBuf, @Nullable EntityPlayerMP aPlayer);
/**
* Reverse engineered and adapted {@link cpw.mods.fml.common.network.ByteBufUtils#readTag(ByteBuf)}