Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Path command extension #96

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,40 +1,242 @@
package org.samo_lego.taterzens.commands.edit;

import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.LiteralCommandNode;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import org.samo_lego.taterzens.Taterzens;
import org.samo_lego.taterzens.commands.NpcCommand;
import org.samo_lego.taterzens.interfaces.ITaterzenEditor;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
import static org.samo_lego.taterzens.Taterzens.config;
import static org.samo_lego.taterzens.util.TextUtil.joinText;
import static org.samo_lego.taterzens.util.TextUtil.successText;
import static org.samo_lego.taterzens.util.TextUtil.translate;
import static org.samo_lego.taterzens.util.TextUtil.*;

public class PathCommand {
public static void registerNode(LiteralCommandNode<CommandSourceStack> editNode) {
LiteralCommandNode<CommandSourceStack> pathNode = literal("path")
.then(literal("clear")
.requires(src -> Taterzens.getInstance().getPlatform().checkPermission(src, "taterzens.npc.edit.path.clear", config.perms.npcCommandPermissionLevel))
.executes(PathCommand::clearTaterzenPath)
.then(literal("clear")
.requires(src -> Taterzens.getInstance().getPlatform().checkPermission(src, "taterzens.npc.edit.path.clear", config.perms.npcCommandPermissionLevel))
.executes(PathCommand::clearTaterzenPath)
)
.then(literal("list")
.requires(src -> Taterzens.getInstance().getPlatform().checkPermission(src, "taterzens.npc.edit.path.list", config.perms.npcCommandPermissionLevel))
.executes(PathCommand::listPathNodes)
)
.requires(src -> Taterzens.getInstance().getPlatform().checkPermission(src, "taterzens.npc.edit.path", config.perms.npcCommandPermissionLevel))
.then(literal("add")
.then(argument("pos", BlockPosArgument.blockPos())
.suggests((context, builder) -> BlockPosArgument.blockPos().listSuggestions(context, builder))
.executes(PathCommand::addPathNode)
)
)
.then(literal("remove")
.then(literal("index")
.then(argument("index", IntegerArgumentType.integer(1))
.suggests((context, builder) -> SharedSuggestionProvider.suggest(getAvailablePathNodeIndices(context), builder))
.executes(PathCommand::removePathNodeByIndex)
)
)
.requires(src -> Taterzens.getInstance().getPlatform().checkPermission(src, "taterzens.npc.edit.path", config.perms.npcCommandPermissionLevel))
.executes(PathCommand::editTaterzenPath)
.build();
.executes(PathCommand::removeRecentPathNode)
)
.executes(PathCommand::editTaterzenPath)
.build();

editNode.addChild(pathNode);
}

private static String[] getAvailablePathNodeIndices(CommandContext<CommandSourceStack> context) throws CommandSyntaxException
{
CommandSourceStack source = context.getSource();
ServerPlayer player = source.getPlayerOrException();

AtomicReference<ArrayList<BlockPos>> pathNodes = new AtomicReference<>();

int result = NpcCommand.selectedTaterzenExecutor(player,
taterzen -> pathNodes.set(taterzen.getPathTargets()));

if (result == 1)
{
String[] availableIndices = new String[pathNodes.get().size()];
for (int i = 0; i < pathNodes.get().size(); i++) {
availableIndices[i] = Integer.toString(i + 1);
}
return availableIndices;
}
else
{
return new String[0];
}
}
private static int listPathNodes(CommandContext<CommandSourceStack> context) throws CommandSyntaxException
{
CommandSourceStack source = context.getSource();
ServerPlayer player = source.getPlayerOrException();

MutableComponent response = translate("taterzens.command.path_editor.list").withStyle(ChatFormatting.AQUA);

return NpcCommand.selectedTaterzenExecutor(player, taterzen -> {

ArrayList<BlockPos> pathNodes = taterzen.getPathTargets();
if (!pathNodes.isEmpty()) {
for (int i = 0; i < pathNodes.size(); i++)
{
int idx = i + 1;

response.append(
new TextComponent("\n" + idx + ": (" + pathNodes.get(i).toShortString() + ")")
.withStyle(i % 2 == 0 ? ChatFormatting.GREEN : ChatFormatting.BLUE)
);
}
}
else
{
response.append(
new TextComponent(" " + translate("taterzens.command.path_editor.empty").getString())
.withStyle(ChatFormatting.YELLOW)
);
}

source.sendSuccess(response, false);
});
}

private static int addPathNode(CommandContext<CommandSourceStack> context) throws CommandSyntaxException
{
CommandSourceStack source = context.getSource();
ServerPlayer player = source.getPlayerOrException();
BlockPos pos = BlockPosArgument.getLoadedBlockPos(context, "pos");

int result = NpcCommand.selectedTaterzenExecutor(player, taterzen -> {

taterzen.addPathTarget(pos);

// Replace block in world with redstone block in case player is in editor mode
if(((ITaterzenEditor) player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {
player.connection.send(new ClientboundBlockUpdatePacket(pos, Blocks.REDSTONE_BLOCK.defaultBlockState()));
}
});

if (result == 1)
{
source.sendSuccess(successText("taterzens.command.path_editor.add.success", "(" + pos.toShortString() + ")"), false);
return 1;
}
else
{
source.sendFailure(errorText("taterzens.command.path_editor.add.failure", "(" + pos.toShortString() + ")"));
return 0;
}
}

private static int removePathNodeByIndex(CommandContext<CommandSourceStack> context) throws CommandSyntaxException
{
CommandSourceStack source = context.getSource();
ServerPlayer player = source.getPlayerOrException();
int idx = IntegerArgumentType.getInteger(context, "index") - 1;
AtomicReference<BlockPos> pathNode = new AtomicReference<>();

AtomicBoolean success = new AtomicBoolean(false);
NpcCommand.selectedTaterzenExecutor(player, taterzen -> {
try
{
if (!taterzen.getPathTargets().isEmpty())
{
pathNode.set(taterzen.getPathTargets().get(idx));
taterzen.removePathTarget(pathNode.get());

// Revert blocks from redstone to before in case player is in editor mode
if(((ITaterzenEditor) player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {
player.connection.send(new ClientboundBlockUpdatePacket(pathNode.get(), player.getLevel().getBlockState(pathNode.get())));
}

source.sendSuccess(successText("taterzens.command.path_editor.remove.success", "(" + pathNode.get().toShortString() + ")"), false);
}
else
{
source.sendSuccess(successText("taterzens.command.path_editor.empty"), false);
}

success.set(true);
}
catch (IndexOutOfBoundsException err)
{
source.sendFailure(errorText("taterzens.command.path_editor.remove.outofbounds",
Integer.toString(idx + 1),
"1",
Integer.toString(taterzen.getPathTargets().size()))
);
success.set(false);
}
});

if (success.get())
{
return 1;
}
else
{
source.sendFailure(errorText("taterzens.command.path_editor.remove.failure.index", Integer.toString(idx + 1)));
return 0;
}
}

private static int removeRecentPathNode(CommandContext<CommandSourceStack> context) throws CommandSyntaxException
{
CommandSourceStack source = context.getSource();
ServerPlayer player = source.getPlayerOrException();

int result = NpcCommand.selectedTaterzenExecutor(player, taterzen -> {
ArrayList<BlockPos> pathNodes = taterzen.getPathTargets();
if (!pathNodes.isEmpty())
{
int lastIndex = pathNodes.size() - 1;
BlockPos lastPos = pathNodes.get(lastIndex);
taterzen.removePathTargetByIndex(lastIndex);

// Revert blocks from redstone to before in case player is in editor mode
if(((ITaterzenEditor) player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {
player.connection.send(new ClientboundBlockUpdatePacket(lastPos, player.getLevel().getBlockState(lastPos)));
}

source.sendSuccess(successText("taterzens.command.path_editor.remove.success", "(" + lastPos.toShortString() + ")"), false);
}
else
{
source.sendSuccess(successText("taterzens.command.path_editor.empty"), false);
}
});

if (result == 1)
{
return 1;
}
else
{
source.sendFailure(errorText("taterzens.command.path_editor.remove.failure"));
return 0;
}
}

private static int clearTaterzenPath(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
CommandSourceStack source = context.getSource();
Entity entity = source.getEntityOrException();
Expand Down
37 changes: 29 additions & 8 deletions common/src/main/java/org/samo_lego/taterzens/event/BlockEvent.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,54 @@
package org.samo_lego.taterzens.event;

import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import org.samo_lego.taterzens.interfaces.ITaterzenEditor;

import java.util.ArrayList;

import static org.samo_lego.taterzens.util.TextUtil.errorText;
import static org.samo_lego.taterzens.util.TextUtil.successText;

public class BlockEvent {

/**
* Used if player is in path edit mode. Interacted blocks are removed from the path
* of selected {@link org.samo_lego.taterzens.npc.TaterzenNPC}.
*
* @param Player player breaking the block.
* @param player player breaking the block.
* @param world world where block is being broken.
* @param blockPos position of block interaction.
*
* @return FAIL if player has selected NPC and is in path edit mode, otherwise PASS.
*/
public static InteractionResult onBlockInteract(Player Player, Level world, BlockPos blockPos) {
if(Player instanceof ServerPlayer) { // Prevents crash on client
ITaterzenEditor player = (ITaterzenEditor) Player;
if(player.getNpc() != null && ((ITaterzenEditor) Player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {
player.getNpc().removePathTarget(blockPos);
((ServerPlayer) player).connection.send(new ClientboundBlockUpdatePacket(blockPos, world.getBlockState(blockPos)));
return InteractionResult.FAIL;
public static InteractionResult onBlockInteract(Player player, Level world, BlockPos blockPos)
{
ITaterzenEditor editorPlayer = (ITaterzenEditor) player;
if(editorPlayer.getNpc() != null && ((ITaterzenEditor) player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {

ServerPlayer serverPlayer = ((ServerPlayer) editorPlayer);
ArrayList<BlockPos> pathNodes = editorPlayer.getNpc().getPathTargets();

if (!pathNodes.isEmpty()) {
int idx = pathNodes.indexOf(blockPos);
if (idx >= 0) {
editorPlayer.getNpc().removePathTargetByIndex(idx);
serverPlayer.connection.send(new ClientboundBlockUpdatePacket(blockPos, world.getBlockState(blockPos)));
serverPlayer.sendMessage(successText("taterzens.command.path_editor.remove.success", "(" + blockPos.toShortString() + ")"), ChatType.SYSTEM, serverPlayer.getUUID());
}
else {
serverPlayer.sendMessage(errorText("taterzens.command.path_editor.remove.404", "(" + blockPos.toShortString() + ")"), ChatType.SYSTEM, serverPlayer.getUUID());
}
}
else {
serverPlayer.sendMessage(successText("taterzens.command.path_editor.empty", "(" + blockPos.toShortString() + ")"), ChatType.SYSTEM, ((ServerPlayer) editorPlayer).getUUID());
}
return InteractionResult.FAIL;
}

return InteractionResult.PASS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.server.level.ServerPlayer;
Expand All @@ -15,6 +16,8 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import static org.samo_lego.taterzens.util.TextUtil.successText;

@Mixin(ServerPlayerGameMode.class)
public class ServerPlayInteractionManagerMixin {

Expand All @@ -39,10 +42,12 @@ public class ServerPlayInteractionManagerMixin {
)
private void onAttackBlock(BlockPos blockPos, ServerboundPlayerActionPacket.Action playerAction, Direction direction, int worldHeight, CallbackInfo ci) {
if (playerAction == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) {
ITaterzenEditor player = (ITaterzenEditor) this.player;
if(player.getNpc() != null && ((ITaterzenEditor) this.player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {
player.getNpc().addPathTarget(blockPos);
((ServerPlayer) player).connection.send(new ClientboundBlockUpdatePacket(blockPos, Blocks.REDSTONE_BLOCK.defaultBlockState()));
ITaterzenEditor editorPlayer = (ITaterzenEditor) this.player;
if(editorPlayer.getNpc() != null && ((ITaterzenEditor) this.player).getEditorMode() == ITaterzenEditor.EditorMode.PATH) {
editorPlayer.getNpc().addPathTarget(blockPos);
ServerPlayer serverPlayer = ((ServerPlayer) editorPlayer);
serverPlayer.connection.send(new ClientboundBlockUpdatePacket(blockPos, Blocks.REDSTONE_BLOCK.defaultBlockState()));
serverPlayer.sendMessage(successText("taterzens.command.path_editor.add.success", "(" + blockPos.toShortString() + ")"), ChatType.SYSTEM, serverPlayer.getUUID());
ci.cancel();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,13 @@ public void removePathTarget(BlockPos blockPos) {
this.npcData.pathTargets.remove(blockPos);
}

/** Removes node from path targets by index.
* @param index Index of the entry in the ArrayList.
* */
public void removePathTargetByIndex(int index) {
this.npcData.pathTargets.remove(index);
}

/**
* Gets the path nodes / targets.
* @return array list of block positions.
Expand Down
9 changes: 9 additions & 0 deletions common/src/main/resources/data/taterzens/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
"taterzens.command.path_editor.desc.1": "Left click the blocks to add them to the path.",
"taterzens.command.path_editor.desc.2": "Right click the blocks to remove them to the path.",
"taterzens.command.path_editor.clear": "Path for %s was cleared successfully.",
"taterzens.command.path_editor.list": "Currently set path nodes:",
"taterzens.command.path_editor.empty": "No path nodes set.",
"taterzens.command.path_editor.add.success": "Successfully added node %s to path.",
"taterzens.command.path_editor.add.failure": "Could not add node %s to path.",
"taterzens.command.path_editor.remove.success": "Successfully removed node %s from path.",
"taterzens.command.path_editor.remove.failure.index": "Could not remove node number %s from path.",
"taterzens.command.path_editor.remove.failure": "Could not remove the last node from path.",
"taterzens.command.path_editor.remove.404": "Block position %s is not part of the path.",
"taterzens.command.path_editor.remove.outofbounds": "Could not remove node with index %s from path. Minimum index is %s, maximum is %s.",
"taterzens.command.profession.add": "Profession %s has been added successfully.",
"taterzens.command.profession.remove": "Profession %s has been removed successfully.",
"taterzens.command.profession.error.404": "No professions with id %s were found.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ permissions = [
"taterzens.npc.edit.path",
# Clears Taterzen's path.
"taterzens.npc.edit.path.clear",
# Lists all path entries of a Taterzen
"taterzens.npc.edit.path.list",

# Editing Taterzen's messages
# Enters message adding mode of Taterzen.
Expand Down
Loading