diff --git a/main/pom.xml b/main/pom.xml index 34f46e55f..a3da9f783 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -55,9 +55,9 @@ provided - net.sf.trove4j - trove4j - 3.0.3 + it.unimi.dsi + fastutil + 8.5.14 provided @@ -202,8 +202,8 @@ - gnu.trove - clib.trove + it.unimi.dsi + clib.fastutil net.kyori diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index 5917ea4f9..40e17b20a 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -291,8 +291,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { // Unfortunately, transitive dependency management is not supported in this library. lib.loadLibrary( Library.builder().groupId("ch{}ethz{}globis{}phtree").artifactId("phtree").version("2.8.0").build()); - lib.loadLibrary(Library.builder().groupId("net{}sf{}trove4j").artifactId("trove4j").version("3.0.3") - .relocate("gnu{}trove", "clib{}trove").build()); + lib.loadLibrary(Library.builder().groupId("it{}unimi{}dsi").artifactId("fastutil").version("8.5.14") + .relocate("it{}unimi{}dsi", "clib{}fastutil").build()); lib.loadLibrary(Library.builder().groupId("net{}kyori").artifactId("adventure-text-minimessage") .version("4.17.0").relocate("net{}kyori", "clib{}net{}kyori").build()); lib.loadLibrary(Library.builder().groupId("net{}kyori").artifactId("adventure-api").version("4.17.0") diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index b63e9e76c..c9709680a 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -2,6 +2,7 @@ package net.citizensnpcs; import java.lang.reflect.Method; import java.util.List; +import java.util.Objects; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -65,7 +66,6 @@ import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import com.google.common.base.Joiner; -import com.google.common.base.Predicates; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -222,7 +222,7 @@ public class EventListen implements Listener { } private Iterable getAllNPCs() { - return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Predicates.notNull()); + return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Objects::nonNull); } void loadNPCs(ChunkEvent event) { diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index c4cd484c6..396646344 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; @@ -55,7 +56,6 @@ 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; @@ -2811,7 +2811,7 @@ public class NPCCommands { } } List search = location.getWorld().getNearbyEntities(location, range, range, range).stream() - .map(registry::getNPC).filter(Predicates.notNull()).collect(Collectors.toList()); + .map(registry::getNPC).filter(Objects::nonNull).collect(Collectors.toList()); search.sort((o1, o2) -> Double.compare(o1.getEntity().getLocation().distanceSquared(location), o2.getEntity().getLocation().distanceSquared(location))); for (NPC test : search) { diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java index 50cc649c1..2d7ee494c 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java @@ -17,7 +17,7 @@ import org.bukkit.inventory.ItemStack; import com.google.common.collect.Maps; -import gnu.trove.map.hash.TIntObjectHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.event.DespawnReason; @@ -36,7 +36,7 @@ import net.citizensnpcs.util.NMS; public class CitizensNPCRegistry implements NPCRegistry { private final String name; - private final TIntObjectHashMap npcs = new TIntObjectHashMap<>(); + private final Int2ObjectOpenHashMap npcs = new Int2ObjectOpenHashMap<>(); private final NPCDataStore saves; private final Map uniqueNPCs = Maps.newHashMap(); @@ -130,7 +130,7 @@ public class CitizensNPCRegistry implements NPCRegistry { try { npc.despawn(reason); } catch (Throwable e) { - e.printStackTrace(); // ensure that all entities are despawned + e.printStackTrace(); } itr.remove(); } @@ -200,7 +200,7 @@ public class CitizensNPCRegistry implements NPCRegistry { @Override public Iterator iterator() { return new Iterator() { - Iterator itr = npcs.valueCollection().iterator(); + Iterator itr = npcs.values().iterator(); UUID lastUUID; @Override @@ -236,7 +236,7 @@ public class CitizensNPCRegistry implements NPCRegistry { @Override public Iterable sorted() { - List vals = new ArrayList<>(npcs.valueCollection()); + List vals = new ArrayList<>(npcs.values()); vals.sort(Comparator.comparing(NPC::getId)); return vals; } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java index 46ee39205..8e46ce6d8 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java @@ -21,7 +21,6 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; -import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; @@ -91,7 +90,7 @@ public class SkinUpdateTracker { } private Iterable getAllNPCs() { - return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Predicates.notNull()); + return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Objects::nonNull); } private List getNearbyNPCs(Player player, boolean reset, boolean checkFov) { diff --git a/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java b/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java index d0488436a..7787d2efd 100644 --- a/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java @@ -3,6 +3,7 @@ package net.citizensnpcs.trait; import java.util.Objects; import org.bukkit.ChatColor; +import org.bukkit.entity.Player; import com.google.common.base.Charsets; import com.google.common.io.BaseEncoding; @@ -16,6 +17,8 @@ import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.api.util.Placeholders; import net.citizensnpcs.npc.skin.Skin; import net.citizensnpcs.npc.skin.SkinnableEntity; +import net.citizensnpcs.util.NMS; +import net.citizensnpcs.util.SkinProperty; @TraitName("skintrait") public class SkinTrait extends Trait { @@ -150,6 +153,17 @@ public class SkinTrait extends Trait { skinName = ChatColor.stripColor(name); } + /** + * Set skin data copying from a {@link Player}. Not subject to rate limiting from Mojang. + * + * @param player + * The player to copy + */ + public void setSkinPersistent(Player player) { + SkinProperty sp = SkinProperty.fromMojangProfile(NMS.getProfile(player)); + setSkinPersistent(sp.name, sp.signature, sp.value); + } + /** * Sets the skin data directly, respawning the NPC if spawned. * diff --git a/main/src/main/java/net/citizensnpcs/trait/text/Text.java b/main/src/main/java/net/citizensnpcs/trait/text/Text.java index ab2d01bb2..29b62bcac 100644 --- a/main/src/main/java/net/citizensnpcs/trait/text/Text.java +++ b/main/src/main/java/net/citizensnpcs/trait/text/Text.java @@ -1,5 +1,7 @@ package net.citizensnpcs.trait.text; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -47,6 +49,8 @@ public class Text extends Trait implements Runnable, Listener { private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble(); @Persist(value = "realistic-looking") private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean(); + @Persist(value = "speech-bubble-duration") + private int speechBubbleDuration = Setting.DEFAULT_TEXT_SPEECH_BUBBLE_DURATION.asTicks(); @Persist(value = "speech-bubbles") private boolean speechBubbles; @Persist(value = "talk-close") @@ -205,11 +209,9 @@ public class Text extends Trait implements Runnable, Listener { } if (speechBubbles) { HologramTrait trait = npc.getOrAddTrait(HologramTrait.class); - trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc), - Setting.DEFAULT_TEXT_SPEECH_BUBBLE_DURATION.asTicks()); - } else { - npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player)); + trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc), speechBubbleDuration); } + npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player)); return true; } @@ -242,6 +244,10 @@ public class Text extends Trait implements Runnable, Listener { this.range = range; } + public void setSpeechBubbleDuration(Duration duration) { + this.speechBubbleDuration = (int) (duration.get(ChronoUnit.MILLIS) / 50); + } + /** * @return Whether talking close is enabled. */ diff --git a/main/src/main/java/net/citizensnpcs/trait/text/TextBasePrompt.java b/main/src/main/java/net/citizensnpcs/trait/text/TextBasePrompt.java index c358faa00..9204a8df6 100644 --- a/main/src/main/java/net/citizensnpcs/trait/text/TextBasePrompt.java +++ b/main/src/main/java/net/citizensnpcs/trait/text/TextBasePrompt.java @@ -1,5 +1,6 @@ package net.citizensnpcs.trait.text; +import java.time.Duration; import java.util.Arrays; import org.bukkit.ChatColor; @@ -13,6 +14,7 @@ import com.google.common.base.Joiner; import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.util.Messaging; +import net.citizensnpcs.api.util.SpigotUtil; import net.citizensnpcs.util.Messages; public class TextBasePrompt extends StringPrompt { @@ -76,6 +78,15 @@ public class TextBasePrompt extends StringPrompt { text.toggleRealisticLooking(); } else if (original.trim().equalsIgnoreCase("speech bubbles")) { text.toggleSpeechBubbles(); + } else if (original.trim().startsWith("speech bubbles duration")) { + try { + Duration duration = SpigotUtil.parseDuration(original.replace("speech bubbles duration", "").trim(), + null); + text.setSpeechBubbleDuration(duration); + Messaging.sendErrorTr(sender, Messages.SPEECH_BUBBLES_DURATION_SET, duration); + } catch (Exception exception) { + Messaging.sendErrorTr(sender, Messages.INVALID_SPEECH_BUBBLES_DURATION); + } } else if (input.equalsIgnoreCase("close") || original.trim().equalsIgnoreCase("talk close")) { text.toggleTalkClose(); } else if (input.equalsIgnoreCase("range")) { diff --git a/main/src/main/java/net/citizensnpcs/util/Messages.java b/main/src/main/java/net/citizensnpcs/util/Messages.java index a44fa53be..25d0a4928 100644 --- a/main/src/main/java/net/citizensnpcs/util/Messages.java +++ b/main/src/main/java/net/citizensnpcs/util/Messages.java @@ -214,6 +214,7 @@ public class Messages { public static final String INVALID_SKIN_FILE = "citizens.commands.npc.skin.invalid-file"; public static final String INVALID_SOUND = "citizens.commands.npc.sound.invalid-sound"; public static final String INVALID_SPAWN_LOCATION = "citizens.commands.npc.create.invalid-location"; + public static final String INVALID_SPEECH_BUBBLES_DURATION = "citizens.commands.npc.text.invalid-speech-bubbles-duration"; public static final String INVALID_TRIGGER_TELEPORT_FORMAT = "citizens.editors.waypoints.triggers.teleport.invalid-format"; public static final String INVALID_TROPICALFISH_COLOR = "citizens.commands.npc.tropicalfish.invalid-color"; public static final String INVALID_TROPICALFISH_PATTERN = "citizens.commands.npc.tropicalfish.invalid-pattern"; @@ -399,6 +400,7 @@ public class Messages { public static final String SNOWMAN_FORM_SNOW_STOPPED = "citizens.commands.npc.snowman.form-snow-stopped"; public static final String SOUND_INFO = "citizens.commands.npc.sound.info"; public static final String SOUND_SET = "citizens.commands.npc.sound.set"; + public static final String SPEECH_BUBBLES_DURATION_SET = "citizens.commands.npc.text.speech-bubbles-duration-set"; public static final String SPEED_MODIFIER_SET = "citizens.commands.npc.speed.set"; public static final String SPEED_TRIGGER_PROMPT = "citizens.editors.waypoints.triggers.speed.prompt"; public static final String SPELL_SET = "citizens.commands.npc.spellcaster.spell-set"; diff --git a/main/src/main/resources/en.json b/main/src/main/resources/en.json index 5cff1704d..72174844a 100644 --- a/main/src/main/resources/en.json +++ b/main/src/main/resources/en.json @@ -642,6 +642,8 @@ "citizens.commands.template.conflict" : "A template by that name already exists.", "citizens.commands.template.list.description" : "Lists available templates", "citizens.commands.template.list.header" : "]]Available templates:", + "citizens.commands.npc.text.invalid-speech-bubbles-duration" : "Invalid speech bubble duration.", + "citizens.commands.npc.text.speech-bubbles-duration-set" : "Speech bubble duration set to [[{0}]].", "citizens.commands.template.list.help" : "", "citizens.commands.template.missing" : "Template not found.", "citizens.commands.template.namespace-already-exists" : "Namespace [[{0}]] already exists", @@ -707,7 +709,7 @@ "citizens.editors.text.range-set" : "[[Range]] set to [[{0}]].", "citizens.editors.text.realistic-looking-set" : "[[Realistic looking]] set to [[{0}]].", "citizens.editors.text.speech-bubbles-set" : "[[Speech bubbles]] set to [[{0}]].", - "citizens.editors.text.start-prompt" : "Add text | default to clear)\">item | range | delay
{0}talk close | {1}random | {2}speech bubbles | {3}realistic", + "citizens.editors.text.start-prompt" : "Add text | default to clear)\">item | range | delay
{0}talk close | {1}random | {2}speech bubble duration | {2}speech bubbles | {3}realistic", "citizens.editors.text.talk-item-set" : "[[Talk item pattern]] set to [[{0}]].", "citizens.editors.text.text-list-header" : "Current text:", "citizens.editors.waypoints.guided.added-available" : "Added a [[destination]] waypoint which the NPC will randomly pathfind between.",