Fix /npc shop copyfrom

This commit is contained in:
fullwall 2024-08-03 18:01:09 +08:00
parent f27fe08c9c
commit 103ecfd369
12 changed files with 83 additions and 70 deletions

View File

@ -91,7 +91,14 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
private boolean enabled;
private LocationLookup locationLookup;
private final NMSHelper nmsHelper = new NMSHelper() {
private boolean SUPPORT_OWNER_PROFILE = true;
private boolean SUPPORT_OWNER_PROFILE = false;
{
try {
SkullMeta.class.getMethod("getOwnerProfile");
SUPPORT_OWNER_PROFILE = true;
} catch (Exception e) {
}
}
@Override
public OfflinePlayer getPlayer(BlockCommandSender sender) {
@ -115,14 +122,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
GameProfile profile = NMS.getProfile(meta);
if (profile == null) {
if (SUPPORT_OWNER_PROFILE) {
try {
profile = new GameProfile(meta.getOwnerProfile().getUniqueId(),
meta.getOwnerProfile().getName());
} catch (Exception e) {
SUPPORT_OWNER_PROFILE = false;
}
}
if (profile == null) {
profile = new GameProfile(meta.getOwnerProfile().getUniqueId(), meta.getOwnerProfile().getName());
} else {
profile = new GameProfile(UUID.randomUUID(), null);
}
}

View File

@ -94,7 +94,8 @@ public class Settings {
"general.debug-chunk-loads", false),
DEBUG_FILE("Send Citizens debug output to a specific file", "general.debug-file", ""),
DEBUG_MODE("Enable Citizens debugging", "general.debug-mode", false),
DEBUG_PATHFINDING("Debug pathfinding by showing fake target blocks", "general.debug-pathfinding", false),
DEBUG_PATHFINDING("Debug pathfinding by showing fake target blocks", "general.debug-pathfinding",
"npc.pathfinding.debug", false),
DEFAULT_BLOCK_BREAKER_RADIUS(
"The default distance radius for block breaking, in blocks<br>The NPC will pathfind to be this far away from the target block if greater than 0",
"npc.defaults.block-breaker-radius", "npc.default.block-breaker-radius", -1),
@ -128,7 +129,7 @@ public class Settings {
DEFAULT_PATHFINDER_UPDATE_PATH_RATE("How often to repathfind when targeting a dynamic target such as an entity",
"npc.pathfinding.update-path-rate", "1s"),
DEFAULT_PATHFINDING_RANGE(
"The default pathfinding range in blocks<br>Shouldn't be set too high to avoid lag - try pathfinding in sections instead",
"The default pathfinding range in blocks<br>Shouldn't be set too high to avoid lag - try pathfinding in shorter segments instead",
"npc.default.pathfinding.range", "npc.pathfinding.default-range-blocks", 75F),
DEFAULT_RANDOM_LOOK_CLOSE("Default random look close enabled", "npc.default.look-close.random-look-enabled",
false),
@ -138,11 +139,11 @@ public class Settings {
DEFAULT_REALISTIC_LOOKING("Default to checking for line of sight when looking at players",
"npc.default.realistic-looking", "npc.default.look-close.realistic-looking", false),
DEFAULT_SPAWN_NODAMAGE_DURATION(
"Default duration of no-damage-ticks on entity spawn, Minecraft default is 20 ticks",
"npc.default.spawn-nodamage-ticks", "npc.default.spawn-nodamage-duration", "1s"),
"Default duration of invincibility on entity spawn, Minecraft default is 20 ticks",
"npc.default.spawn-nodamage-duration", "npc.default.spawn-invincibility-duration", "1s"),
DEFAULT_STATIONARY_DURATION(
"Default duration in the same location before the NPC considers itself stuck and failed pathfinding",
"npc.default.stationary-ticks", "npc.default.stationary-duration", -1),
"npc.default.stationary-duration", "npc.pathfinding.default-stationary-duration", -1),
DEFAULT_STRAIGHT_LINE_TARGETING_DISTANCE(
"The distance in blocks where the NPC will switch to walking straight towards the target instead of pathfinding<br>Currently only for dynamic targets like entities",
"npc.pathfinding.straight-line-targeting-distance", 5),
@ -171,7 +172,8 @@ public class Settings {
"npc.default.look-close.disable-while-navigating", true),
DISABLE_MC_NAVIGATION_FALLBACK(
"Minecraft will pick a 'close-enough' location when pathfinding to a block if it can't find a direct path<br>Disabled by default",
"npc.pathfinding.disable-mc-fallback-navigation", true),
"npc.pathfinding.disable-mc-fallback-navigation",
"npc.pathfinding.minecraft.disable-fallback-navigation", true),
DISABLE_TABLIST("Whether to remove NPCs from the tablist", "npc.tablist.disable", true),
ENTITY_SPAWN_WAIT_DURATION(
"Entities are no longer spawned until the chunks are loaded from disk<br>Wait for chunk loading for one second by default, increase if your disk is slow",
@ -204,9 +206,9 @@ public class Settings {
MAX_TEXT_RANGE("The maximum range in blocks for chatting", "npc.chat.options.max-text-range", 500),
MAXIMUM_ASTAR_ITERATIONS("The maximum number of blocks to check when pathfinding",
"npc.pathfinding.maximum-new-pathfinder-iterations", "npc.pathfinding.new-finder.maximum-iterations",
768),
MAXIMUM_VISITED_NODES("The maximum number of blocks to check when MINECRAFT pathfinding",
"npc.pathfinding.maximum-visited-nodes", "npc.pathfinding.maximum-visited-blocks", 1024),
1024),
MAXIMUM_VISITED_NODES("The maximum number of blocks to check", "npc.pathfinding.maximum-visited-blocks",
"npc.pathfinding.minecraft.max-visited-blocks", 1024),
MESSAGE_COLOUR("general.color-scheme.message", "<green>"),
NEW_PATHFINDER_CHECK_BOUNDING_BOXES(
"Whether to check bounding boxes when pathfinding such as between fences, inside doors, or other half-blocks",
@ -229,21 +231,22 @@ public class Settings {
"Please wait for {minutes} minutes and {seconds_over} seconds."),
NPC_COMMAND_ON_GLOBAL_COOLDOWN_MESSAGE("npc.commands.error-messages.on-global-cooldown",
"Please wait for {minutes} minutes and {seconds_over} seconds."),
NPC_COST("The default cost to create an NPC", "economy.npc.cost", 100D),
NPC_COST("The default cost to create an NPC", "economy.npc.cost", "npc.defaults.npc-cost", 100D),
NPC_SKIN_FETCH_DEFAULT(
"Whether to try and look for the player skin for all new NPCs<br>If this is set to false and you create an NPC named Dinnerbone, the NPC will have the default (steve/alex/etc) skin rather than trying to fetch the Dinnerbone skin",
"npc.skins.try-fetch-default-skin", true),
NPC_SKIN_RETRY_DELAY("How long before retrying skin requests (typically due to Mojang rate limiting)",
"npc.skins.retry-delay", "5s"),
NPC_SKIN_ROTATION_UPDATE_DEGREES("npc.skins.rotation-update-degrees", 90f),
NPC_SKIN_USE_LATEST("npc.skins.use-latest-by-default", false),
NPC_SKIN_VIEW_DISTANCE("npc.skins.view-distance", 100D),
NPC_SKIN_USE_LATEST("Whether to fetch new skins from Minecraft every so often",
"npc.skins.use-latest-by-default", false),
NPC_SKIN_VIEW_DISTANCE("View distance in blocks", "npc.skins.view-distance", 100D),
NPC_WATER_SPEED_MODIFIER("Movement speed percentage increase while in water",
"npc.movement.water-speed-modifier", 1.15F),
PACKET_HOLOGRAMS("Use packet NPCs for name holograms (experimental)", "npc.use-packet-holograms", false),
PACKET_UPDATE_DELAY("npc.packets.update-delay", 30),
PATHFINDER_FALL_DISTANCE(
"The default allowed maximum fall distance when pathfinding, set to -1 to use the Minecraft value",
"The default allowed maximum fall distance when pathfinding, set to -1 to use the default value",
"npc.pathfinding.allowed-fall-distance", -1),
PLACEHOLDER_SKIN_UPDATE_FREQUENCY("How often to update placeholders",
"npc.skins.placeholder-update-frequency-ticks", "npc.skins.placeholder-update-frequency", "5m"),
@ -262,7 +265,7 @@ public class Settings {
SERVER_OWNS_NPCS("Whether the server owns NPCs rather than individual players", "npc.server-ownership", false),
SHOP_GLOBAL_VIEW_PERMISSION(
"The global view permission that players need to view any NPC shop<br>Defaults to empty (no permission required).",
"npc.shops.global-view-permission", ""),
"npc.shops.global-view-permission", "npc.defaults.shops.global-view-permission", ""),
STORAGE_FILE("storage.file", "saves.yml"),
TABLIST_REMOVE_PACKET_DELAY("How long to wait before sending the tablist remove packet",
"npc.tablist.remove-packet-delay", "1t"),
@ -274,7 +277,7 @@ public class Settings {
USE_NEW_PATHFINDER(
"Whether to use the Citizens pathfinder instead of the Minecraft pathfinder<br>Much more flexible, but may have different performance to Minecraft's pathfinder",
"npc.pathfinding.use-new-finder", false),
USE_SCOREBOARD_TEAMS("npc.scoreboard-teams.enable", true),
USE_SCOREBOARD_TEAMS("npc.scoreboard-teams.enable", "npc.defaults.enable-scoreboard-teams", true),
WARN_ON_RELOAD("general.reload-warning-enabled", true),;
private String comments;

View File

@ -24,7 +24,6 @@ import org.bukkit.DyeColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer;
import org.bukkit.Rotation;
import org.bukkit.Sound;
@ -51,10 +50,13 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
@ -911,9 +913,7 @@ public class NPCCommands {
StringBuilder builder = new StringBuilder();
for (String part : parts) {
if (part.contains(":")) {
int idx = part.indexOf(':');
Template template = templateRegistry.getTemplateByKey(new NamespacedKey(part.substring(0, idx),
part.substring(idx + 1).toLowerCase(Locale.ROOT)));
Template template = templateRegistry.getTemplateByKey(SpigotUtil.getKey(part));
if (template == null)
continue;
template.apply(npc);
@ -2775,7 +2775,7 @@ public class NPCCommands {
permission = "citizens.npc.select")
@Requirements
public void select(CommandContext args, CommandSender sender, NPC npc,
@Flag(value = "range", defValue = "10") double range, @Flag("registry") String registryName)
@Flag(value = "range", defValue = "15") double range, @Flag("registry") String registryName)
throws CommandException {
NPCCommandSelector.Callback callback = toSelect -> {
if (toSelect == null)
@ -2795,9 +2795,22 @@ public class NPCCommands {
if (args.getSenderLocation() == null)
throw new ServerCommandException();
Location location = args.getSenderLocation();
if (SUPPORT_RAYTRACE && sender instanceof Player) {
Location eyeLoc = ((Player) sender).getEyeLocation();
RayTraceResult res = eyeLoc.getWorld().rayTraceEntities(eyeLoc, eyeLoc.getDirection(), range, 0.1,
e -> !e.equals(sender));
if (res != null && registry.isNPC(res.getHitEntity())) {
NPC hit = registry.getNPC(res.getHitEntity());
if (hit.hasTrait(ClickRedirectTrait.class)) {
hit = hit.getTraitNullable(ClickRedirectTrait.class).getRedirectNPC();
}
callback.run(hit);
return;
}
}
List<NPC> search = location.getWorld().getNearbyEntities(location, range, range, range).stream()
.map(e -> registry.getNPC(e)).filter(e -> e != null).collect(Collectors.toList());
Collections.sort(search, (o1, o2) -> Double.compare(o1.getEntity().getLocation().distanceSquared(location),
.map(registry::getNPC).filter(Predicates.notNull()).collect(Collectors.toList());
search.sort((o1, o2) -> Double.compare(o1.getEntity().getLocation().distanceSquared(location),
o2.getEntity().getLocation().distanceSquared(location)));
for (NPC test : search) {
if (test.hasTrait(ClickRedirectTrait.class)) {
@ -2865,11 +2878,11 @@ public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "shop (edit|show|delete|copyfrom) (name)",
usage = "shop (edit|show|delete|copyfrom) (name) (new_name)",
desc = "",
modifiers = { "shop" },
min = 1,
max = 3,
max = 4,
permission = "citizens.npc.shop")
@Requirements(selected = false, ownership = true)
public void shop(CommandContext args, Player sender, NPC npc,
@ -2882,7 +2895,7 @@ public class NPCCommands {
return;
}
NPCShop shop = npc != null ? npc.getOrAddTrait(ShopTrait.class).getDefaultShop() : null;
if (args.argsLength() == 3) {
if (args.argsLength() >= 3) {
shop = shops.getShop(args.getString(2));
}
if (shop == null)
@ -2900,7 +2913,8 @@ public class NPCCommands {
} else if (action.equalsIgnoreCase("copyfrom")) {
if (!shop.canEdit(npc, sender) || !npc.getOrAddTrait(ShopTrait.class).getDefaultShop().canEdit(npc, sender))
throw new NoPermissionsException();
DataKey key = new MemoryDataKey();
String newName = args.argsLength() == 4 ? args.getString(3) : UUID.randomUUID().toString();
DataKey key = new MemoryDataKey().getRelative(newName);
PersistenceLoader.save(shop, key);
NPCShop copy = PersistenceLoader.load(NPCShop.class, key);
npc.getOrAddTrait(ShopTrait.class).setDefaultShop(copy);
@ -3706,4 +3720,14 @@ public class NPCCommands {
return "org.bukkit.entity.Boat.Type";
}
}
private static boolean SUPPORT_RAYTRACE = false;
static {
try {
SUPPORT_RAYTRACE = World.class.getMethod("rayTraceEntities", Location.class, Vector.class,
double.class) != null;
} catch (Exception e) {
}
}
}

View File

@ -1,7 +1,6 @@
package net.citizensnpcs.commands;
import java.util.Collection;
import java.util.Locale;
import java.util.stream.Collectors;
import org.bukkit.NamespacedKey;
@ -20,6 +19,7 @@ import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.templates.Template;
import net.citizensnpcs.api.npc.templates.TemplateRegistry;
import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.util.Messages;
@Requirements(selected = true, ownership = true)
@ -42,9 +42,7 @@ public class TemplateCommands {
throws CommandException {
Template template = null;
if (templateKey.contains(":")) {
int idx = templateKey.indexOf(':');
template = registry.getTemplateByKey(new NamespacedKey(templateKey.substring(0, idx),
templateKey.substring(idx + 1).toLowerCase(Locale.ROOT)));
template = registry.getTemplateByKey(SpigotUtil.getKey(templateKey));
} else {
Collection<Template> templates = registry.getTemplates(templateKey);
if (templates.isEmpty())
@ -72,10 +70,7 @@ public class TemplateCommands {
public void generate(CommandContext args, CommandSender sender, NPC npc,
@Arg(value = 1, completionsProvider = TemplateCompletions.class) String templateName)
throws CommandException {
int idx = templateName.indexOf(':');
NamespacedKey key = idx == -1 ? new NamespacedKey("generated", templateName.toLowerCase(Locale.ROOT))
: new NamespacedKey(templateName.substring(0, idx),
templateName.substring(idx + 1).toLowerCase(Locale.ROOT));
NamespacedKey key = SpigotUtil.getKey(templateName, "generated");
if (registry.getTemplateByKey(key) != null)
throw new CommandException(Messages.TEMPLATE_CONFLICT);
registry.generateTemplateFromNPC(key, npc);

View File

@ -1,7 +1,6 @@
package net.citizensnpcs.npc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@ -238,7 +237,7 @@ public class CitizensNPCRegistry implements NPCRegistry {
@Override
public Iterable<NPC> sorted() {
List<NPC> vals = new ArrayList<>(npcs.valueCollection());
Collections.sort(vals, Comparator.comparing(NPC::getId));
vals.sort(Comparator.comparing(NPC::getId));
return vals;
}
}

View File

@ -16,6 +16,7 @@ import org.bukkit.scheduler.BukkitTask;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.util.NMS;
/**
@ -127,8 +128,8 @@ public class SkinPacketTracker {
if (!entity.getNPC().isSpawned())
return;
double viewDistance = Setting.NPC_SKIN_VIEW_DISTANCE.asDouble();
updateNearbyViewers(viewDistance);
updateNearbyViewers(entity.getNPC().data().get(NPC.Metadata.TRACKING_RANGE,
Setting.NPC_SKIN_VIEW_DISTANCE.asDouble()));
}
}.runTaskLater(CitizensAPI.getPlugin(), 15);
}

View File

@ -63,7 +63,8 @@ public class SkinUpdateTracker {
Location playerLoc = player.getLocation();
Location skinLoc = entity.getLocation();
if (playerLoc.distance(skinLoc) > Setting.NPC_SKIN_VIEW_DISTANCE.asDouble())
if (playerLoc.distance(skinLoc) > skinnable.getNPC().data().get(NPC.Metadata.TRACKING_RANGE,
Setting.NPC_SKIN_VIEW_DISTANCE.asDouble()))
return false;
// see if the NPC is within the players field of view
@ -260,7 +261,8 @@ public class SkinUpdateTracker {
if (entity == null || !entity.isValid())
return;
double viewDistance = Setting.NPC_SKIN_VIEW_DISTANCE.asDouble();
double viewDistance = skinnable.getNPC().data().get(NPC.Metadata.TRACKING_RANGE,
Setting.NPC_SKIN_VIEW_DISTANCE.asDouble());
Location location = entity.getLocation();
List<Player> players = entity.getWorld().getPlayers();
for (Player player : players) {

View File

@ -2,7 +2,6 @@ package net.citizensnpcs.trait;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.Iterator;
@ -302,7 +301,7 @@ public class CommandTrait extends Trait {
}
int max = -1;
if (executionMode == ExecutionMode.SEQUENTIAL || executionMode == ExecutionMode.CYCLE) {
Collections.sort(commandList, Comparator.comparing(o1 -> o1.id));
commandList.sort(Comparator.comparing(o1 -> o1.id));
max = commandList.size() > 0 ? commandList.get(commandList.size() - 1).id : -1;
}
if (executionMode == ExecutionMode.LINEAR) {
@ -784,7 +783,7 @@ public class CommandTrait extends Trait {
String commandKey = command.getEncodedKey();
if (!player.hasPermission("citizens.npc.command.ignoreerrors.cooldown")
&& lastUsed.containsKey(commandKey)) {
long deadline = ((Number) lastUsed.get(commandKey)).longValue()
long deadline = lastUsed.get(commandKey).longValue()
+ (command.cooldown != 0 ? command.cooldown : globalDelay);
if (currentTimeSec < deadline) {
long seconds = deadline - currentTimeSec;
@ -796,7 +795,7 @@ public class CommandTrait extends Trait {
}
if (!player.hasPermission("citizens.npc.command.ignoreerrors.globalcooldown") && command.globalCooldown > 0
&& trait.globalCooldowns.containsKey(commandKey)) {
long deadline = ((Number) trait.globalCooldowns.get(commandKey)).longValue() + command.globalCooldown;
long deadline = trait.globalCooldowns.get(commandKey).longValue() + command.globalCooldown;
if (currentTimeSec < deadline) {
long seconds = deadline - currentTimeSec;
trait.sendErrorMessage(player, CommandTraitError.ON_GLOBAL_COOLDOWN,

View File

@ -6,9 +6,7 @@ import org.bukkit.entity.Sittable;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.MemoryNPCDataStore;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName;
@ -74,16 +72,12 @@ public class SitTrait extends Trait {
return;
}
if (chair == null) {
NPCRegistry registry = CitizensAPI.getNamedNPCRegistry("SitRegistry");
if (registry == null) {
registry = CitizensAPI.createNamedNPCRegistry("SitRegistry", new MemoryNPCDataStore());
}
chair = registry.createNPC(EntityType.ARMOR_STAND, "");
chair = CitizensAPI.getTemporaryNPCRegistry().createNPC(EntityType.ARMOR_STAND, "");
chair.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntity(npc);
if (!chair.spawn(sittingAt.clone())) {
chair = null;
delay = 20;
Messaging.debug("Unable to spawn chair NPC");
Messaging.debug("Unable to spawn chair NPC for", npc);
return;
}
}

View File

@ -5,8 +5,6 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Villager;
import org.bukkit.entity.Villager.Profession;
import com.google.common.base.Joiner;
import net.citizensnpcs.api.command.Command;
import net.citizensnpcs.api.command.CommandContext;
import net.citizensnpcs.api.command.Flag;
@ -93,7 +91,7 @@ public class VillagerTrait extends Trait {
if (args.hasValueFlag("profession")) {
if (profession == null)
throw new CommandException(Messages.INVALID_PROFESSION, args.getFlag("profession"),
Joiner.on(',').join(VillagerProfessionEnum.values()));
Util.listValuesPretty(VillagerProfessionEnum.values()));
npc.getOrAddTrait(VillagerProfession.class).setProfession(profession);
output += " " + Messaging.tr(Messages.PROFESSION_SET, npc.getName(), args.getFlag("profession"));
}

View File

@ -52,9 +52,9 @@ public class WanderWaypointProvider implements WaypointProvider {
private String worldguardRegion;
private Object worldguardRegionCache;
@Persist
private int xrange = DEFAULT_XRANGE;
private int xrange = 25;
@Persist
private int yrange = DEFAULT_YRANGE;
private int yrange = 3;
public void addRegionCentre(Location centre) {
regionCentres.add(centre);
@ -376,7 +376,4 @@ public class WanderWaypointProvider implements WaypointProvider {
return val;
}
}
private static int DEFAULT_XRANGE = 25;
private static int DEFAULT_YRANGE = 3;
}

View File

@ -385,7 +385,7 @@ public class Util {
if (list.size() == 3) {
return Color.fromRGB(list.get(0), list.get(1), list.get(2));
} else if (list.size() == 4) {
return Color.fromARGB(list.get(0), list.get(1), list.get(2), list.get(3));
return Color.fromARGB(list.get(3), list.get(0), list.get(1), list.get(2));
}
throw new NumberFormatException();
}