diff options
author | Glease <4586901+Glease@users.noreply.github.com> | 2022-01-23 21:28:13 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-23 14:28:13 +0100 |
commit | 0bb36cbb957053d4d91053d152ebddd6f0ed1009 (patch) | |
tree | 6d967b5509828981e71b20ff78b9996d8534920b /src/main/java/gregtech/api/util | |
parent | b5b8478b20bfd01bc20ff0a175ea53a4da4983a9 (diff) | |
download | GT5-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.java | 133 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_BlockSet.java | 38 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_Config.java | 13 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_CoverBehaviorBase.java | 94 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/GT_RenderingWorld.java | 191 | ||||
-rw-r--r-- | src/main/java/gregtech/api/util/ISerializableObject.java | 4 |
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)} |