Add a hook for farmland watering (#4891)

This commit is contained in:
ichttt 2018-12-02 02:08:04 +01:00 committed by tterrag
parent 72bc39d831
commit a67cce2f91
11 changed files with 635 additions and 0 deletions

View file

@ -22,3 +22,12 @@
}
private static boolean func_176530_e(IWorldReaderBase p_176530_0_, BlockPos p_176530_1_) {
@@ -102,7 +102,7 @@
}
}
- return false;
+ return net.minecraftforge.common.FarmlandWaterManager.hasBlockWaterTicket(p_176530_0_, p_176530_1_);
}
public IItemProvider func_199769_a(IBlockState p_199769_1_, World p_199769_2_, BlockPos p_199769_3_, int p_199769_4_) {

View file

@ -0,0 +1,145 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common;
import com.google.common.base.Preconditions;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IWorld;
import net.minecraft.world.IWorldReaderBase;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunk;
import net.minecraftforge.common.ticket.AABBTicket;
import net.minecraftforge.common.ticket.ChunkTicketManager;
import net.minecraftforge.common.ticket.MultiTicketManager;
import net.minecraftforge.common.ticket.SimpleTicket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class FarmlandWaterManager
{
private static final Int2ObjectMap<Map<ChunkPos, ChunkTicketManager<Vec3d>>> customWaterHandler = new Int2ObjectOpenHashMap<>();
/**
* Adds a custom ticket.
* Use {@link #addAABBTicket(World, AxisAlignedBB)} if you just need a ticket that can water a certain area.
* <br>
* If you don't want to water the region anymore, call {@link SimpleTicket#invalidate()}. Also call this
* when the region this is unloaded (e.g. your TE is unloaded or the block is removed), and validate once it is loaded
* @param world The world where the region should be marked. Only server-side worlds are allowed
* @param ticket Your ticket you want to have registered
* @param chunkPoses The chunkPoses where the ticket is located
* @return The ticket for your requested region.
*/
@SuppressWarnings("unchecked")
public static<T extends SimpleTicket<Vec3d>> T addCustomTicket(World world, T ticket, ChunkPos... chunkPoses)
{
Preconditions.checkArgument(!world.isRemote, "Water region is only determined server-side");
Preconditions.checkArgument(chunkPoses.length > 0, "Need at least one chunk pos");
Map<ChunkPos, ChunkTicketManager<Vec3d>> ticketMap = customWaterHandler.computeIfAbsent(world.getDimension().getType().getId(), id -> new HashMap<>());
if (chunkPoses.length == 1)
{
ticket.setBackend(ticketMap.computeIfAbsent(chunkPoses[0], ChunkTicketManager::new));
}
else
{
ChunkTicketManager<Vec3d>[] tickets = new ChunkTicketManager[chunkPoses.length];
for (int i = 0; i < chunkPoses.length; i++)
tickets[i] = ticketMap.computeIfAbsent(chunkPoses[i], ChunkTicketManager::new);
ticket.setBackend(new MultiTicketManager<>(tickets));
}
ticket.validate();
return ticket;
}
/**
* Convenience method to add a ticket that is backed by an AABB.
* <br>
* If you don't want to water the region anymore, call {@link SimpleTicket#invalidate()}. Also call this
* when the region this is unloaded (e.g. your TE is unloaded or the block is removed), and validate once it is loaded
* <br>
* The AABB in the ticket is immutable
* @param world The world where the region should be marked. Only server-side worlds are allowed
* @param aabb The region where blocks should be watered
* @return The ticket for your requested region.
*/
public static AABBTicket addAABBTicket(World world, AxisAlignedBB aabb)
{
//First calculate all chunks the aabb is in
ChunkPos leftUp = new ChunkPos(((int) aabb.minX) >> 4, ((int) aabb.minZ) >> 4);
ChunkPos rightDown = new ChunkPos(((int) aabb.maxX) >> 4, ((int) aabb.maxZ) >> 4);
Set<ChunkPos> posSet = new HashSet<>();
for (int x = leftUp.x; x <= rightDown.x; x++)
{
for (int z = leftUp.z; z <= rightDown.z; z++)
{
posSet.add(new ChunkPos(x, z));
}
}
return addCustomTicket(world, new AABBTicket(aabb), posSet.toArray(new ChunkPos[0]));
}
/**
* Tests if a block is in a region that is watered by blocks. This does not check vanilla water, see {@link net.minecraft.block.BlockFarmland#hasWater(World, BlockPos)}
* @return true if there is a ticket with an AABB that includes your block
*/
public static boolean hasBlockWaterTicket(IWorldReaderBase world, BlockPos pos)
{
ChunkTicketManager<Vec3d> ticketManager = getTicketManager(new ChunkPos(pos.getX() >> 4, pos.getZ() >> 4), world);
if (ticketManager != null)
{
Vec3d posAsVec3d = new Vec3d(pos);
for (SimpleTicket<Vec3d> ticket : ticketManager.getTickets()) {
if (ticket.matches(posAsVec3d))
return true;
}
}
return false;
}
static void removeTickets(IChunk chunk)
{
ChunkTicketManager<Vec3d> ticketManager = getTicketManager(chunk.getPos(), chunk.getWorldForge());
if (ticketManager != null)
{
for (SimpleTicket<Vec3d> ticket : ticketManager.getTickets())
{
ticket.invalidate();
}
}
}
private static ChunkTicketManager<Vec3d> getTicketManager(ChunkPos pos, IWorldReaderBase world) {
Preconditions.checkArgument(!world.isRemote(), "Water region is only determined server-side");
Map<ChunkPos, ChunkTicketManager<Vec3d>> ticketMap = customWaterHandler.get(world.getDimension().getType().getId());
if (ticketMap == null)
{
return null;
}
return ticketMap.get(pos);
}
}

View file

@ -27,6 +27,7 @@ import net.minecraft.world.WorldServer;
import net.minecraftforge.client.CloudRenderer;
import net.minecraftforge.common.util.FakePlayerFactory;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@ -80,5 +81,12 @@ public class ForgeInternalHandler
if (event.phase == Phase.END)
CloudRenderer.updateCloudSettings();
}
@SubscribeEvent
public void onChunkUnload(ChunkEvent.Unload event)
{
if (!event.getWorld().isRemote())
FarmlandWaterManager.removeTickets(event.getChunk());
}
}

