diff --git a/patches/minecraft/net/minecraft/world/chunk/Chunk.java.patch b/patches/minecraft/net/minecraft/world/chunk/Chunk.java.patch index 444dee266..e5aaf0634 100644 --- a/patches/minecraft/net/minecraft/world/chunk/Chunk.java.patch +++ b/patches/minecraft/net/minecraft/world/chunk/Chunk.java.patch @@ -1,6 +1,23 @@ --- ../src-base/minecraft/net/minecraft/world/chunk/Chunk.java +++ ../src-work/minecraft/net/minecraft/world/chunk/Chunk.java -@@ -179,7 +179,7 @@ +@@ -41,7 +41,7 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + +-public class Chunk ++public class Chunk implements net.minecraftforge.common.capabilities.ICapabilityProvider + { + private static final Logger field_150817_t = LogManager.getLogger(); + public static final ExtendedBlockStorage field_186036_a = null; +@@ -91,6 +91,7 @@ + + Arrays.fill(this.field_76638_b, -999); + Arrays.fill(this.field_76651_r, (byte) - 1); ++ capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(this); + } + + public Chunk(World p_i45645_1_, ChunkPrimer p_i45645_2_, int p_i45645_3_, int p_i45645_4_) +@@ -179,7 +180,7 @@ { IBlockState iblockstate = this.func_186032_a(j, l - 1, k); @@ -9,7 +26,7 @@ { this.field_76634_f[k << 4 | j] = l; -@@ -452,12 +452,13 @@ +@@ -452,12 +453,13 @@ public int func_177437_b(BlockPos p_177437_1_) { @@ -25,7 +42,7 @@ } public IBlockState func_177435_g(BlockPos p_177435_1_) -@@ -539,6 +540,7 @@ +@@ -539,6 +541,7 @@ { Block block = p_177436_2_.func_177230_c(); Block block1 = iblockstate.func_177230_c(); @@ -33,7 +50,7 @@ ExtendedBlockStorage extendedblockstorage = this.field_76652_q[j >> 4]; boolean flag = false; -@@ -556,14 +558,19 @@ +@@ -556,14 +559,19 @@ extendedblockstorage.func_177484_a(i, j & 15, k, p_177436_2_); @@ -55,7 +72,7 @@ this.field_76637_e.func_175713_t(p_177436_1_); } } -@@ -580,8 +587,7 @@ +@@ -580,8 +588,7 @@ } else { @@ -65,7 +82,7 @@ if (j1 > 0) { -@@ -601,28 +607,19 @@ +@@ -601,28 +608,19 @@ } } @@ -98,7 +115,7 @@ this.field_76637_e.func_175690_a(p_177436_1_, tileentity1); } -@@ -738,6 +735,7 @@ +@@ -738,6 +736,7 @@ k = this.field_76645_j.length - 1; } @@ -106,7 +123,7 @@ p_76612_1_.field_70175_ag = true; p_76612_1_.field_70176_ah = this.field_76635_g; p_76612_1_.field_70162_ai = k; -@@ -778,7 +776,7 @@ +@@ -778,7 +777,7 @@ { IBlockState iblockstate = this.func_177435_g(p_177422_1_); Block block = iblockstate.func_177230_c(); @@ -115,7 +132,7 @@ } @Nullable -@@ -786,6 +784,12 @@ +@@ -786,6 +785,12 @@ { TileEntity tileentity = this.field_150816_i.get(p_177424_1_); @@ -128,7 +145,7 @@ if (tileentity == null) { if (p_177424_2_ == Chunk.EnumCreateEntityType.IMMEDIATE) -@@ -795,14 +799,9 @@ +@@ -795,14 +800,9 @@ } else if (p_177424_2_ == Chunk.EnumCreateEntityType.QUEUED) { @@ -144,7 +161,7 @@ return tileentity; } -@@ -819,10 +818,11 @@ +@@ -819,10 +819,11 @@ public void func_177426_a(BlockPos p_177426_1_, TileEntity p_177426_2_) { @@ -157,7 +174,7 @@ { if (this.field_150816_i.containsKey(p_177426_1_)) { -@@ -854,8 +854,9 @@ +@@ -854,8 +855,9 @@ for (ClassInheritanceMultiMap classinheritancemultimap : this.field_76645_j) { @@ -168,7 +185,7 @@ } public void func_76623_d() -@@ -871,6 +872,7 @@ +@@ -871,6 +873,7 @@ { this.field_76637_e.func_175681_c(classinheritancemultimap); } @@ -176,7 +193,7 @@ } public void func_76630_e() -@@ -880,8 +882,8 @@ +@@ -880,8 +883,8 @@ public void func_177414_a(@Nullable Entity p_177414_1_, AxisAlignedBB p_177414_2_, List p_177414_3_, Predicate p_177414_4_) { @@ -187,7 +204,7 @@ i = MathHelper.func_76125_a(i, 0, this.field_76645_j.length - 1); j = MathHelper.func_76125_a(j, 0, this.field_76645_j.length - 1); -@@ -918,8 +920,8 @@ +@@ -918,8 +921,8 @@ public void func_177430_a(Class p_177430_1_, AxisAlignedBB p_177430_2_, List p_177430_3_, Predicate p_177430_4_) { @@ -198,7 +215,7 @@ i = MathHelper.func_76125_a(i, 0, this.field_76645_j.length - 1); j = MathHelper.func_76125_a(j, 0, this.field_76645_j.length - 1); -@@ -997,6 +999,8 @@ +@@ -997,6 +100,8 @@ protected void func_186034_a(IChunkGenerator p_186034_1_) { @@ -207,7 +224,7 @@ if (this.func_177419_t()) { if (p_186034_1_.func_185933_a(this, this.field_76635_g, this.field_76647_h)) -@@ -1008,8 +1012,10 @@ +@@ -1008,8 +1013,10 @@ { this.func_150809_p(); p_186034_1_.func_185931_b(this.field_76635_g, this.field_76647_h); @@ -218,7 +235,7 @@ } public BlockPos func_177440_h(BlockPos p_177440_1_) -@@ -1064,7 +1070,7 @@ +@@ -1064,7 +1071,7 @@ { BlockPos blockpos = this.field_177447_w.poll(); @@ -227,7 +244,7 @@ { TileEntity tileentity = this.func_177422_i(blockpos); this.field_76637_e.func_175690_a(blockpos, tileentity); -@@ -1128,6 +1134,13 @@ +@@ -1128,6 +1135,13 @@ @SideOnly(Side.CLIENT) public void func_186033_a(PacketBuffer p_186033_1_, int p_186033_2_, boolean p_186033_3_) { @@ -241,7 +258,7 @@ boolean flag = this.field_76637_e.field_73011_w.func_191066_m(); for (int i = 0; i < this.field_76652_q.length; ++i) -@@ -1176,10 +1189,16 @@ +@@ -1176,10 +1190,16 @@ this.field_76646_k = true; this.func_76590_a(); @@ -258,7 +275,7 @@ } public Biome func_177411_a(BlockPos p_177411_1_, BiomeProvider p_177411_2_) -@@ -1244,13 +1263,13 @@ +@@ -1244,13 +1264,13 @@ BlockPos blockpos1 = blockpos.func_177982_a(k, (j << 4) + i1, l); boolean flag = i1 == 0 || i1 == 15 || k == 0 || k == 15 || l == 0 || l == 15; @@ -274,7 +291,7 @@ { this.field_76637_e.func_175664_x(blockpos2); } -@@ -1381,7 +1400,7 @@ +@@ -1381,7 +1401,7 @@ { blockpos$mutableblockpos.func_181079_c(blockpos$mutableblockpos.func_177958_n(), l, blockpos$mutableblockpos.func_177952_p()); @@ -283,7 +300,7 @@ { this.field_76637_e.func_175664_x(blockpos$mutableblockpos); } -@@ -1489,4 +1508,34 @@ +@@ -1489,4 +1509,34 @@ QUEUED, CHECK; } @@ -316,5 +333,23 @@ + net.minecraftforge.fml.common.FMLLog.log.debug(format, "Minecraft", this.field_76635_g, this.field_76647_h, this.field_76637_e.field_73011_w.getDimension()); + else + net.minecraftforge.fml.common.FMLLog.log.warn(format, activeModContainer.getName(), this.field_76635_g, this.field_76647_h, this.field_76637_e.field_73011_w.getDimension()); ++ } ++ ++ private final net.minecraftforge.common.capabilities.CapabilityDispatcher capabilities; ++ @Nullable ++ public net.minecraftforge.common.capabilities.CapabilityDispatcher getCapabilities() ++ { ++ return capabilities; ++ } ++ @Override ++ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable EnumFacing facing) ++ { ++ return capabilities == null ? false : capabilities.hasCapability(capability, facing); ++ } ++ @Override ++ @Nullable ++ public T getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable EnumFacing facing) ++ { ++ return capabilities == null ? null : capabilities.getCapability(capability, facing); + } } diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch index 887a35164..9bce5f4e5 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch +++ b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -132,10 +132,33 @@ } p_75820_3_.func_74782_a("TileEntities", nbttaglist2); -@@ -388,6 +453,12 @@ +@@ -345,6 +410,18 @@ + + p_75820_3_.func_74782_a("TileTicks", nbttaglist3); + } ++ ++ if (p_75820_1_.getCapabilities() != null) ++ { ++ try ++ { ++ p_75820_3_.func_74782_a("ForgeCaps", p_75820_1_.getCapabilities().serializeNBT()); ++ } ++ catch (Exception exception) ++ { ++ net.minecraftforge.fml.common.FMLLog.log.error("A capability provider has thrown an exception trying to write state. It will not persist. Report this to the mod author", exception); ++ } ++ } + } + + private Chunk func_75823_a(World p_75823_1_, NBTTagCompound p_75823_2_) +@@ -388,6 +465,16 @@ chunk.func_76616_a(p_75823_2_.func_74770_j("Biomes")); } ++ if (chunk.getCapabilities() != null && p_75823_2_.func_74764_b("ForgeCaps")) { ++ chunk.getCapabilities().deserializeNBT(p_75823_2_.func_74775_l("ForgeCaps")); ++ } ++ + // End this method here and split off entity loading to another method + return chunk; + } @@ -145,7 +168,7 @@ NBTTagList nbttaglist1 = p_75823_2_.func_150295_c("Entities", 10); for (int j1 = 0; j1 < nbttaglist1.func_74745_c(); ++j1) -@@ -431,8 +502,6 @@ +@@ -431,8 +518,6 @@ p_75823_1_.func_180497_b(new BlockPos(nbttagcompound3.func_74762_e("x"), nbttagcompound3.func_74762_e("y"), nbttagcompound3.func_74762_e("z")), block, nbttagcompound3.func_74762_e("t"), nbttagcompound3.func_74762_e("p")); } } diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index 6ee1fa1b9..89da10828 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -59,6 +59,7 @@ import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraft.world.WorldSettings; import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.ChunkPrimer; import net.minecraft.world.gen.IChunkGenerator; import net.minecraft.world.storage.IPlayerFileData; @@ -597,6 +598,12 @@ public class ForgeEventFactory return gatherCapabilities(new AttachCapabilitiesEvent(World.class, world), parent); } + @Nullable + public static CapabilityDispatcher gatherCapabilities(Chunk chunk) + { + return gatherCapabilities(new AttachCapabilitiesEvent(Chunk.class, chunk), null); + } + @Nullable private static CapabilityDispatcher gatherCapabilities(AttachCapabilitiesEvent event, @Nullable ICapabilityProvider parent) { diff --git a/src/test/java/net/minecraftforge/debug/ChunkCapabilityPollutionTest.java b/src/test/java/net/minecraftforge/debug/ChunkCapabilityPollutionTest.java new file mode 100644 index 000000000..d4cd18217 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/ChunkCapabilityPollutionTest.java @@ -0,0 +1,177 @@ +package net.minecraftforge.debug; + +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Simple mod to test chunk capabilities. + * Use flint and steel to increase pollution in a chunk and saplings to decrease pollution in a chunk. + */ +@Mod(modid = ChunkCapabilityPollutionTest.MODID, name = "Chunk Capability Test", version = "0.0.0") +public class ChunkCapabilityPollutionTest { + public static final String MODID = "chunkcapabilitypollutiontest"; + public static final boolean ENABLE = false; + + public interface IPollution + { + int get(); + + void set(int value, boolean markDirty); + } + + public static class PollutionStorage implements Capability.IStorage + { + @Nullable + @Override + public NBTBase writeNBT(Capability capability, IPollution instance, EnumFacing side) { + return new NBTTagInt(instance.get()); + } + + @Override + public void readNBT(Capability capability, IPollution instance, EnumFacing side, NBTBase nbt) { + if (nbt instanceof NBTTagInt) + { + // The state is being loaded and not updated. We set the value silently to avoid unnecessary dirty chunks + instance.set(((NBTTagInt) nbt).getInt(), false); + } + } + } + + public static class DefaultPollution implements IPollution + { + private int value; + + @Override + public int get() { + return value; + } + + @Override + public void set(int value, boolean markDirty) { + this.value = value; + } + } + + /** + * Marks the chunk as dirty when the value changes. + * Cannot be the default implementation because it requires a chunk in the constructor + */ + public static class SafePollution extends DefaultPollution + { + private final Chunk chunk; + + public SafePollution(Chunk chunk) + { + this.chunk = chunk; + } + + @Override + public void set(int value, boolean markDirty) { + super.set(value, markDirty); + if (markDirty) { + chunk.markDirty(); + } + } + } + + public static class PollutionProvider implements ICapabilitySerializable + { + private final IPollution pollution; + + public PollutionProvider(Chunk chunk) + { + pollution = new SafePollution(chunk); + } + + @Override + public NBTBase serializeNBT() { + return POLLUTION_CAPABILITY.getStorage().writeNBT(POLLUTION_CAPABILITY, pollution, null); + } + + @Override + public void deserializeNBT(NBTBase nbt) { + POLLUTION_CAPABILITY.getStorage().readNBT(POLLUTION_CAPABILITY, pollution, null, nbt); + } + + @Override + public boolean hasCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { + return capability == POLLUTION_CAPABILITY; + } + + @Nullable + @Override + public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { + return capability == POLLUTION_CAPABILITY ? POLLUTION_CAPABILITY.cast(pollution) : null; + } + } + + public static class EventBusHandler + { + @SubscribeEvent + public void onAttachChunkCapabilities(AttachCapabilitiesEvent event) + { + event.addCapability(new ResourceLocation(MODID, "pollution"), new PollutionProvider(event.getObject())); + } + + @SubscribeEvent + public void onUseItem(PlayerInteractEvent.RightClickBlock event) + { + if (!event.getWorld().isRemote) { + ItemStack stack = event.getEntityPlayer().getHeldItem(event.getHand()); + int delta = 0; + if (stack.getItem() == Items.FLINT_AND_STEEL) + { + delta = 1; + } + else if (stack.getItem() == Item.getItemFromBlock(Blocks.SAPLING)) + { + delta = -1; + } + + if (delta != 0) + { + Chunk chunk = event.getWorld().getChunkFromBlockCoords(event.getPos()); + IPollution pollution = chunk.getCapability(POLLUTION_CAPABILITY, null); + pollution.set(pollution.get() + delta, true); + + event.getEntityPlayer().sendStatusMessage(new TextComponentString("Chunk pollution: " + pollution.get()), true); + } + } + } + } + + @CapabilityInject(IPollution.class) + public static final Capability POLLUTION_CAPABILITY = null; + + @Mod.EventHandler + public void init(FMLInitializationEvent event) + { + if (ENABLE) + { + CapabilityManager.INSTANCE.register(IPollution.class, new PollutionStorage(), DefaultPollution.class); + MinecraftForge.EVENT_BUS.register(new EventBusHandler()); + } + } + +}