Move from trove to fastutil to match Minecraft. Add /npc text speech bubbles duration. Fix old Java back compatibility.

This commit is contained in:
fullwall 2024-08-31 00:15:54 +08:00
parent a1d208fe1e
commit ceb8d10cd2
11 changed files with 57 additions and 23 deletions

View File

@ -55,9 +55,9 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.sf.trove4j</groupId> <groupId>it.unimi.dsi</groupId>
<artifactId>trove4j</artifactId> <artifactId>fastutil</artifactId>
<version>3.0.3</version> <version>8.5.14</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -202,8 +202,8 @@
</filters> </filters>
<relocations> <relocations>
<relocation> <relocation>
<pattern>gnu.trove</pattern> <pattern>it.unimi.dsi</pattern>
<shadedPattern>clib.trove</shadedPattern> <shadedPattern>clib.fastutil</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>net.kyori</pattern> <pattern>net.kyori</pattern>

View File

@ -291,8 +291,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
// Unfortunately, transitive dependency management is not supported in this library. // Unfortunately, transitive dependency management is not supported in this library.
lib.loadLibrary( lib.loadLibrary(
Library.builder().groupId("ch{}ethz{}globis{}phtree").artifactId("phtree").version("2.8.0").build()); 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") lib.loadLibrary(Library.builder().groupId("it{}unimi{}dsi").artifactId("fastutil").version("8.5.14")
.relocate("gnu{}trove", "clib{}trove").build()); .relocate("it{}unimi{}dsi", "clib{}fastutil").build());
lib.loadLibrary(Library.builder().groupId("net{}kyori").artifactId("adventure-text-minimessage") lib.loadLibrary(Library.builder().groupId("net{}kyori").artifactId("adventure-text-minimessage")
.version("4.17.0").relocate("net{}kyori", "clib{}net{}kyori").build()); .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") lib.loadLibrary(Library.builder().groupId("net{}kyori").artifactId("adventure-api").version("4.17.0")

View File

@ -2,6 +2,7 @@ package net.citizensnpcs;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Objects;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@ -65,7 +66,6 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -222,7 +222,7 @@ public class EventListen implements Listener {
} }
private Iterable<NPC> getAllNPCs() { private Iterable<NPC> getAllNPCs() {
return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Predicates.notNull()); return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Objects::nonNull);
} }
void loadNPCs(ChunkEvent event) { void loadNPCs(ChunkEvent event) {

View File

@ -14,6 +14,7 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -55,7 +56,6 @@ 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;
@ -2811,7 +2811,7 @@ public class NPCCommands {
} }
} }
List<NPC> search = location.getWorld().getNearbyEntities(location, range, range, range).stream() List<NPC> 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), 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) {

View File

@ -17,7 +17,7 @@ import org.bukkit.inventory.ItemStack;
import com.google.common.collect.Maps; 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.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.event.DespawnReason; import net.citizensnpcs.api.event.DespawnReason;
@ -36,7 +36,7 @@ import net.citizensnpcs.util.NMS;
public class CitizensNPCRegistry implements NPCRegistry { public class CitizensNPCRegistry implements NPCRegistry {
private final String name; private final String name;
private final TIntObjectHashMap<NPC> npcs = new TIntObjectHashMap<>(); private final Int2ObjectOpenHashMap<NPC> npcs = new Int2ObjectOpenHashMap<>();
private final NPCDataStore saves; private final NPCDataStore saves;
private final Map<UUID, NPC> uniqueNPCs = Maps.newHashMap(); private final Map<UUID, NPC> uniqueNPCs = Maps.newHashMap();
@ -130,7 +130,7 @@ public class CitizensNPCRegistry implements NPCRegistry {
try { try {
npc.despawn(reason); npc.despawn(reason);
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); // ensure that all entities are despawned e.printStackTrace();
} }
itr.remove(); itr.remove();
} }
@ -200,7 +200,7 @@ public class CitizensNPCRegistry implements NPCRegistry {
@Override @Override
public Iterator<NPC> iterator() { public Iterator<NPC> iterator() {
return new Iterator<NPC>() { return new Iterator<NPC>() {
Iterator<NPC> itr = npcs.valueCollection().iterator(); Iterator<NPC> itr = npcs.values().iterator();
UUID lastUUID; UUID lastUUID;
@Override @Override
@ -236,7 +236,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.values());
vals.sort(Comparator.comparing(NPC::getId)); vals.sort(Comparator.comparing(NPC::getId));
return vals; return vals;
} }

View File

@ -21,7 +21,6 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -91,7 +90,7 @@ public class SkinUpdateTracker {
} }
private Iterable<NPC> getAllNPCs() { private Iterable<NPC> getAllNPCs() {
return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Predicates.notNull()); return Iterables.filter(Iterables.concat(CitizensAPI.getNPCRegistries()), Objects::nonNull);
} }
private List<SkinnableEntity> getNearbyNPCs(Player player, boolean reset, boolean checkFov) { private List<SkinnableEntity> getNearbyNPCs(Player player, boolean reset, boolean checkFov) {

View File

@ -3,6 +3,7 @@ package net.citizensnpcs.trait;
import java.util.Objects; import java.util.Objects;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.io.BaseEncoding; 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.api.util.Placeholders;
import net.citizensnpcs.npc.skin.Skin; import net.citizensnpcs.npc.skin.Skin;
import net.citizensnpcs.npc.skin.SkinnableEntity; import net.citizensnpcs.npc.skin.SkinnableEntity;
import net.citizensnpcs.util.NMS;
import net.citizensnpcs.util.SkinProperty;
@TraitName("skintrait") @TraitName("skintrait")
public class SkinTrait extends Trait { public class SkinTrait extends Trait {
@ -150,6 +153,17 @@ public class SkinTrait extends Trait {
skinName = ChatColor.stripColor(name); 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. * Sets the skin data directly, respawning the NPC if spawned.
* *

View File

@ -1,5 +1,7 @@
package net.citizensnpcs.trait.text; package net.citizensnpcs.trait.text;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; 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(); private double range = Setting.DEFAULT_TALK_CLOSE_RANGE.asDouble();
@Persist(value = "realistic-looking") @Persist(value = "realistic-looking")
private boolean realisticLooker = Setting.DEFAULT_REALISTIC_LOOKING.asBoolean(); 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") @Persist(value = "speech-bubbles")
private boolean speechBubbles; private boolean speechBubbles;
@Persist(value = "talk-close") @Persist(value = "talk-close")
@ -205,11 +209,9 @@ public class Text extends Trait implements Runnable, Listener {
} }
if (speechBubbles) { if (speechBubbles) {
HologramTrait trait = npc.getOrAddTrait(HologramTrait.class); HologramTrait trait = npc.getOrAddTrait(HologramTrait.class);
trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc), trait.addTemporaryLine(Placeholders.replace(text.get(index), player, npc), speechBubbleDuration);
Setting.DEFAULT_TEXT_SPEECH_BUBBLE_DURATION.asTicks());
} else {
npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
} }
npc.getDefaultSpeechController().speak(new SpeechContext(text.get(index), player));
return true; return true;
} }
@ -242,6 +244,10 @@ public class Text extends Trait implements Runnable, Listener {
this.range = range; this.range = range;
} }
public void setSpeechBubbleDuration(Duration duration) {
this.speechBubbleDuration = (int) (duration.get(ChronoUnit.MILLIS) / 50);
}
/** /**
* @return Whether talking close is enabled. * @return Whether talking close is enabled.
*/ */