View file

@ -0,0 +1,42 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.ticket;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.Vec3d;
import javax.annotation.Nonnull;
public class AABBTicket extends SimpleTicket<Vec3d>
{
@Nonnull
public final AxisAlignedBB axisAlignedBB;
public AABBTicket(@Nonnull AxisAlignedBB axisAlignedBB)
{
this.axisAlignedBB = axisAlignedBB;
}
@Override
public boolean matches(Vec3d toMatch)
{
return this.axisAlignedBB.contains(toMatch);
}
}

View file

@ -0,0 +1,56 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.ticket;
import net.minecraft.util.math.ChunkPos;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
public class ChunkTicketManager<T> implements ITicketGetter<T>
{
private final Set<SimpleTicket<T>> tickets = Collections.newSetFromMap(new WeakHashMap<>());
public final ChunkPos pos;
public ChunkTicketManager(ChunkPos pos)
{
this.pos = pos;
}
@Override
public void add(SimpleTicket<T> ticket)
{
this.tickets.add(ticket);
}
@Override
public void remove(SimpleTicket<T> ticket)
{
this.tickets.remove(ticket);
}
@Override
public Collection<SimpleTicket<T>> getTickets()
{
return tickets;
}
}

View file

@ -0,0 +1,27 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.ticket;
import java.util.Collection;
public interface ITicketGetter<T> extends ITicketManager<T>
{
Collection<SimpleTicket<T>> getTickets();
}

View file

@ -0,0 +1,27 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.ticket;
public interface ITicketManager<T>
{
void add(SimpleTicket<T> ticket);
void remove(SimpleTicket<T> ticket);
}

View file

@ -0,0 +1,45 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.ticket;
public class MultiTicketManager<T> implements ITicketManager<T>
{
private final ITicketGetter<T>[] ticketManagers;
@SafeVarargs
public MultiTicketManager(ITicketGetter<T>... ticketManagers)
{
this.ticketManagers = ticketManagers;
}
@Override
public void add(SimpleTicket<T> ticket)
{
for (ITicketGetter<T> manager : ticketManagers)
manager.add(ticket);
}
@Override
public void remove(SimpleTicket<T> ticket)
{
for (ITicketGetter<T> manager : ticketManagers)
manager.remove(ticket);
}
}

View file

