Add rudimentary /npc undo, some misc cleanup

This commit is contained in:
fullwall 2022-02-19 13:35:16 +08:00
parent 984ce6d9e7
commit aacff7b45a
12 changed files with 143 additions and 45 deletions

View File

@ -45,7 +45,9 @@ import org.json.simple.parser.JSONParser;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import net.citizensnpcs.Citizens;
@ -83,6 +85,9 @@ import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.Paginator;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.commands.gui.NPCConfigurator;
import net.citizensnpcs.commands.history.CommandHistoryItem;
import net.citizensnpcs.commands.history.CreateNPCHistoryItem;
import net.citizensnpcs.commands.history.RemoveNPCHistoryItem;
import net.citizensnpcs.npc.EntityControllers;
import net.citizensnpcs.npc.NPCSelector;
import net.citizensnpcs.npc.Template;
@ -131,6 +136,7 @@ import net.citizensnpcs.util.Util;
@Requirements(selected = true, ownership = true)
public class NPCCommands {
private final ListMultimap<UUID, CommandHistoryItem> history = ArrayListMultimap.create();
private final NPCSelector selector;
private final NPCRegistry temporaryRegistry;
@ -139,6 +145,10 @@ public class NPCCommands {
temporaryRegistry = CitizensAPI.createCitizensBackedNPCRegistry(new MemoryNPCDataStore());
}
private void addCommandHistory(CommandSender sender, CommandHistoryItem item) {
history.put(sender instanceof Entity ? ((Entity) sender).getUniqueId() : null, item);
}
@Command(
aliases = { "npc" },
usage = "age [age] (-l)",
@ -378,7 +388,7 @@ public class NPCCommands {
} else if (args.getString(1).equalsIgnoreCase("remove")) {
if (args.argsLength() == 2)
throw new CommandUsageException();
int id = args.getInteger(2);
int id = args.getInteger(2, -1);
if (!commands.hasCommandId(id))
throw new CommandException(Messages.COMMAND_UNKNOWN_COMMAND_ID, id);
commands.removeCommandById(id);
@ -479,6 +489,7 @@ public class NPCCommands {
Messaging.sendTr(sender, Messages.NPC_COPIED, npc.getName());
selector.select(sender, copy);
addCommandHistory(sender, new CreateNPCHistoryItem(copy));
}
@Command(
@ -615,6 +626,7 @@ public class NPCCommands {
npc.getOrAddTrait(Age.class).setAge(age);
}
selector.select(sender, npc);
addCommandHistory(sender, new CreateNPCHistoryItem(npc));
Messaging.send(sender, msg + '.');
}
@ -1734,6 +1746,7 @@ public class NPCCommands {
Collection<NPC> npcs = Lists.newArrayList(CitizensAPI.getNPCRegistry());
for (NPC o : npcs) {
if (o.getOrAddTrait(Owner.class).isOwnedBy(owner)) {
addCommandHistory(sender, new RemoveNPCHistoryItem(o));
o.destroy(sender);
}
}
@ -1743,6 +1756,7 @@ public class NPCCommands {
if (args.hasValueFlag("eid")) {
Entity entity = Bukkit.getServer().getEntity(UUID.fromString(args.getFlag("eid")));
if (entity != null && (npc = CitizensAPI.getNPCRegistry().getNPC(entity)) != null) {
addCommandHistory(sender, new RemoveNPCHistoryItem(npc));
npc.destroy(sender);
Messaging.sendTr(sender, Messages.NPC_REMOVED, npc.getName());
return;
@ -1755,7 +1769,10 @@ public class NPCCommands {
if (args.getString(1).equalsIgnoreCase("all")) {
if (!sender.hasPermission("citizens.admin.remove.all") && !sender.hasPermission("citizens.admin"))
throw new NoPermissionsException();
CitizensAPI.getNPCRegistry().deregisterAll();
for (NPC rem : CitizensAPI.getNPCRegistry()) {
addCommandHistory(sender, new RemoveNPCHistoryItem(rem));
rem.destroy();
}
Messaging.sendTr(sender, Messages.REMOVED_ALL_NPCS);
return;
} else {
@ -1769,6 +1786,7 @@ public class NPCCommands {
throw new CommandException(Messages.COMMAND_MUST_BE_OWNER);
if (!sender.hasPermission("citizens.npc.remove") && !sender.hasPermission("citizens.admin"))
throw new NoPermissionsException();
addCommandHistory(sender, new RemoveNPCHistoryItem(npc));
npc.destroy(sender);
Messaging.sendTr(sender, Messages.NPC_REMOVED, npc.getName());
}
@ -1784,6 +1802,7 @@ public class NPCCommands {
throw new CommandException(Messages.COMMAND_MUST_BE_OWNER);
if (!sender.hasPermission("citizens.npc.remove") && !sender.hasPermission("citizens.admin"))
throw new NoPermissionsException();
addCommandHistory(sender, new RemoveNPCHistoryItem(npc));
npc.destroy(sender);
Messaging.sendTr(sender, Messages.NPC_REMOVED, npc.getName());
}
@ -2504,6 +2523,36 @@ public class NPCCommands {
Messaging.sendTr(sender, Messages.ENTITY_TYPE_SET, npc.getName(), args.getString(1));
}
@Command(
aliases = { "npc" },
usage = "undo (all)",
desc = "Undoes the last action (currently only create/remove supported)",
modifiers = { "undo" },
min = 1,
max = 2,
permission = "citizens.npc.undo")
@Requirements
public void undo(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
if (args.argsLength() > 1 && args.getString(1).equals("all")) {
while (undoLastAction(sender)) {
}
} else if (undoLastAction(sender)) {
Messaging.sendTr(sender, Messages.UNDO_SUCCESSFUL);
} else {
Messaging.sendTr(sender, Messages.UNDO_UNSUCCESSFUL);
}
}
private boolean undoLastAction(CommandSender sender) {
UUID uuid = sender instanceof Entity ? ((Entity) sender).getUniqueId() : null;
List<CommandHistoryItem> hist = history.get(uuid);
if (hist.size() == 0)
return false;
CommandHistoryItem item = hist.remove(hist.size() - 1);
item.undo(sender, selector);
return true;
}
@Command(
aliases = { "npc" },
usage = "useitem (-o(ffhand))",

View File

@ -0,0 +1,9 @@
package net.citizensnpcs.commands.history;
import org.bukkit.command.CommandSender;
import net.citizensnpcs.api.npc.NPCSelector;
public interface CommandHistoryItem {
void undo(CommandSender sender, NPCSelector selector);
}

View File

@ -0,0 +1,25 @@
package net.citizensnpcs.commands.history;
import java.util.UUID;
import org.bukkit.command.CommandSender;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCSelector;
public class CreateNPCHistoryItem implements CommandHistoryItem {
private final UUID uuid;
public CreateNPCHistoryItem(NPC npc) {
this.uuid = npc.getUniqueId();
}
@Override
public void undo(CommandSender sender, NPCSelector selector) {
NPC npc = CitizensAPI.getNPCRegistry().getByUniqueIdGlobal(uuid);
if (npc != null) {
npc.destroy();
}
}
}

View File

@ -0,0 +1,35 @@
package net.citizensnpcs.commands.history;
import java.util.UUID;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCSelector;
import net.citizensnpcs.api.trait.trait.MobType;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.MemoryDataKey;
public class RemoveNPCHistoryItem implements CommandHistoryItem {
private final int id;
private final DataKey key;
private final EntityType type;
private final UUID uuid;
public RemoveNPCHistoryItem(NPC from) {
this.key = new MemoryDataKey();
from.save(key);
this.type = from.getOrAddTrait(MobType.class).getType();
this.uuid = from.getUniqueId();
this.id = from.getId();
}
@Override
public void undo(CommandSender sender, NPCSelector selector) {
NPC npc = CitizensAPI.getNPCRegistry().createNPC(type, uuid, id, key.getString("name"));
npc.load(key);
selector.select(sender, npc);
}
}

View File

@ -385,6 +385,8 @@ public class Messages {
public static final String TROPICALFISH_BODY_COLOR_SET = "citizens.commands.npc.tropicalfish.body-color-set";
public static final String TROPICALFISH_PATTERN_COLOR_SET = "citizens.commands.npc.tropicalfish.pattern-color-set";
public static final String TROPICALFISH_PATTERN_SET = "citizens.commands.npc.tropicalfish.pattern-set";
public static final String UNDO_SUCCESSFUL = "citizens.commands.npc.undo.successful";
public static final String UNDO_UNSUCCESSFUL = "citizens.commands.npc.undo.unsuccessful";
public static final String UNKNOWN_COMMAND = "citizens.commands.unknown-command";
public static final String UNKNOWN_MATERIAL = "citizens.commands.npc.item.unknown-material";
public static final String USING_MINECRAFT_AI = "citizens.commands.npc.ai.started";

View File

@ -254,6 +254,8 @@ citizens.commands.npc.wolf.collar-color-unsupported=[[{0}]] is not a RGB color c
citizens.commands.npc.villager.level-set=Level set to [[{0}]].
citizens.commands.npc.villager.invalid-type=Invalid villager type. Valid types are: [[{0}]].
citizens.commands.npc.villager.type-set=Type set to [[{0}]].
citizens.commands.npc.undo.successful=Undo successful
citizens.commands.npc.undo.unsuccessful=No commands to undo
citizens.commands.npc.zombiemod.villager-set=[[{0}]] is now a villager.
citizens.commands.npc.zombiemod.villager-unset=[[{0}]] is no longer a villager.
citizens.commands.npc.zombiemod.baby-set=[[{0}]] is now a baby.

View File

@ -183,8 +183,8 @@ public class EntityHumanNPC extends ServerPlayer implements NPCHolder, Skinnable
}
if (navigating) {
if (!NMSImpl.isNavigationFinished(navigation)) {
NMSImpl.updateNavigation(navigation);
if (!navigation.isDone()) {
navigation.tick();
}
moveOnCurrentHeading();
}

View File

@ -645,7 +645,7 @@ public class NMSImpl implements NMSBridge {
((Mob) raw).setPathfindingMalus(BlockPathTypes.WATER, oldWater);
}
}
stopNavigation(navigation);
navigation.stop();
}
@Override
@ -680,7 +680,7 @@ public class NMSImpl implements NMSBridge {
}
lastSpeed = params.speed();
}
if (params.debug() && !NMSImpl.isNavigationFinished(navigation)) {
if (params.debug() && !navigation.isDone()) {
BlockData data = Material.DANDELION.createBlockData();
Path path = getPathEntity(navigation);
for (Player player : Bukkit.getOnlinePlayers()) {
@ -691,7 +691,7 @@ public class NMSImpl implements NMSBridge {
}
}
navigation.setSpeedModifier(params.speed());
return NMSImpl.isNavigationFinished(navigation);
return navigation.isDone();
}
};
}
@ -1417,12 +1417,12 @@ public class NMSImpl implements NMSBridge {
@Override
public void stop() {
stopNavigation(navigation);
navigation.stop();
}
@Override
public void update() {
updateNavigation(navigation);
navigation.tick();
}
}
@ -1694,10 +1694,6 @@ public class NMSImpl implements NMSBridge {
network.address = socketAddress;
}
public static boolean isNavigationFinished(PathNavigation navigation) {
return navigation.isDone();
}
@SuppressWarnings("deprecation")
public static void minecartItemLogic(AbstractMinecart minecart) {
NPC npc = ((NPCHolder) minecart).getNPC();
@ -1848,15 +1844,11 @@ public class NMSImpl implements NMSBridge {
}
}
public static void stopNavigation(PathNavigation navigation) {
navigation.stop();
}
public static void updateAI(LivingEntity entity) {
if (entity instanceof Mob) {
Mob handle = (Mob) entity;
handle.getSensing().tick();
NMSImpl.updateNavigation(handle.getNavigation());
handle.getNavigation().tick();
handle.getMoveControl().tick();
handle.getLookControl().tick();
handle.getJumpControl().tick();
@ -1885,10 +1877,6 @@ public class NMSImpl implements NMSBridge {
}
}
public static void updateNavigation(PathNavigation navigation) {
navigation.tick();
}
private static final MethodHandle ADVANCEMENTS_PLAYER_FIELD = NMS.getFinalSetter(ServerPlayer.class, "cr");
private static final Set<EntityType> BAD_CONTROLLER_LOOK = EnumSet.of(EntityType.POLAR_BEAR, EntityType.BEE,

View File

@ -22,7 +22,7 @@ public class PlayerLookControl {
}
public void a() {
if (!NMSImpl.isNavigationFinished(this.a.getNavigation())) {
if (!this.a.getNavigation().isDone()) {
// TODO: use Citizens AI?
// this.a.yHeadRot = Mth.rotateIfNecessary(this.a.yHeadRot, this.a.yBodyRot, 75);
return;

View File

@ -184,8 +184,8 @@ public class EntityHumanNPC extends ServerPlayer implements NPCHolder, Skinnable
}
if (navigating) {
if (!NMSImpl.isNavigationFinished(navigation)) {
NMSImpl.updateNavigation(navigation);
if (!navigation.isDone()) {
navigation.tick();
}
moveOnCurrentHeading();
}

View File

@ -649,7 +649,7 @@ public class NMSImpl implements NMSBridge {
((Mob) raw).setPathfindingMalus(BlockPathTypes.WATER, oldWater);
}
}
stopNavigation(navigation);
navigation.stop();
}
@Override
@ -684,7 +684,7 @@ public class NMSImpl implements NMSBridge {
}
lastSpeed = params.speed();
}
if (params.debug() && !NMSImpl.isNavigationFinished(navigation)) {
if (params.debug() && !navigation.isDone()) {
BlockData data = Material.DANDELION.createBlockData();
Path path = getPathEntity(navigation);
for (Player player : Bukkit.getOnlinePlayers()) {
@ -695,7 +695,7 @@ public class NMSImpl implements NMSBridge {
}
}
navigation.setSpeedModifier(params.speed());
return NMSImpl.isNavigationFinished(navigation);
return navigation.isDone();
}
};
}
@ -1421,12 +1421,12 @@ public class NMSImpl implements NMSBridge {
@Override
public void stop() {
stopNavigation(navigation);
navigation.stop();
}
@Override
public void update() {
updateNavigation(navigation);
navigation.tick();
}
}
@ -1698,10 +1698,6 @@ public class NMSImpl implements NMSBridge {
network.address = socketAddress;
}
public static boolean isNavigationFinished(PathNavigation navigation) {
return navigation.isDone();
}
@SuppressWarnings("deprecation")
public static void minecartItemLogic(AbstractMinecart minecart) {
NPC npc = ((NPCHolder) minecart).getNPC();
@ -1732,7 +1728,7 @@ public class NMSImpl implements NMSBridge {
int i = 0;
for (GoalSelector selector : goalSelectors) {
try {
Collection list = selector.getAvailableGoals();
Collection<?> list = selector.getAvailableGoals();
list.clear();
Collection old = npc.data().get("selector" + i);
@ -1852,10 +1848,6 @@ public class NMSImpl implements NMSBridge {
}
}
public static void stopNavigation(PathNavigation navigation) {
navigation.stop();
}
public static Entity teleportAcrossWorld(Entity entity, ServerLevel worldserver, BlockPos location) {
if (FIND_DIMENSION_ENTRY_POINT == null || entity.isRemoved())
return null;
@ -1888,7 +1880,7 @@ public class NMSImpl implements NMSBridge {
if (entity instanceof Mob) {
Mob handle = (Mob) entity;
handle.getSensing().tick();
NMSImpl.updateNavigation(handle.getNavigation());
handle.getNavigation().tick();
handle.getMoveControl().tick();
handle.getLookControl().tick();
handle.getJumpControl().tick();
@ -1917,10 +1909,6 @@ public class NMSImpl implements NMSBridge {
}
}
public static void updateNavigation(PathNavigation navigation) {
navigation.tick();
}
private static final MethodHandle ADVANCEMENTS_PLAYER_FIELD = NMS.getFinalSetter(ServerPlayer.class, "cs");
private static final Set<EntityType> BAD_CONTROLLER_LOOK = EnumSet.of(EntityType.POLAR_BEAR, EntityType.BEE,
EntityType.SILVERFISH, EntityType.SHULKER, EntityType.ENDERMITE, EntityType.ENDER_DRAGON, EntityType.BAT,

View File

@ -22,7 +22,7 @@ public class PlayerLookControl {
}
public void a() {
if (!NMSImpl.isNavigationFinished(this.a.getNavigation())) {
if (!this.a.getNavigation().isDone()) {
// TODO: use Citizens AI?
// this.a.yHeadRot = Mth.rotateIfNecessary(this.a.yHeadRot, this.a.yBodyRot, 75);
return;