diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 3b1ce7495f..f11c8adf40 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -7,6 +7,7 @@ package dan200.computercraft; import com.google.common.base.CaseFormat; +import com.google.common.base.Converter; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.media.IMedia; @@ -18,6 +19,7 @@ import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.event.TurtleAction; import dan200.computercraft.core.apis.AddressPredicate; import dan200.computercraft.core.filesystem.ComboMount; import dan200.computercraft.core.filesystem.FileMount; @@ -81,10 +83,7 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -133,6 +132,7 @@ public class ComputerCraft public static int advancedTurtleFuelLimit = 100000; public static boolean turtlesObeyBlockProtection = true; public static boolean turtlesCanPush = true; + public static EnumSet turtleDisabledActions = EnumSet.noneOf( TurtleAction.class ); public static final int terminalWidth_computer = 51; public static final int terminalHeight_computer = 19; @@ -212,6 +212,7 @@ public static class Config { public static Property advancedTurtleFuelLimit; public static Property turtlesObeyBlockProtection; public static Property turtlesCanPush; + public static Property turtleDisabledActions; public static Property modem_range; public static Property modem_highAltitudeRange; @@ -341,6 +342,9 @@ public void preInit( FMLPreInitializationEvent event ) Config.turtlesCanPush = Config.config.get( Configuration.CATEGORY_GENERAL, "turtlesCanPush", turtlesCanPush ); Config.turtlesCanPush.setComment( "If set to true, Turtles will push entities out of the way instead of stopping if there is space to do so" ); + Config.turtleDisabledActions = Config.config.get( Configuration.CATEGORY_GENERAL, "turtle_disabled_actions", new String[ 0 ] ); + Config.turtleDisabledActions.setComment( "A list of turtle actions which are disabled." ); + Config.maxNotesPerTick = Config.config.get( Configuration.CATEGORY_GENERAL, "maxNotesPerTick", maxNotesPerTick ); Config.maxNotesPerTick.setComment( "Maximum amount of notes a speaker can play at once" ); @@ -385,6 +389,20 @@ public static void syncConfig() { turtlesObeyBlockProtection = Config.turtlesObeyBlockProtection.getBoolean(); turtlesCanPush = Config.turtlesCanPush.getBoolean(); + turtleDisabledActions.clear(); + Converter converter = CaseFormat.LOWER_CAMEL.converterTo( CaseFormat.UPPER_UNDERSCORE ); + for( String value : Config.turtleDisabledActions.getStringList() ) + { + try + { + turtleDisabledActions.add( TurtleAction.valueOf( converter.convert( value ) ) ); + } + catch( IllegalArgumentException e ) + { + ComputerCraft.log.error( "Unknown turtle action " + value ); + } + } + maxNotesPerTick = Math.max(1, Config.maxNotesPerTick.getInt()); Config.config.save(); diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java index 83bb94581a..d9741c6b75 100644 --- a/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java +++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java @@ -8,11 +8,15 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.turtle.event.TurtleAttackEvent; +import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import net.minecraft.client.renderer.block.model.IBakedModel; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.event.entity.player.AttackEntityEvent; +import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.lang3.tuple.Pair; @@ -100,6 +104,9 @@ public interface ITurtleUpgrade * Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called * by the turtle, and the tool is required to do some work. * + * Conforming implementations should fire {@link BlockEvent.BreakEvent} and {@link TurtleBlockEvent.Dig}for digging, + * {@link AttackEntityEvent} and {@link TurtleAttackEvent} for attacking. + * * @param turtle Access to the turtle that the tool resides on. * @param side Which side of the turtle (left or right) the tool resides on. * @param verb Which action (dig or attack) the turtle is being called on to perform. diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleAction.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleAction.java new file mode 100644 index 0000000000..d7fd7fd4e1 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleAction.java @@ -0,0 +1,84 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.turtle.event; + +/** + * A basic action that a turtle may perform, as accessed by the {@code turtle} API. + * + * @see TurtleActionEvent + */ +public enum TurtleAction +{ + /** + * A turtle moves to a new position. + * + * @see TurtleBlockEvent.Move + */ + MOVE, + + /** + * A turtle turns in a specific direction. + */ + TURN, + + /** + * A turtle attempts to dig a block. + * + * @see TurtleBlockEvent.Dig + */ + DIG, + + /** + * A turtle attempts to place a block or item in the world. + * + * @see TurtleBlockEvent.Place + */ + PLACE, + + /** + * A turtle attempts to attack an entity. + * + * @see TurtleActionEvent + */ + ATTACK, + + /** + * Drop an item into an inventory/the world. + * + * @see TurtleInventoryEvent.Drop + */ + DROP, + + /** + * Suck an item from an inventory or the world. + * + * @see TurtleInventoryEvent.Suck + */ + SUCK, + + /** + * Refuel the turtle's fuel levels. + */ + REFUEL, + + /** + * Equip or unequip an item. + */ + EQUIP, + + /** + * Inspect a block in world + * + * @see TurtleBlockEvent.Inspect + */ + INSPECT, + + /** + * Gather metdata about an item in the turtle's inventory. + */ + INSPECT_ITEM, +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleActionEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleActionEvent.java new file mode 100644 index 0000000000..cfd0f0fe11 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleActionEvent.java @@ -0,0 +1,76 @@ +package dan200.computercraft.api.turtle.event; + +import com.google.common.base.Preconditions; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleCommandResult; +import net.minecraftforge.fml.common.eventhandler.Cancelable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * An event fired when a turtle is performing a known action. + */ +@Cancelable +public class TurtleActionEvent extends TurtleEvent +{ + private final TurtleAction action; + private String failureMessage; + + public TurtleActionEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action ) + { + super( turtle ); + + Preconditions.checkNotNull( action, "action cannot be null" ); + this.action = action; + } + + public TurtleAction getAction() + { + return action; + } + + /** + * Sets the cancellation state of this action. + * + * If {@code cancel} is {@code true}, this action will not be carried out. + * + * @param cancel The new canceled value. + * @see TurtleCommandResult#failure() + * @deprecated Use {@link #setCanceled(boolean, String)} instead. + */ + @Override + @Deprecated + public void setCanceled( boolean cancel ) + { + setCanceled( cancel, null ); + } + + /** + * Set the cancellation state of this action, setting a failure message if required. + * + * If {@code cancel} is {@code true}, this action will not be carried out. + * + * @param cancel The new canceled value. + * @param failureMessage The message to return to the user explaining the failure. + * @see TurtleCommandResult#failure(String) + */ + public void setCanceled( boolean cancel, @Nullable String failureMessage ) + { + super.setCanceled( cancel ); + this.failureMessage = cancel ? failureMessage : null; + } + + /** + * Get the message with which this will fail. + * + * @return The failure message. + * @see TurtleCommandResult#failure() + * @see #setCanceled(boolean, String) + */ + @Nullable + public String getFailureMessage() + { + return failureMessage; + } +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleAttackEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleAttackEvent.java new file mode 100644 index 0000000000..7b83473928 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleAttackEvent.java @@ -0,0 +1,80 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.turtle.event; + +import com.google.common.base.Preconditions; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.api.turtle.TurtleVerb; +import net.minecraft.entity.Entity; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.entity.player.AttackEntityEvent; + +import javax.annotation.Nonnull; + +/** + * Fired when a turtle attempts to attack an entity. + * + * This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)}, + * as the base {@code turtle.attack()} command does not fire it. + * + * Note that such commands should also fire {@link AttackEntityEvent}, so you do not need to listen to both. + * + * @see TurtleAction#ATTACK + */ +public class TurtleAttackEvent extends TurtlePlayerEvent +{ + private final Entity target; + private final ITurtleUpgrade upgrade; + private final TurtleSide side; + + public TurtleAttackEvent( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side ) + { + super( turtle, TurtleAction.ATTACK, player ); + Preconditions.checkNotNull( target, "target cannot be null" ); + Preconditions.checkNotNull( upgrade, "upgrade cannot be null" ); + Preconditions.checkNotNull( side, "side cannot be null" ); + this.target = target; + this.upgrade = upgrade; + this.side = side; + } + + /** + * Get the entity being attacked by this turtle. + * + * @return The entity being attacked. + */ + @Nonnull + public Entity getTarget() + { + return target; + } + + /** + * Get the upgrade responsible for attacking. + * + * @return The upgrade responsible for attacking. + */ + @Nonnull + public ITurtleUpgrade getUpgrade() + { + return upgrade; + } + + /** + * Get the side the attacking upgrade is on. + * + * @return The upgrade's side. + */ + @Nonnull + public TurtleSide getSide() + { + return side; + } +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleBlockEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleBlockEvent.java new file mode 100644 index 0000000000..cc037287d5 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleBlockEvent.java @@ -0,0 +1,241 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.turtle.event; + +import com.google.common.base.Preconditions; +import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.api.turtle.TurtleVerb; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.fml.common.eventhandler.Cancelable; + +import javax.annotation.Nonnull; +import java.util.Map; + +/** + * A general event for when a turtle interacts with a block or region. + * + * You should generally listen to one of the sub-events instead, cancelling them where + * appropriate. + * + * Note that you are not guaranteed to receive this event, if it has been cancelled by other + * mechanisms, such as block protection systems. + * + * Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact + * with a block, simply objects within that block space. + */ +@Cancelable +public abstract class TurtleBlockEvent extends TurtlePlayerEvent +{ + private final World world; + private final BlockPos pos; + + protected TurtleBlockEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos ) + { + super( turtle, action, player ); + + Preconditions.checkNotNull( world, "world cannot be null" ); + Preconditions.checkNotNull( pos, "pos cannot be null" ); + this.world = world; + this.pos = pos; + } + + /** + * Get the world the turtle is interacting in. + * + * @return The world the turtle is interacting in. + */ + public World getWorld() + { + return world; + } + + /** + * Get the position the turtle is interacting with. Note that this is different + * to {@link ITurtleAccess#getPosition()}. + * + * @return The position the turtle is interacting with. + */ + public BlockPos getPos() + { + return pos; + } + + /** + * Fired when a turtle attempts to dig a block. + * + * This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)}, + * as the base {@code turtle.dig()} command does not fire it. + * + * Note that such commands should also fire {@link BlockEvent.BreakEvent}, so you do not need to listen to both. + * + * @see TurtleAction#DIG + */ + @Cancelable + public static class Dig extends TurtleBlockEvent + { + private final IBlockState block; + private final ITurtleUpgrade upgrade; + private final TurtleSide side; + + public Dig( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState block, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side ) + { + super( turtle, TurtleAction.DIG, player, world, pos ); + + Preconditions.checkNotNull( block, "block cannot be null" ); + Preconditions.checkNotNull( upgrade, "upgrade cannot be null" ); + Preconditions.checkNotNull( side, "side cannot be null" ); + this.block = block; + this.upgrade = upgrade; + this.side = side; + } + + /** + * Get the block which is about to be broken. + * + * @return The block which is going to be broken. + */ + @Nonnull + public IBlockState getBlock() + { + return block; + } + + /** + * Get the upgrade doing the digging + * + * @return The upgrade doing the digging. + */ + @Nonnull + public ITurtleUpgrade getUpgrade() + { + return upgrade; + } + + /** + * Get the side the upgrade doing the digging is on. + * + * @return The upgrade's side. + */ + @Nonnull + public TurtleSide getSide() + { + return side; + } + } + + /** + * Fired when a turtle attempts to move into a block. + * + * @see TurtleAction#MOVE + */ + @Cancelable + public static class Move extends TurtleBlockEvent + { + public Move( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos ) + { + super( turtle, TurtleAction.MOVE, player, world, pos ); + } + } + + /** + * Fired when a turtle attempts to place a block in the world. + * + * @see TurtleAction#PLACE + */ + @Cancelable + public static class Place extends TurtleBlockEvent + { + private final ItemStack stack; + + public Place( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack ) + { + super( turtle, TurtleAction.PLACE, player, world, pos ); + + Preconditions.checkNotNull( stack, "stack cannot be null" ); + this.stack = stack; + } + + /** + * Get the item stack that will be placed. This should not be modified. + * + * @return The item stack to be placed. + */ + @Nonnull + public ItemStack getStack() + { + return stack; + } + } + + /** + * Fired when a turtle gathers data on a block in world. + * + * You may prevent blocks being inspected, or add additional information to the result. + * + * @see TurtleAction#INSPECT + */ + @Cancelable + public static class Inspect extends TurtleBlockEvent + { + private final IBlockState state; + private final Map data; + + public Inspect( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Map data ) + { + super( turtle, TurtleAction.INSPECT, player, world, pos ); + + Preconditions.checkNotNull( state, "state cannot be null" ); + Preconditions.checkNotNull( data, "data cannot be null" ); + this.data = data; + this.state = state; + } + + /** + * Get the block state which is being inspected. + * + * @return The inspected block state. + */ + @Nonnull + public IBlockState getState() + { + return state; + } + + /** + * Get the "inspection data" from this block, which will be returned to the user. + * + * @return This block's inspection data. + */ + @Nonnull + public Map getData() + { + return data; + } + + /** + * Add new information to the inspection result. Note this will override fields with the same name. + * + * @param newData The data to add. Note all values should be convertable to Lua (see + * {@link dan200.computercraft.api.peripheral.IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}). + */ + public void addData( @Nonnull Map newData ) + { + Preconditions.checkNotNull( newData, "newData cannot be null" ); + data.putAll( newData ); + } + } +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java new file mode 100644 index 0000000000..b6d27d6832 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java @@ -0,0 +1,43 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.turtle.event; + +import com.google.common.base.Preconditions; +import dan200.computercraft.api.turtle.ITurtleAccess; +import net.minecraftforge.fml.common.eventhandler.Event; + +import javax.annotation.Nonnull; + +/** + * A base class for all events concerning a turtle. This will only ever constructed and fired on the server side, + * so sever specific methods on {@link ITurtleAccess} are safe to use. + * + * You should generally not need to subscribe to this event, preferring one of the more specific classes. + * + * @see TurtleActionEvent + */ +public abstract class TurtleEvent extends Event +{ + private final ITurtleAccess turtle; + + protected TurtleEvent( @Nonnull ITurtleAccess turtle ) + { + Preconditions.checkNotNull( turtle, "turtle cannot be null" ); + this.turtle = turtle; + } + + /** + * Get the turtle which is performing this action. + * + * @return The access for this turtle. + */ + @Nonnull + public ITurtleAccess getTurtle() + { + return turtle; + } +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleInventoryEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleInventoryEvent.java new file mode 100644 index 0000000000..e52faf8e98 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleInventoryEvent.java @@ -0,0 +1,84 @@ +package dan200.computercraft.api.turtle.event; + +import com.google.common.base.Preconditions; +import dan200.computercraft.api.turtle.ITurtleAccess; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.fml.common.eventhandler.Cancelable; +import net.minecraftforge.items.IItemHandler; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Fired when a turtle attempts to interact with an inventory. + */ +@Cancelable +public abstract class TurtleInventoryEvent extends TurtleBlockEvent +{ + private final IItemHandler handler; + + protected TurtleInventoryEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler ) + { + super( turtle, action, player, world, pos ); + this.handler = handler; + } + + /** + * Get the inventory being interacted with + * + * @return The inventory being interacted with, {@code null} if the item will be dropped to/sucked from the world. + */ + @Nullable + public IItemHandler getItemHandler() + { + return handler; + } + + /** + * Fired when a turtle attempts to suck from an inventory. + * + * @see TurtleAction#SUCK + */ + @Cancelable + public static class Suck extends TurtleInventoryEvent + { + public Suck( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler ) + { + super( turtle, TurtleAction.SUCK, player, world, pos, handler ); + } + } + + /** + * Fired when a turtle attempts to drop an item into an inventory. + * + * @see TurtleAction#DROP + */ + @Cancelable + public static class Drop extends TurtleInventoryEvent + { + private final ItemStack stack; + + public Drop( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler, @Nonnull ItemStack stack ) + { + super( turtle, TurtleAction.DROP, player, world, pos, handler ); + + Preconditions.checkNotNull( stack, "stack cannot be null" ); + this.stack = stack; + } + + /** + * The item which will be inserted into the inventory/dropped on the ground. + * + * Note that this is a copy of the original stack, and so should not be modified, as that will have no effect. + * + * @return The item stack which will be dropped. + */ + public ItemStack getStack() + { + return stack.copy(); + } + } +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtlePlayerEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtlePlayerEvent.java new file mode 100644 index 0000000000..46179f3de7 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtlePlayerEvent.java @@ -0,0 +1,44 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.turtle.event; + +import com.google.common.base.Preconditions; +import dan200.computercraft.api.turtle.ITurtleAccess; +import net.minecraftforge.common.util.FakePlayer; + +import javax.annotation.Nonnull; + +/** + * An action done by a turtle which is normally done by a player. + * + * {@link #getPlayer()} may be used to modify the player's attributes or perform permission checks. + */ +public abstract class TurtlePlayerEvent extends TurtleActionEvent +{ + private final FakePlayer player; + + protected TurtlePlayerEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player ) + { + super( turtle, action ); + + Preconditions.checkNotNull( player, "player cannot be null" ); + this.player = player; + } + + /** + * A fake player, representing this turtle. + * + * This may be used for triggering permission checks. + * + * @return A {@link FakePlayer} representing this turtle. + */ + @Nonnull + public FakePlayer getPlayer() + { + return player; + } +} diff --git a/src/main/java/dan200/computercraft/api/turtle/event/package-info.java b/src/main/java/dan200/computercraft/api/turtle/event/package-info.java new file mode 100644 index 0000000000..a1459ab317 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/event/package-info.java @@ -0,0 +1,10 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +@API(owner = "ComputerCraft", provides = "ComputerCraft|API|Turtle|Event", apiVersion = "${version}") +package dan200.computercraft.api.turtle.event; + +import net.minecraftforge.fml.common.API; diff --git a/src/main/java/dan200/computercraft/shared/proxy/CCTurtleProxyCommon.java b/src/main/java/dan200/computercraft/shared/proxy/CCTurtleProxyCommon.java index 53e80563a5..a5e20eab01 100644 --- a/src/main/java/dan200/computercraft/shared/proxy/CCTurtleProxyCommon.java +++ b/src/main/java/dan200/computercraft/shared/proxy/CCTurtleProxyCommon.java @@ -8,6 +8,7 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.event.TurtleActionEvent; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.turtle.blocks.BlockTurtle; import dan200.computercraft.shared.turtle.blocks.TileTurtle; @@ -465,6 +466,14 @@ public void onEntityLivingDrops( LivingDropsEvent event ) { dispatchEntityDrops( event.getEntity(), event.getDrops() ); } + + @SubscribeEvent + public void onTurtleAction( TurtleActionEvent event) { + if( ComputerCraft.turtleDisabledActions.contains( event.getAction() ) ) + { + event.setCanceled( true, "Action has been disabled" ); + } + } } private void dispatchEntityDrops( Entity entity, java.util.List drops ) diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java index 1d5b6083f1..dcee1f488b 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java +++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java @@ -11,11 +11,14 @@ import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.api.turtle.event.TurtleAction; +import dan200.computercraft.api.turtle.event.TurtleActionEvent; import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.ILuaAPI; import dan200.computercraft.shared.turtle.core.*; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; import javax.annotation.Nonnull; import java.util.HashMap; @@ -439,6 +442,13 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O table.put( "name", name ); table.put( "damage", damage ); table.put( "count", count ); + + TurtleActionEvent event = new TurtleActionEvent( m_turtle, TurtleAction.INSPECT_ITEM ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + return new Object[] { false, event.getFailureMessage() }; + } + return new Object[] { table }; } else diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java index e2f259e312..d89d2d05ca 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java @@ -10,12 +10,14 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleInventoryEvent; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.items.IItemHandler; import javax.annotation.Nonnull; @@ -59,6 +61,16 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) EnumFacing side = direction.getOpposite(); IItemHandler inventory = InventoryUtil.getInventory( world, newPosition, side ); + + // Fire the event, restoring the inventory and exiting if it is cancelled. + TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction ); + TurtleInventoryEvent.Drop event = new TurtleInventoryEvent.Drop( turtle, player, world, newPosition, inventory, stack ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() ); + return TurtleCommandResult.failure( event.getFailureMessage() ); + } + if( inventory != null ) { // Drop the item into the inventory diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java index ed4ac9f762..c257eb83b5 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java @@ -8,11 +8,14 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.turtle.*; +import dan200.computercraft.api.turtle.event.TurtleAction; +import dan200.computercraft.api.turtle.event.TurtleActionEvent; import dan200.computercraft.shared.proxy.CCTurtleProxyCommon; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.items.IItemHandler; import javax.annotation.Nonnull; @@ -63,6 +66,12 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) oldUpgradeStack = null; } + TurtleActionEvent event = new TurtleActionEvent( turtle, TurtleAction.EQUIP ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + return TurtleCommandResult.failure( event.getFailureMessage() ); + } + // Do the swapping: if( newUpgradeStack != null ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java index b947da7c25..2c62abca7b 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java @@ -10,13 +10,15 @@ import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.block.Block; import net.minecraft.block.properties.IProperty; import net.minecraft.block.state.IBlockState; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; import javax.annotation.Nonnull; import java.util.HashMap; @@ -46,14 +48,14 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) if( WorldUtil.isBlockInWorld( world, newPosition ) ) { - if( !FAIL_ON_AIR || !world.isAirBlock( newPosition ) ) + IBlockState state = world.getBlockState( newPosition ); + if( !FAIL_ON_AIR || !state.getBlock().isAir( state, world, newPosition ) ) { - IBlockState state = world.getBlockState( newPosition ); Block block = state.getBlock(); String name = Block.REGISTRY.getNameForObject( block ).toString(); int metadata = block.getMetaFromState( state ); - Map table = new HashMap<>(); + Map table = new HashMap<>(); table.put( "name", name ); table.put( "metadata", metadata ); @@ -73,7 +75,15 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) } table.put( "state", stateTable ); - return TurtleCommandResult.success( new Object[]{ table } ); + // Fire the event, exiting if it is cancelled + TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction ); + TurtleBlockEvent.Inspect event = new TurtleBlockEvent.Inspect( turtle, turtlePlayer, world, newPosition, state, table ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + return TurtleCommandResult.failure( event.getFailureMessage() ); + } + + return TurtleCommandResult.success( new Object[] { table } ); } } @@ -83,7 +93,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) table.put( "name", "minecraft:air" ); table.put( "metadata", 0 ); table.put( "state", new HashMap<>() ); - return TurtleCommandResult.success( new Object[]{ table } ); + return TurtleCommandResult.success( new Object[] { table } ); } return TurtleCommandResult.failure( "No block to inspect" ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java index cadc8b73ec..99072163a1 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java @@ -11,13 +11,15 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.block.Block; import net.minecraft.entity.Entity; +import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.EnumFacing; import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; import javax.annotation.Nonnull; import java.util.List; @@ -103,6 +105,12 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) } } + TurtleBlockEvent.Move moveEvent = new TurtleBlockEvent.Move( turtle, turtlePlayer, oldWorld, newPosition ); + if( MinecraftForge.EVENT_BUS.post( moveEvent ) ) + { + return TurtleCommandResult.failure( moveEvent.getFailureMessage() ); + } + // Check fuel level if( turtle.isFuelNeeded() && turtle.getFuelLevel() < 1 ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java index 26b1a47801..7ceb9b5b8b 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java @@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; @@ -31,6 +32,7 @@ import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.fml.common.eventhandler.Event; import org.apache.commons.lang3.tuple.Pair; @@ -64,6 +66,16 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) World world = turtle.getWorld(); BlockPos coordinates = WorldUtil.moveCoords( turtle.getPosition(), direction ); + // Create a fake player, and orient it appropriately + BlockPos playerPosition = WorldUtil.moveCoords( turtle.getPosition(), direction ); + TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction ); + + TurtleBlockEvent.Place place = new TurtleBlockEvent.Place( turtle, turtlePlayer, turtle.getWorld(), coordinates, stack ); + if( MinecraftForge.EVENT_BUS.post( place ) ) + { + return TurtleCommandResult.failure( place.getFailureMessage() ); + } + IBlockState previousState; if( WorldUtil.isBlockInWorld( world, coordinates ) ) { @@ -75,8 +87,8 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) } // Do the deploying - String[] errorMessage = new String[1]; - ItemStack remainder = deploy( stack, turtle, direction, m_extraArguments, errorMessage ); + String[] errorMessage = new String[ 1 ]; + ItemStack remainder = deploy( stack, turtle, turtlePlayer, direction, m_extraArguments, errorMessage ); if( remainder != stack ) { // Put the remaining items back @@ -117,6 +129,11 @@ public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, BlockPos playerPosition = WorldUtil.moveCoords( turtle.getPosition(), direction ); TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction ); + return deploy( stack, turtle, turtlePlayer, direction, extraArguments, o_errorMessage ); + } + + public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, EnumFacing direction, Object[] extraArguments, String[] o_errorMessage ) + { // Deploy on an entity ItemStack remainder = deployOnEntity( stack, turtle, turtlePlayer, direction, extraArguments, o_errorMessage ); if( remainder != stack ) diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java index 0e4fd84ebd..d69144a039 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java @@ -10,9 +10,12 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleAction; +import dan200.computercraft.api.turtle.event.TurtleActionEvent; import dan200.computercraft.shared.util.InventoryUtil; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntityFurnace; +import net.minecraftforge.common.MinecraftForge; import javax.annotation.Nonnull; @@ -71,6 +74,12 @@ private TurtleCommandResult refuel( ITurtleAccess turtle, @Nonnull ItemStack sta return TurtleCommandResult.failure( "Items not combustible" ); } + TurtleActionEvent event = new TurtleActionEvent( turtle, TurtleAction.REFUEL ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + return TurtleCommandResult.failure( event.getFailureMessage() ); + } + if( !testOnly ) { // Determine fuel to give and replacement item to leave behind diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java index a9db1f9b0f..a7ed73df33 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java @@ -10,6 +10,7 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleInventoryEvent; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.entity.Entity; @@ -19,6 +20,7 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.items.IItemHandler; import javax.annotation.Nonnull; @@ -56,6 +58,15 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) EnumFacing side = direction.getOpposite(); IItemHandler inventory = InventoryUtil.getInventory( world, newPosition, side ); + + // Fire the event, exiting if it is cancelled. + TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction ); + TurtleInventoryEvent.Suck event = new TurtleInventoryEvent.Suck( turtle, player, world, newPosition, inventory ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + return TurtleCommandResult.failure( event.getFailureMessage() ); + } + if( inventory != null ) { // Take from inventory of thing in front diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java index 5b253d7d00..3a50d4b0f6 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java @@ -10,7 +10,10 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; +import dan200.computercraft.api.turtle.event.TurtleAction; +import dan200.computercraft.api.turtle.event.TurtleActionEvent; import dan200.computercraft.shared.util.DirectionUtil; +import net.minecraftforge.common.MinecraftForge; import javax.annotation.Nonnull; @@ -27,6 +30,12 @@ public TurtleTurnCommand( TurnDirection direction ) @Override public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) { + TurtleActionEvent event = new TurtleActionEvent( turtle, TurtleAction.TURN ); + if( MinecraftForge.EVENT_BUS.post( event ) ) + { + return TurtleCommandResult.failure( event.getFailureMessage() ); + } + switch( m_direction ) { case Left: diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java index 4f8738f54a..cb7fdba4cf 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java @@ -9,6 +9,8 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.turtle.*; +import dan200.computercraft.api.turtle.event.TurtleAttackEvent; +import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand; import dan200.computercraft.shared.turtle.core.TurtlePlayer; @@ -128,11 +130,11 @@ public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull Turt { case Attack: { - return attack( turtle, direction ); + return attack( turtle, direction, side ); } case Dig: { - return dig( turtle, direction ); + return dig( turtle, direction, side ); } default: { @@ -161,7 +163,7 @@ protected float getDamageMultiplier() return 3.0f; } - private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction ) + private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction, TurtleSide side ) { // Create a fake player, and orient it appropriately final World world = turtle.getWorld(); @@ -178,8 +180,21 @@ private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direc ItemStack stackCopy = m_item.copy(); turtlePlayer.loadInventory( stackCopy ); - // Start claiming entity drops Entity hitEntity = hit.getKey(); + + // Fire several events to ensure we have permissions. + if( MinecraftForge.EVENT_BUS.post( new AttackEntityEvent( turtlePlayer, hitEntity ) ) || !hitEntity.canBeAttackedWithItem() ) + { + return TurtleCommandResult.failure( "Nothing to attack here" ); + } + + TurtleAttackEvent attackEvent = new TurtleAttackEvent( turtle, turtlePlayer, hitEntity, this, side ); + if( MinecraftForge.EVENT_BUS.post( attackEvent ) ) + { + return TurtleCommandResult.failure( attackEvent.getFailureMessage() ); + } + + // Start claiming entity drops ComputerCraft.setEntityDropConsumer( hitEntity, ( entity, drop ) -> { ItemStack remainder = InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() ); @@ -191,8 +206,7 @@ private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direc // Attack the entity boolean attacked = false; - if( hitEntity.canBeAttackedWithItem() && !hitEntity.hitByEntity( turtlePlayer ) - && !MinecraftForge.EVENT_BUS.post( new AttackEntityEvent( turtlePlayer, hitEntity ) ) ) + if( !hitEntity.hitByEntity( turtlePlayer ) ) { float damage = (float)turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue(); damage *= getDamageMultiplier(); @@ -233,7 +247,7 @@ private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direc return TurtleCommandResult.failure( "Nothing to attack here" ); } - private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction ) + private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction, TurtleSide side ) { // Get ready to dig World world = turtle.getWorld(); @@ -266,6 +280,13 @@ private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction ) return TurtleCommandResult.failure( "Unbreakable block detected" ); } + // Fire the dig event, checking whether it was cancelled. + TurtleBlockEvent.Dig digEvent = new TurtleBlockEvent.Dig( turtle, turtlePlayer, world, newPosition, world.getBlockState( newPosition ), this, side ); + if( MinecraftForge.EVENT_BUS.post( digEvent ) ) + { + return TurtleCommandResult.failure( digEvent.getFailureMessage() ); + } + // Consume the items the block drops if( canHarvestBlock( world, newPosition ) ) { diff --git a/src/main/resources/assets/computercraft/lang/en_us.lang b/src/main/resources/assets/computercraft/lang/en_us.lang index edc0b8a0d0..cbc347bea3 100644 --- a/src/main/resources/assets/computercraft/lang/en_us.lang +++ b/src/main/resources/assets/computercraft/lang/en_us.lang @@ -60,5 +60,6 @@ gui.computercraft:config.turtle_fuel_limit=Turtle fuel limit gui.computercraft:config.advanced_turtle_fuel_limit=Advanced Turtle fuel limit gui.computercraft:config.turtles_obey_block_protection=Turtles obey block protection gui.computercraft:config.turtles_can_push=Turtles can push entities +gui.computercraft:config.turtle_disabled_actions=Disabled turtle actions gui.computercraft:config.maximum_files_open=Maximum files open per computer gui.computercraft:config.max_notes_per_tick=Maximum notes that a computer can play at once