@ -0,0 +1,84 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.ticket;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Common class for a simple ticket based system.
* @param <T> The type that will be used to check if your ticket matches
*/
public abstract class SimpleTicket<T>
{
@Nullable
private ITicketManager<T> manager;
protected boolean isValid = false;
/**
* Internal method that sets the collection from the managing system.
* <br>
* Should <b>not</b> be called if you just want to register a ticket to a system like the {@link net.minecraftforge.common.FarmlandWaterManager}
*/
public final void setBackend(@Nonnull ITicketManager<T> ticketManager)
{
Preconditions.checkState(this.manager == null, "Ticket is already registered to a managing system");
this.manager = ticketManager;
}
/**
* Checks if your ticket is still registered in the system.
*/
public boolean isValid()
{
return isValid;
}
/**
* Removes the ticket from the managing system.
* After this call, any calls to {@link #isValid()} should return false unless it is registered again using {@link #validate()}
*/
public void invalidate()
{
Preconditions.checkState(this.manager != null, "Ticket is not registered to a managing system");
if (this.isValid())
{
this.manager.remove(this);
}
this.isValid = false;
}
/**
* Re-adds your ticket to the system.
*/
public void validate()
{
Preconditions.checkState(this.manager != null, "Ticket is not registered to a managing system");
if (!this.isValid())
{
this.manager.add(this);
}
this.isValid = true;
}
public abstract boolean matches(T toMatch);
}

View file

@ -0,0 +1,182 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.debug.block;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.World;
import net.minecraftforge.common.FarmlandWaterManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ticket.AABBTicket;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.GameRegistry;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
@Mod(modid = FarmlandWaterTest.ID, name = "Farmland Water Test", version = "1.0.0", acceptableRemoteVersions = "*")
public class FarmlandWaterTest
{
//This adds a block that creates a 4x4x4 watered region when activated
private static Logger logger;
private static Block testBlock;
static final String ID = "farmlandwatertest";
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event)
{
logger = event.getModLog();
MinecraftForge.EVENT_BUS.register(FarmlandWaterTest.class);
}
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event)
{
testBlock = new TestBlock();
event.getRegistry().register(testBlock.setRegistryName(new ResourceLocation(ID, "test_block")).setCreativeTab(CreativeTabs.MISC).setUnlocalizedName("Farmland Water Test Block"));
GameRegistry.registerTileEntity(TestTileEntity.class, new ResourceLocation(ID, "test_te"));
}
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event)
{
event.getRegistry().register(new ItemBlock(testBlock).setRegistryName(new ResourceLocation(ID, "test_block")));
}
public static class TestBlock extends Block
{
public TestBlock()
{
super(Material.ROCK);
}
@Override
public boolean hasTileEntity(IBlockState state)
{
return true;
}
@Nullable
@Override
public TileEntity createTileEntity(World world, IBlockState state)
{
return new TestTileEntity();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if (world.isRemote)
return true;
TestTileEntity tileEntity = (TestTileEntity) world.getTileEntity(pos);
if (tileEntity == null)
{
return false;
}
tileEntity.isActive = !tileEntity.isActive;
tileEntity.updateTicket();
player.sendStatusMessage(new TextComponentString("Changed block powered state to " + tileEntity.isActive), true);
logger.info("Changed block powered state at {} to {}", pos, tileEntity.isActive);
return true;
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState state)
{
if (world.isRemote)
return;
TestTileEntity tileEntity = (TestTileEntity) world.getTileEntity(pos);
if (tileEntity == null)
return;
tileEntity.farmlandTicket.invalidate();
}
}
public static class TestTileEntity extends TileEntity
{
private AABBTicket farmlandTicket;
private boolean isActive = false;
@Override
public void onLoad()
{
if (!world.isRemote)
{
farmlandTicket = FarmlandWaterManager.addAABBTicket(world, new AxisAlignedBB(pos).grow(4D));
updateTicket();
}
}
private void updateTicket()
{
if (world.isRemote)
return;
if (isActive)
{
farmlandTicket.validate();
}
else
{
farmlandTicket.invalidate();
}
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound)
{
compound = super.writeToNBT(compound);
compound.setBoolean("active", isActive);
return compound;
}
@Override
public void readFromNBT(NBTTagCompound compound)
{
super.readFromNBT(compound);
isActive = compound.getBoolean("active");
}
@Override
public void onChunkUnload()
{
if (!world.isRemote)
{
farmlandTicket.invalidate();
}
}
}
}

View file

@ -0,0 +1,10 @@
{
"forge_marker": 1,
"defaults": {
"model": "minecraft:bedrock"
},
"variants": {
"normal": [{}],
"inventory": [{}]
}
}