View File

@ -1,5 +1,6 @@
package net.citizensnpcs.trait.text; package net.citizensnpcs.trait.text;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -13,6 +14,7 @@ import com.google.common.base.Joiner;
import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.Settings.Setting;
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;
public class TextBasePrompt extends StringPrompt { public class TextBasePrompt extends StringPrompt {
@ -76,6 +78,15 @@ public class TextBasePrompt extends StringPrompt {
text.toggleRealisticLooking(); text.toggleRealisticLooking();
} else if (original.trim().equalsIgnoreCase("speech bubbles")) { } else if (original.trim().equalsIgnoreCase("speech bubbles")) {
text.toggleSpeechBubbles(); 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")) { } else if (input.equalsIgnoreCase("close") || original.trim().equalsIgnoreCase("talk close")) {
text.toggleTalkClose(); text.toggleTalkClose();
} else if (input.equalsIgnoreCase("range")) { } else if (input.equalsIgnoreCase("range")) {

View File

@ -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_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_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_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_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_COLOR = "citizens.commands.npc.tropicalfish.invalid-color";
public static final String INVALID_TROPICALFISH_PATTERN = "citizens.commands.npc.tropicalfish.invalid-pattern"; 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 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_INFO = "citizens.commands.npc.sound.info";
public static final String SOUND_SET = "citizens.commands.npc.sound.set"; 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_MODIFIER_SET = "citizens.commands.npc.speed.set";
public static final String SPEED_TRIGGER_PROMPT = "citizens.editors.waypoints.triggers.speed.prompt"; 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"; public static final String SPELL_SET = "citizens.commands.npc.spellcaster.spell-set";

View File

@ -642,6 +642,8 @@
"citizens.commands.template.conflict" : "A template by that name already exists.", "citizens.commands.template.conflict" : "A template by that name already exists.",
"citizens.commands.template.list.description" : "Lists available templates", "citizens.commands.template.list.description" : "Lists available templates",
"citizens.commands.template.list.header" : "]]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.list.help" : "",
"citizens.commands.template.missing" : "Template not found.", "citizens.commands.template.missing" : "Template not found.",
"citizens.commands.template.namespace-already-exists" : "Namespace [[{0}]] already exists", "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.range-set" : "[[Range]] set to [[{0}]].",
"citizens.editors.text.realistic-looking-set" : "[[Realistic looking]] 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.speech-bubbles-set" : "[[Speech bubbles]] set to [[{0}]].",
"citizens.editors.text.start-prompt" : "<click:suggest_command:add ><yellow><u>Add text</u></click> | <click:suggest_command:item ><hover:show_text:\"Set the talk item in hand pattern (set to <yellow>default</yellow> to clear)\"><yellow><u>item</hover></click> | <click:suggest_command:range ><hover:show_text:Set the talking range in blocks><yellow><u>range</hover></click> | <click:suggest_command:delay ><hover:show_text:Set the talking delay in ticks><yellow><u>delay</u></yellow></hover></click><br><click:run_command:/npc text close><hover:show_text:Toggle sending messages when players get close>{0}<u>talk close</hover></click> | <click:run_command:/npc text random><hover:show_text:Toggle random talking>{1}<u>random</hover></click> | <click:run_command:/npc text speech bubbles><hover:show_text:Toggle showing text as holograms instead of chat messages>{2}<u>speech bubbles</hover></click> | <click:run_command:/npc text realistic looking><hover:show_text:Toggle requiring line of sight before speaking>{3}<u>realistic</hover></click>", "citizens.editors.text.start-prompt" : "<click:suggest_command:add ><yellow><u>Add text</u></click> | <click:suggest_command:item ><hover:show_text:\"Set the talk item in hand pattern (set to <yellow>default</yellow> to clear)\"><yellow><u>item</hover></click> | <click:suggest_command:range ><hover:show_text:Set the talking range in blocks><yellow><u>range</hover></click> | <click:suggest_command:delay ><hover:show_text:Set the talking delay in ticks><yellow><u>delay</u></yellow></hover></click><br><click:run_command:/npc text close><hover:show_text:Toggle sending messages when players get close>{0}<u>talk close</hover></click> | <click:run_command:/npc text random><hover:show_text:Toggle random talking>{1}<u>random</hover></click> | <click:suggest_command:/npc text speech bubbles duration ><hover:show_text:Set speech bubble duration>{2}<u>speech bubble duration</hover></click> | <click:run_command:/npc text speech bubbles><hover:show_text:Toggle showing text as holograms instead of chat messages>{2}<u>speech bubbles</hover></click> | <click:run_command:/npc text realistic looking><hover:show_text:Toggle requiring line of sight before speaking>{3}<u>realistic</hover></click>",
"citizens.editors.text.talk-item-set" : "[[Talk item pattern]] set to [[{0}]].", "citizens.editors.text.talk-item-set" : "[[Talk item pattern]] set to [[{0}]].",
"citizens.editors.text.text-list-header" : "Current text:", "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.", "citizens.editors.waypoints.guided.added-available" : "Added a [[destination]] waypoint which the NPC will randomly pathfind between.",