diff --git a/src/main/java/net/citizensnpcs/Settings.java b/src/main/java/net/citizensnpcs/Settings.java index 30d7dcaf5..69da88d75 100644 --- a/src/main/java/net/citizensnpcs/Settings.java +++ b/src/main/java/net/citizensnpcs/Settings.java @@ -58,27 +58,27 @@ public class Settings { CHAT_FORMAT_TO_TARGET("npc.chat.format.to-target", "[] -> You: "), CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS("npc.chat.format.with-targets-to-bystanders", "[] -> []: "), - CHAT_MAX_NUMBER_OF_TARGETS("npc.chat.options.max-number-of-targets-to-show", 2), - CHAT_MULTIPLE_TARGETS_FORMAT("npc.chat.options.multiple-targets-format", - "|, | & | & others"), - CHAT_RANGE("npc.chat.options.range", 5), - CHECK_MINECRAFT_VERSION("advanced.check-minecraft-version", true), - DATABASE_DRIVER("storage.database.driver", ""), - DATABASE_PASSWORD("storage.database.password", ""), - DATABASE_URL("storage.database.url", ""), - DATABASE_USERNAME("storage.database.username", ""), - DEBUG_MODE("general.debug-mode", false), - DEBUG_PATHFINDING("general.debug-pathfinding", false), - DEFAULT_LOOK_CLOSE("npc.default.look-close.enabled", false), - DEFAULT_LOOK_CLOSE_RANGE("npc.default.look-close.range", 5), - DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10), - DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F), - DEFAULT_RANDOM_TALKER("npc.default.random-talker", true), - DEFAULT_REALISTIC_LOOKING("npc.default.realistic-looking", false), - DEFAULT_STATIONARY_TICKS("npc.default.stationary-ticks", -1), - DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false), - DEFAULT_TALK_CLOSE_RANGE("npc.default.talk-close.range", 5), - DEFAULT_TEXT("npc.default.text.0", "Hi, I'm !") { + CHAT_MAX_NUMBER_OF_TARGETS("npc.chat.options.max-number-of-targets-to-show", 2), + CHAT_MULTIPLE_TARGETS_FORMAT("npc.chat.options.multiple-targets-format", + "|, | & | & others"), + CHAT_RANGE("npc.chat.options.range", 5), + CHECK_MINECRAFT_VERSION("advanced.check-minecraft-version", true), + DATABASE_DRIVER("storage.database.driver", ""), + DATABASE_PASSWORD("storage.database.password", ""), + DATABASE_URL("storage.database.url", ""), + DATABASE_USERNAME("storage.database.username", ""), + DEBUG_MODE("general.debug-mode", false), + DEBUG_PATHFINDING("general.debug-pathfinding", false), + DEFAULT_LOOK_CLOSE("npc.default.look-close.enabled", false), + DEFAULT_LOOK_CLOSE_RANGE("npc.default.look-close.range", 5), + DEFAULT_NPC_LIMIT("npc.limits.default-limit", 10), + DEFAULT_PATHFINDING_RANGE("npc.default.pathfinding.range", 25F), + DEFAULT_RANDOM_TALKER("npc.default.random-talker", true), + DEFAULT_REALISTIC_LOOKING("npc.default.realistic-looking", false), + DEFAULT_STATIONARY_TICKS("npc.default.stationary-ticks", -1), + DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false), + DEFAULT_TALK_CLOSE_RANGE("npc.default.talk-close.range", 5), + DEFAULT_TEXT("npc.default.text.0", "Hi, I'm !") { @Override public void loadFromKey(DataKey root) { List list = new ArrayList(); @@ -96,6 +96,7 @@ public class Settings { MESSAGE_COLOUR("general.color-scheme.message", ""), NPC_ATTACK_DISTANCE("npc.pathfinding.attack-range", 1.75 * 1.75), NPC_COST("economy.npc.cost", 100D), + PACKET_UPDATE_DELAY("npc.packets.update-delay", 30), QUICK_SELECT("npc.selection.quick-select", false), REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true), SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60), diff --git a/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index 830f6c550..6b22be12d 100644 --- a/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -217,7 +217,8 @@ public class CitizensNPC extends AbstractNPC { } navigator.run(); - if (!getNavigator().isNavigating() && getEntity().getWorld().getTime() % 30 == 0) { + if (!getNavigator().isNavigating() + && getEntity().getWorld().getTime() % Setting.PACKET_UPDATE_DELAY.asInt() == 0) { Player player = getEntity() instanceof Player ? (Player) getEntity() : null; NMS.sendPacketNearby(player, getStoredLocation(), new PacketPlayOutEntityTeleport(NMS.getHandle(getEntity()))); diff --git a/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java b/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java index c24520d99..a1a269f9f 100644 --- a/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java +++ b/src/main/java/net/citizensnpcs/npc/entity/EntityHumanNPC.java @@ -52,7 +52,6 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHolder { private PlayerNavigation navigation; private final CitizensNPC npc; private final Location packetLocationCache = new Location(null, 0, 0, 0); - private int packetUpdateCount; private int useListName = -1; public EntityHumanNPC(MinecraftServer minecraftServer, WorldServer world, GameProfile gameProfile, @@ -258,7 +257,7 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHolder { } private void updatePackets(boolean navigating) { - if (++packetUpdateCount >= 30) { + if (world.getWorld().getTime() % Setting.PACKET_UPDATE_DELAY.asInt() == 0) { Location current = getBukkitEntity().getLocation(packetLocationCache); Packet[] packets = new Packet[navigating ? 6 : 7]; if (!navigating) { @@ -278,7 +277,6 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHolder { removeFromPlayerList ? 9999 : ping); } NMS.sendPacketsNearby(getBukkitEntity(), current, packets); - packetUpdateCount = 0; } } diff --git a/src/main/java/net/citizensnpcs/trait/text/Text.java b/src/main/java/net/citizensnpcs/trait/text/Text.java index 2a2b1cb3b..e097abe85 100644 --- a/src/main/java/net/citizensnpcs/trait/text/Text.java +++ b/src/main/java/net/citizensnpcs/trait/text/Text.java @@ -1,237 +1,237 @@ -package net.citizensnpcs.trait.text; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -import net.citizensnpcs.Settings.Setting; -import net.citizensnpcs.api.CitizensAPI; -import net.citizensnpcs.api.ai.speech.SpeechContext; -import net.citizensnpcs.api.event.NPCRightClickEvent; -import net.citizensnpcs.api.exception.NPCLoadException; -import net.citizensnpcs.api.trait.Trait; -import net.citizensnpcs.api.util.DataKey; -import net.citizensnpcs.api.util.Messaging; -import net.citizensnpcs.api.util.Paginator; -import net.citizensnpcs.editor.Editor; -import net.citizensnpcs.trait.Toggleable; -import net.citizensnpcs.util.Messages; -import net.citizensnpcs.util.Util; - -import org.bukkit.Bukkit; -import org.bukkit.conversations.Conversation; -import org.bukkit.conversations.ConversationAbandonedEvent; -import org.bukkit.conversations.ConversationAbandonedListener; -import org.bukkit.conversations.ConversationFactory; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.plugin.Plugin; - -import com.google.common.collect.Maps; - -public class Text extends Trait implements Runnable, Toggleable, Listener, ConversationAbandonedListener { - private final Map cooldowns = Maps.newHashMap(); - private int currentIndex; - private String itemInHandPattern = "default"; - private final Plugin plugin; - private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean(); - private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble(); - private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean(); - private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean(); - private final List text = new ArrayList(); - - public Text() { - super("text"); - this.plugin = CitizensAPI.getPlugin(); - } - - void add(String string) { - text.add(string); - } - - @Override - public void conversationAbandoned(ConversationAbandonedEvent event) { - Bukkit.dispatchCommand((Player) event.getContext().getForWhom(), "npc text"); - } - - void edit(int index, String newText) { - text.set(index, newText); - } - - public Editor getEditor(final Player player) { - final Conversation conversation = new ConversationFactory(plugin).addConversationAbandonedListener(this) - .withLocalEcho(false).withEscapeSequence("/npc text").withEscapeSequence("exit").withModality(false) - .withFirstPrompt(new TextStartPrompt(this)).buildConversation(player); - return new Editor() { - @Override - public void begin() { - Messaging.sendTr(player, Messages.TEXT_EDITOR_BEGIN); - conversation.begin(); - } - - @Override - public void end() { - Messaging.sendTr(player, Messages.TEXT_EDITOR_END); - conversation.abandon(); - } - }; - } - - boolean hasIndex(int index) { - return index >= 0 && text.size() > index; - } - - @Override - public void load(DataKey key) throws NPCLoadException { - text.clear(); - // TODO: legacy, remove later - for (DataKey sub : key.getIntegerSubKeys()) { - text.add(sub.getString("")); - } - for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) { - text.add(sub.getString("")); - } - if (text.isEmpty()) { - populateDefaultText(); - } - - talkClose = key.getBoolean("talk-close", talkClose); - realisticLooker = key.getBoolean("realistic-looking", realisticLooker); - randomTalker = key.getBoolean("random-talker", randomTalker); - range = key.getDouble("range", range); - itemInHandPattern = key.getString("talkitem", itemInHandPattern); - } - - @EventHandler - public void onRightClick(NPCRightClickEvent event) { - if (!event.getNPC().equals(npc)) - return; - String localPattern = itemInHandPattern.equals("default") ? Setting.TALK_ITEM.asString() : itemInHandPattern; - if (Util.matchesItemInHand(event.getClicker(), localPattern) && !shouldTalkClose()) { - sendText(event.getClicker()); - } - } - - private void populateDefaultText() { - text.addAll(Setting.DEFAULT_TEXT.asList()); - } - - void remove(int index) { - text.remove(index); - } - - @Override - public void run() { - if (!talkClose || !npc.isSpawned()) - return; - List nearby = npc.getEntity().getNearbyEntities(range, range, range); - for (Entity search : nearby) { - if (!(search instanceof Player)) - continue; - Player player = (Player) search; - // If the cooldown is not expired, do not send text - Date cooldown = cooldowns.get(player.getName()); - if (cooldown != null) { - if (!new Date().after(cooldown)) { - return; - } - cooldowns.remove(player.getName()); - } - if (!sendText(player)) - return; - // Add a cooldown if the text was successfully sent - Date wait = new Date(); - int secondsDelta = RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt()) - + Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt(); - if (secondsDelta <= 0) - return; - long millisecondsDelta = TimeUnit.MILLISECONDS.convert(secondsDelta, TimeUnit.SECONDS); - wait.setTime(wait.getTime() + millisecondsDelta); - cooldowns.put(player.getName(), wait); - } - } - - @Override - public void save(DataKey key) { - key.setBoolean("talk-close", talkClose); - key.setBoolean("random-talker", randomTalker); - key.setBoolean("realistic-looking", realisticLooker); - key.setDouble("range", range); - key.setString("talkitem", itemInHandPattern); - // TODO: legacy, remove later - for (int i = 0; i < 100; i++) - key.removeKey(String.valueOf(i)); - key.removeKey("text"); - for (int i = 0; i < text.size(); i++) - key.setString("text." + String.valueOf(i), text.get(i)); - } - - boolean sendPage(Player player, int page) { - Paginator paginator = new Paginator().header(npc.getName() + "'s Text Entries"); - for (int i = 0; i < text.size(); i++) - paginator.addLine("" + i + " <7>- " + text.get(i)); - - return paginator.sendPage(player, page); - } - - private boolean sendText(Player player) { - if (!player.hasPermission("citizens.admin") && !player.hasPermission("citizens.npc.talk")) - return false; - if (text.size() == 0) - return false; - - int index = 0; - if (randomTalker) - index = RANDOM.nextInt(text.size()); - else { - if (currentIndex > text.size() - 1) - currentIndex = 0; - index = currentIndex++; - } - - npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player)); - return true; - } - - void setItemInHandPattern(String pattern) { - itemInHandPattern = pattern; - } - - void setRange(double range) { - this.range = range; - } - - boolean shouldTalkClose() { - return talkClose; - } - - @Override - public boolean toggle() { - return (talkClose = !talkClose); - } - - boolean toggleRandomTalker() { - return (randomTalker = !randomTalker); - } - - boolean toggleRealisticLooking() { - return (realisticLooker = !realisticLooker); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Text{talk-close=" + talkClose + ",text="); - for (String line : text) - builder.append(line + ","); - builder.append("}"); - return builder.toString(); - } - - private static Random RANDOM = Util.getFastRandom(); +package net.citizensnpcs.trait.text; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import net.citizensnpcs.Settings.Setting; +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.ai.speech.SpeechContext; +import net.citizensnpcs.api.event.NPCRightClickEvent; +import net.citizensnpcs.api.exception.NPCLoadException; +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.util.DataKey; +import net.citizensnpcs.api.util.Messaging; +import net.citizensnpcs.api.util.Paginator; +import net.citizensnpcs.editor.Editor; +import net.citizensnpcs.trait.Toggleable; +import net.citizensnpcs.util.Messages; +import net.citizensnpcs.util.Util; + +import org.bukkit.Bukkit; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ConversationAbandonedListener; +import org.bukkit.conversations.ConversationFactory; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +import com.google.common.collect.Maps; + +public class Text extends Trait implements Runnable, Toggleable, Listener, ConversationAbandonedListener { + private final Map cooldowns = Maps.newHashMap(); + private int currentIndex; + private String itemInHandPattern = "default"; + private final Plugin plugin; + private boolean randomTalker = Setting.DEFAULT_RANDOM_TALKER.asBoolean(); + private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble(); + private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean(); + private boolean talkClose = Setting.DEFAULT_TALK_CLOSE.asBoolean(); + private final List text = new ArrayList(); + + public Text() { + super("text"); + this.plugin = CitizensAPI.getPlugin(); + } + + void add(String string) { + text.add(string); + } + + @Override + public void conversationAbandoned(ConversationAbandonedEvent event) { + Bukkit.dispatchCommand((Player) event.getContext().getForWhom(), "npc text"); + } + + void edit(int index, String newText) { + text.set(index, newText); + } + + public Editor getEditor(final Player player) { + final Conversation conversation = new ConversationFactory(plugin).addConversationAbandonedListener(this) + .withLocalEcho(false).withEscapeSequence("/npc text").withEscapeSequence("exit").withModality(false) + .withFirstPrompt(new TextStartPrompt(this)).buildConversation(player); + return new Editor() { + @Override + public void begin() { + Messaging.sendTr(player, Messages.TEXT_EDITOR_BEGIN); + conversation.begin(); + } + + @Override + public void end() { + Messaging.sendTr(player, Messages.TEXT_EDITOR_END); + conversation.abandon(); + } + }; + } + + boolean hasIndex(int index) { + return index >= 0 && text.size() > index; + } + + @Override + public void load(DataKey key) throws NPCLoadException { + text.clear(); + // TODO: legacy, remove later + for (DataKey sub : key.getIntegerSubKeys()) { + text.add(sub.getString("")); + } + for (DataKey sub : key.getRelative("text").getIntegerSubKeys()) { + text.add(sub.getString("")); + } + if (text.isEmpty()) { + populateDefaultText(); + } + + talkClose = key.getBoolean("talk-close", talkClose); + realisticLooker = key.getBoolean("realistic-looking", realisticLooker); + randomTalker = key.getBoolean("random-talker", randomTalker); + range = key.getDouble("range", range); + itemInHandPattern = key.getString("talkitem", itemInHandPattern); + } + + @EventHandler + public void onRightClick(NPCRightClickEvent event) { + if (!event.getNPC().equals(npc)) + return; + String localPattern = itemInHandPattern.equals("default") ? Setting.TALK_ITEM.asString() : itemInHandPattern; + if (Util.matchesItemInHand(event.getClicker(), localPattern) && !shouldTalkClose()) { + sendText(event.getClicker()); + } + } + + private void populateDefaultText() { + text.addAll(Setting.DEFAULT_TEXT.asList()); + } + + void remove(int index) { + text.remove(index); + } + + @Override + public void run() { + if (!talkClose || !npc.isSpawned()) + return; + List nearby = npc.getEntity().getNearbyEntities(range, range, range); + for (Entity search : nearby) { + if (!(search instanceof Player)) + continue; + Player player = (Player) search; + // If the cooldown is not expired, do not send text + Date cooldown = cooldowns.get(player.getName()); + if (cooldown != null) { + if (!new Date().after(cooldown)) { + return; + } + cooldowns.remove(player.getName()); + } + if (!sendText(player)) + return; + // Add a cooldown if the text was successfully sent + Date wait = new Date(); + int secondsDelta = RANDOM.nextInt(Setting.TALK_CLOSE_MAXIMUM_COOLDOWN.asInt()) + + Setting.TALK_CLOSE_MINIMUM_COOLDOWN.asInt(); + if (secondsDelta <= 0) + return; + long millisecondsDelta = TimeUnit.MILLISECONDS.convert(secondsDelta, TimeUnit.SECONDS); + wait.setTime(wait.getTime() + millisecondsDelta); + cooldowns.put(player.getName(), wait); + } + } + + @Override + public void save(DataKey key) { + key.setBoolean("talk-close", talkClose); + key.setBoolean("random-talker", randomTalker); + key.setBoolean("realistic-looking", realisticLooker); + key.setDouble("range", range); + key.setString("talkitem", itemInHandPattern); + // TODO: legacy, remove later + for (int i = 0; i < 100; i++) + key.removeKey(String.valueOf(i)); + key.removeKey("text"); + for (int i = 0; i < text.size(); i++) + key.setString("text." + String.valueOf(i), text.get(i)); + } + + boolean sendPage(Player player, int page) { + Paginator paginator = new Paginator().header(npc.getName() + "'s Text Entries"); + for (int i = 0; i < text.size(); i++) + paginator.addLine("" + i + " <7>- " + text.get(i)); + + return paginator.sendPage(player, page); + } + + private boolean sendText(Player player) { + if (!player.hasPermission("citizens.admin") && !player.hasPermission("citizens.npc.talk")) + return false; + if (text.size() == 0) + return false; + + int index = 0; + if (randomTalker) + index = RANDOM.nextInt(text.size()); + else { + if (currentIndex > text.size() - 1) + currentIndex = 0; + index = currentIndex++; + } + + npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player)); + return true; + } + + void setItemInHandPattern(String pattern) { + itemInHandPattern = pattern; + } + + void setRange(double range) { + this.range = range; + } + + boolean shouldTalkClose() { + return talkClose; + } + + @Override + public boolean toggle() { + return (talkClose = !talkClose); + } + + boolean toggleRandomTalker() { + return (randomTalker = !randomTalker); + } + + boolean toggleRealisticLooking() { + return (realisticLooker = !realisticLooker); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Text{talk-close=" + talkClose + ",text="); + for (String line : text) + builder.append(line + ","); + builder.append("}"); + return builder.toString(); + } + + private static Random RANDOM = Util.getFastRandom(); } \ No newline at end of file