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

View File

@ -94,7 +94,8 @@ public class Settings {
"general.debug-chunk-loads", false), "general.debug-chunk-loads", false),
DEBUG_FILE("Send Citizens debug output to a specific file", "general.debug-file", ""), DEBUG_FILE("Send Citizens debug output to a specific file", "general.debug-file", ""),
DEBUG_MODE("Enable Citizens debugging", "general.debug-mode", false), 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( 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", "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), "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", DEFAULT_PATHFINDER_UPDATE_PATH_RATE("How often to repathfind when targeting a dynamic target such as an entity",
"npc.pathfinding.update-path-rate", "1s"), "npc.pathfinding.update-path-rate", "1s"),
DEFAULT_PATHFINDING_RANGE( 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), "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", DEFAULT_RANDOM_LOOK_CLOSE("Default random look close enabled", "npc.default.look-close.random-look-enabled",
false), false),
@ -138,11 +139,11 @@ public class Settings {
DEFAULT_REALISTIC_LOOKING("Default to checking for line of sight when looking at players", 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), "npc.default.realistic-looking", "npc.default.look-close.realistic-looking", false),
DEFAULT_SPAWN_NODAMAGE_DURATION( DEFAULT_SPAWN_NODAMAGE_DURATION(
"Default duration of no-damage-ticks on entity spawn, Minecraft default is 20 ticks", "Default duration of invincibility on entity spawn, Minecraft default is 20 ticks",
"npc.default.spawn-nodamage-ticks", "npc.default.spawn-nodamage-duration", "1s"), "npc.default.spawn-nodamage-duration", "npc.default.spawn-invincibility-duration", "1s"),
DEFAULT_STATIONARY_DURATION( DEFAULT_STATIONARY_DURATION(
"Default duration in the same location before the NPC considers itself stuck and failed pathfinding", "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( 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", "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), "npc.pathfinding.straight-line-targeting-distance", 5),
@ -171,7 +172,8 @@ public class Settings {
"npc.default.look-close.disable-while-navigating", true), "npc.default.look-close.disable-while-navigating", true),
DISABLE_MC_NAVIGATION_FALLBACK( 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", "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), DISABLE_TABLIST("Whether to remove NPCs from the tablist", "npc.tablist.disable", true),
ENTITY_SPAWN_WAIT_DURATION( 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", "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), 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", MAXIMUM_ASTAR_ITERATIONS("The maximum number of blocks to check when pathfinding",
"npc.pathfinding.maximum-new-pathfinder-iterations", "npc.pathfinding.new-finder.maximum-iterations", "npc.pathfinding.maximum-new-pathfinder-iterations", "npc.pathfinding.new-finder.maximum-iterations",
768), 1024),
MAXIMUM_VISITED_NODES("The maximum number of blocks to check when MINECRAFT pathfinding", MAXIMUM_VISITED_NODES("The maximum number of blocks to check", "npc.pathfinding.maximum-visited-blocks",
"npc.pathfinding.maximum-visited-nodes", "npc.pathfinding.maximum-visited-blocks", 1024), "npc.pathfinding.minecraft.max-visited-blocks", 1024),
MESSAGE_COLOUR("general.color-scheme.message", "<green>"), MESSAGE_COLOUR("general.color-scheme.message", "<green>"),
NEW_PATHFINDER_CHECK_BOUNDING_BOXES( NEW_PATHFINDER_CHECK_BOUNDING_BOXES(
"Whether to check bounding boxes when pathfinding such as between fences, inside doors, or other half-blocks", "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."), "Please wait for {minutes} minutes and {seconds_over} seconds."),
NPC_COMMAND_ON_GLOBAL_COOLDOWN_MESSAGE("npc.commands.error-messages.on-global-cooldown", NPC_COMMAND_ON_GLOBAL_COOLDOWN_MESSAGE("npc.commands.error-messages.on-global-cooldown",
"Please wait for {minutes} minutes and {seconds_over} seconds."), "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( 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", "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.skins.try-fetch-default-skin", true),
NPC_SKIN_RETRY_DELAY("How long before retrying skin requests (typically due to Mojang rate limiting)", NPC_SKIN_RETRY_DELAY("How long before retrying skin requests (typically due to Mojang rate limiting)",
"npc.skins.retry-delay", "5s"), "npc.skins.retry-delay", "5s"),
NPC_SKIN_ROTATION_UPDATE_DEGREES("npc.skins.rotation-update-degrees", 90f), NPC_SKIN_ROTATION_UPDATE_DEGREES("npc.skins.rotation-update-degrees", 90f),
NPC_SKIN_USE_LATEST("npc.skins.use-latest-by-default", false), NPC_SKIN_USE_LATEST("Whether to fetch new skins from Minecraft every so often",
NPC_SKIN_VIEW_DISTANCE("npc.skins.view-distance", 100D), "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_WATER_SPEED_MODIFIER("Movement speed percentage increase while in water",
"npc.movement.water-speed-modifier", 1.15F), "npc.movement.water-speed-modifier", 1.15F),
PACKET_HOLOGRAMS("Use packet NPCs for name holograms (experimental)", "npc.use-packet-holograms", false), PACKET_HOLOGRAMS("Use packet NPCs for name holograms (experimental)", "npc.use-packet-holograms", false),
PACKET_UPDATE_DELAY("npc.packets.update-delay", 30), PACKET_UPDATE_DELAY("npc.packets.update-delay", 30),
PATHFINDER_FALL_DISTANCE( 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), "npc.pathfinding.allowed-fall-distance", -1),
PLACEHOLDER_SKIN_UPDATE_FREQUENCY("How often to update placeholders", PLACEHOLDER_SKIN_UPDATE_FREQUENCY("How often to update placeholders",
"npc.skins.placeholder-update-frequency-ticks", "npc.skins.placeholder-update-frequency", "5m"), "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), SERVER_OWNS_NPCS("Whether the server owns NPCs rather than individual players", "npc.server-ownership", false),
SHOP_GLOBAL_VIEW_PERMISSION( SHOP_GLOBAL_VIEW_PERMISSION(
"The global view permission that players need to view any NPC shop<br>Defaults to empty (no permission required).", "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"), STORAGE_FILE("storage.file", "saves.yml"),
TABLIST_REMOVE_PACKET_DELAY("How long to wait before sending the tablist remove packet", TABLIST_REMOVE_PACKET_DELAY("How long to wait before sending the tablist remove packet",
"npc.tablist.remove-packet-delay", "1t"), "npc.tablist.remove-packet-delay", "1t"),
@ -274,7 +277,7 @@ public class Settings {
USE_NEW_PATHFINDER( 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", "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), "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),; WARN_ON_RELOAD("general.reload-warning-enabled", true),;
private String comments; private String comments;

View File

@ -24,7 +24,6 @@ import org.bukkit.DyeColor;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.Rotation; import org.bukkit.Rotation;
import org.bukkit.Sound; import org.bukkit.Sound;
@ -51,10 +50,13 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta; 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.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -911,9 +913,7 @@ public class NPCCommands {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (String part : parts) { for (String part : parts) {
if (part.contains(":")) { if (part.contains(":")) {
int idx = part.indexOf(':'); Template template = templateRegistry.getTemplateByKey(SpigotUtil.getKey(part));
Template template = templateRegistry.getTemplateByKey(new NamespacedKey(part.substring(0, idx),
part.substring(idx + 1).toLowerCase(Locale.ROOT)));
if (template == null) if (template == null)
continue; continue;
template.apply(npc); template.apply(npc);
@ -2775,7 +2775,7 @@ public class NPCCommands {
permission = "citizens.npc.select") permission = "citizens.npc.select")
@Requirements @Requirements
public void select(CommandContext args, CommandSender sender, NPC npc, 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 { throws CommandException {
NPCCommandSelector.Callback callback = toSelect -> { NPCCommandSelector.Callback callback = toSelect -> {
if (toSelect == null) if (toSelect == null)
@ -2795,9 +2795,22 @@ public class NPCCommands {
if (args.getSenderLocation() == null) if (args.getSenderLocation() == null)
throw new ServerCommandException(); throw new ServerCommandException();
Location location = args.getSenderLocation(); 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() List<NPC> search = location.getWorld().getNearbyEntities(location, range, range, range).stream()
.map(e -> registry.getNPC(e)).filter(e -> e != null).collect(Collectors.toList()); .map(registry::getNPC).filter(Predicates.notNull()).collect(Collectors.toList());
Collections.sort(search, (o1, o2) -> Double.compare(o1.getEntity().getLocation().distanceSquared(location), search.sort((o1, o2) -> Double.compare(o1.getEntity().getLocation().distanceSquared(location),
o2.getEntity().getLocation().distanceSquared(location))); o2.getEntity().getLocation().distanceSquared(location)));
for (NPC test : search) { for (NPC test : search) {
if (test.hasTrait(ClickRedirectTrait.class)) { if (test.hasTrait(ClickRedirectTrait.class)) {
@ -2865,11 +2878,11 @@ public class NPCCommands {
@Command( @Command(
aliases = { "npc" }, aliases = { "npc" },
usage = "shop (edit|show|delete|copyfrom) (name)", usage = "shop (edit|show|delete|copyfrom) (name) (new_name)",
desc = "", desc = "",
modifiers = { "shop" }, modifiers = { "shop" },
min = 1, min = 1,
max = 3, max = 4,
permission = "citizens.npc.shop") permission = "citizens.npc.shop")
@Requirements(selected = false, ownership = true) @Requirements(selected = false, ownership = true)
public void shop(CommandContext args, Player sender, NPC npc, public void shop(CommandContext args, Player sender, NPC npc,
@ -2882,7 +2895,7 @@ public class NPCCommands {
return; return;
} }
NPCShop shop = npc != null ? npc.getOrAddTrait(ShopTrait.class).getDefaultShop() : null; NPCShop shop = npc != null ? npc.getOrAddTrait(ShopTrait.class).getDefaultShop() : null;
if (args.argsLength() == 3) { if (args.argsLength() >= 3) {
shop = shops.getShop(args.getString(2)); shop = shops.getShop(args.getString(2));
} }
if (shop == null) if (shop == null)
@ -2900,7 +2913,8 @@ public class NPCCommands {
} else if (action.equalsIgnoreCase("copyfrom")) { } else if (action.equalsIgnoreCase("copyfrom")) {
if (!shop.canEdit(npc, sender) || !npc.getOrAddTrait(ShopTrait.class).getDefaultShop().canEdit(npc, sender)) if (!shop.canEdit(npc, sender) || !npc.getOrAddTrait(ShopTrait.class).getDefaultShop().canEdit(npc, sender))
throw new NoPermissionsException(); 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); PersistenceLoader.save(shop, key);
NPCShop copy = PersistenceLoader.load(NPCShop.class, key); NPCShop copy = PersistenceLoader.load(NPCShop.class, key);
npc.getOrAddTrait(ShopTrait.class).setDefaultShop(copy); npc.getOrAddTrait(ShopTrait.class).setDefaultShop(copy);
@ -3706,4 +3720,14 @@ public class NPCCommands {
return "org.bukkit.entity.Boat.Type"; 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; package net.citizensnpcs.commands;
import java.util.Collection; import java.util.Collection;
import java.util.Locale;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.NamespacedKey; 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.Template;
import net.citizensnpcs.api.npc.templates.TemplateRegistry; import net.citizensnpcs.api.npc.templates.TemplateRegistry;
import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.api.util.Messaging;
import net.citizensnpcs.api.util.SpigotUtil;
import net.citizensnpcs.util.Messages; import net.citizensnpcs.util.Messages;
@Requirements(selected = true, ownership = true) @Requirements(selected = true, ownership = true)
@ -42,9 +42,7 @@ public class TemplateCommands {
throws CommandException { throws CommandException {
Template template = null; Template template = null;
if (templateKey.contains(":")) { if (templateKey.contains(":")) {
int idx = templateKey.indexOf(':'); template = registry.getTemplateByKey(SpigotUtil.getKey(templateKey));
template = registry.getTemplateByKey(new NamespacedKey(templateKey.substring(0, idx),
templateKey.substring(idx + 1).toLowerCase(Locale.ROOT)));
} else { } else {
Collection<Template> templates = registry.getTemplates(templateKey); Collection<Template> templates = registry.getTemplates(templateKey);
if (templates.isEmpty()) if (templates.isEmpty())
@ -72,10 +70,7 @@ public class TemplateCommands {
public void generate(CommandContext args, CommandSender sender, NPC npc, public void generate(CommandContext args, CommandSender sender, NPC npc,
@Arg(value = 1, completionsProvider = TemplateCompletions.class) String templateName) @Arg(value = 1, completionsProvider = TemplateCompletions.class) String templateName)
throws CommandException { throws CommandException {
int idx = templateName.indexOf(':'); NamespacedKey key = SpigotUtil.getKey(templateName, "generated");
NamespacedKey key = idx == -1 ? new NamespacedKey("generated", templateName.toLowerCase(Locale.ROOT))
: new NamespacedKey(templateName.substring(0, idx),
templateName.substring(idx + 1).toLowerCase(Locale.ROOT));
if (registry.getTemplateByKey(key) != null) if (registry.getTemplateByKey(key) != null)
throw new CommandException(Messages.TEMPLATE_CONFLICT); throw new CommandException(Messages.TEMPLATE_CONFLICT);
registry.generateTemplateFromNPC(key, npc); registry.generateTemplateFromNPC(key, npc);

View File

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

View File

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

View File

@ -63,7 +63,8 @@ public class SkinUpdateTracker {
Location playerLoc = player.getLocation(); Location playerLoc = player.getLocation();
Location skinLoc = entity.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; return false;
// see if the NPC is within the players field of view // see if the NPC is within the players field of view
@ -260,7 +261,8 @@ public class SkinUpdateTracker {
if (entity == null || !entity.isValid()) if (entity == null || !entity.isValid())
return; 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(); Location location = entity.getLocation();
List<Player> players = entity.getWorld().getPlayers(); List<Player> players = entity.getWorld().getPlayers();
for (Player player : players) { for (Player player : players) {

View File

@ -2,7 +2,6 @@ package net.citizensnpcs.trait;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Iterator; import java.util.Iterator;
@ -302,7 +301,7 @@ public class CommandTrait extends Trait {
} }
int max = -1; int max = -1;
if (executionMode == ExecutionMode.SEQUENTIAL || executionMode == ExecutionMode.CYCLE) { 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; max = commandList.size() > 0 ? commandList.get(commandList.size() - 1).id : -1;
} }
if (executionMode == ExecutionMode.LINEAR) { if (executionMode == ExecutionMode.LINEAR) {
@ -784,7 +783,7 @@ public class CommandTrait extends Trait {
String commandKey = command.getEncodedKey(); String commandKey = command.getEncodedKey();
if (!player.hasPermission("citizens.npc.command.ignoreerrors.cooldown") if (!player.hasPermission("citizens.npc.command.ignoreerrors.cooldown")
&& lastUsed.containsKey(commandKey)) { && lastUsed.containsKey(commandKey)) {
long deadline = ((Number) lastUsed.get(commandKey)).longValue() long deadline = lastUsed.get(commandKey).longValue()
+ (command.cooldown != 0 ? command.cooldown : globalDelay); + (command.cooldown != 0 ? command.cooldown : globalDelay);
if (currentTimeSec < deadline) { if (currentTimeSec < deadline) {
long seconds = deadline - currentTimeSec; long seconds = deadline - currentTimeSec;
@ -796,7 +795,7 @@ public class CommandTrait extends Trait {
} }
if (!player.hasPermission("citizens.npc.command.ignoreerrors.globalcooldown") && command.globalCooldown > 0 if (!player.hasPermission("citizens.npc.command.ignoreerrors.globalcooldown") && command.globalCooldown > 0
&& trait.globalCooldowns.containsKey(commandKey)) { && 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) { if (currentTimeSec < deadline) {
long seconds = deadline - currentTimeSec; long seconds = deadline - currentTimeSec;
trait.sendErrorMessage(player, CommandTraitError.ON_GLOBAL_COOLDOWN, 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 org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.MemoryNPCDataStore;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCRegistry;
import net.citizensnpcs.api.persistence.Persist; import net.citizensnpcs.api.persistence.Persist;
import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.trait.TraitName; import net.citizensnpcs.api.trait.TraitName;
@ -74,16 +72,12 @@ public class SitTrait extends Trait {
return; return;
} }
if (chair == null) { if (chair == null) {
NPCRegistry registry = CitizensAPI.getNamedNPCRegistry("SitRegistry"); chair = CitizensAPI.getTemporaryNPCRegistry().createNPC(EntityType.ARMOR_STAND, "");
if (registry == null) {
registry = CitizensAPI.createNamedNPCRegistry("SitRegistry", new MemoryNPCDataStore());
}
chair = registry.createNPC(EntityType.ARMOR_STAND, "");
chair.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntity(npc); chair.getOrAddTrait(ArmorStandTrait.class).setAsHelperEntity(npc);
if (!chair.spawn(sittingAt.clone())) { if (!chair.spawn(sittingAt.clone())) {
chair = null; chair = null;
delay = 20; delay = 20;
Messaging.debug("Unable to spawn chair NPC"); Messaging.debug("Unable to spawn chair NPC for", npc);
return; return;
} }
} }

View File

@ -5,8 +5,6 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
import org.bukkit.entity.Villager.Profession; import org.bukkit.entity.Villager.Profession;
import com.google.common.base.Joiner;
import net.citizensnpcs.api.command.Command; import net.citizensnpcs.api.command.Command;
import net.citizensnpcs.api.command.CommandContext; import net.citizensnpcs.api.command.CommandContext;
import net.citizensnpcs.api.command.Flag; import net.citizensnpcs.api.command.Flag;
@ -93,7 +91,7 @@ public class VillagerTrait extends Trait {
if (args.hasValueFlag("profession")) { if (args.hasValueFlag("profession")) {
if (profession == null) if (profession == null)
throw new CommandException(Messages.INVALID_PROFESSION, args.getFlag("profession"), throw new CommandException(Messages.INVALID_PROFESSION, args.getFlag("profession"),
Joiner.on(',').join(VillagerProfessionEnum.values())); Util.listValuesPretty(VillagerProfessionEnum.values()));
npc.getOrAddTrait(VillagerProfession.class).setProfession(profession); npc.getOrAddTrait(VillagerProfession.class).setProfession(profession);
output += " " + Messaging.tr(Messages.PROFESSION_SET, npc.getName(), args.getFlag("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 String worldguardRegion;
private Object worldguardRegionCache; private Object worldguardRegionCache;
@Persist @Persist
private int xrange = DEFAULT_XRANGE; private int xrange = 25;
@Persist @Persist
private int yrange = DEFAULT_YRANGE; private int yrange = 3;
public void addRegionCentre(Location centre) { public void addRegionCentre(Location centre) {
regionCentres.add(centre); regionCentres.add(centre);
@ -376,7 +376,4 @@ public class WanderWaypointProvider implements WaypointProvider {
return val; 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) { if (list.size() == 3) {
return Color.fromRGB(list.get(0), list.get(1), list.get(2)); return Color.fromRGB(list.get(0), list.get(1), list.get(2));
} else if (list.size() == 4) { } 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(); throw new NumberFormatException();
} }