diff --git a/main/src/main/java/net/citizensnpcs/Settings.java b/main/src/main/java/net/citizensnpcs/Settings.java index 6f5a4f969..70bb7fb67 100644 --- a/main/src/main/java/net/citizensnpcs/Settings.java +++ b/main/src/main/java/net/citizensnpcs/Settings.java @@ -137,6 +137,7 @@ public void loadFromKey(DataKey root) { NPC_SKIN_USE_LATEST("npc.skins.use-latest-by-default", false), NPC_SKIN_VIEW_DISTANCE("npc.skins.view-distance", 100D), PACKET_UPDATE_DELAY("npc.packets.update-delay", 30), + PLACEHOLDER_SKIN_UPDATE_FREQUENCY("npc.skins.placeholder-update-frequency-ticks", 5 * 60 * 20), REMOVE_PLAYERS_FROM_PLAYER_LIST("npc.player.remove-from-list", true), SAVE_TASK_DELAY("storage.save-task.delay", 20 * 60 * 60), SELECTION_ITEM("npc.selection.item", "stick"), diff --git a/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java b/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java index 64a8e362f..ebc8e7438 100644 --- a/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/SkinTrait.java @@ -7,18 +7,23 @@ import net.citizensnpcs.api.persistence.Persist; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.TraitName; +import net.citizensnpcs.api.util.DataKey; +import net.citizensnpcs.api.util.Placeholders; import net.citizensnpcs.npc.skin.SkinnableEntity; +import net.md_5.bungee.api.ChatColor; @TraitName("skintrait") public class SkinTrait extends Trait { @Persist private boolean fetchDefaultSkin = true; + private String filledPlaceholder; @Persist private String signature; @Persist private String skinName; @Persist private String textureRaw; + private int timer; @Persist private boolean updateSkins = Setting.NPC_SKIN_USE_LATEST.asBoolean(); @@ -26,6 +31,16 @@ public SkinTrait() { super("skintrait"); } + private void checkPlaceholder(boolean update) { + String filled = ChatColor.stripColor(Placeholders.replace(skinName, null, npc).toLowerCase()); + if (!filled.equalsIgnoreCase(skinName)) { + filledPlaceholder = filled; + if (update) { + onSkinChange(true); + } + } + } + /** * Clears skin texture and name. */ @@ -53,7 +68,7 @@ public String getSignature() { * @return The skin name if set, or null (i.e. using the NPC's name) */ public String getSkinName() { - return skinName; + return filledPlaceholder != null && skinName != null ? filledPlaceholder : skinName; } /** @@ -63,6 +78,11 @@ public String getTexture() { return textureRaw; } + @Override + public void load(DataKey key) { + checkPlaceholder(false); + } + @SuppressWarnings("deprecation") private void migrate() { boolean update = false; @@ -100,6 +120,12 @@ private void onSkinChange(boolean forceUpdate) { @Override public void run() { migrate(); + if (timer-- > 0) + return; + timer = Setting.PLACEHOLDER_SKIN_UPDATE_FREQUENCY.asInt(); + if (filledPlaceholder == null) + return; + checkPlaceholder(true); } /** @@ -137,10 +163,21 @@ public void setSkinName(String name) { */ public void setSkinName(String name, boolean forceUpdate) { Preconditions.checkNotNull(name); - this.skinName = name.toLowerCase(); + setSkinNameInternal(name); onSkinChange(forceUpdate); } + private void setSkinNameInternal(String name) { + skinName = ChatColor.stripColor(name.toLowerCase()); + checkPlaceholder(false); + String filled = ChatColor.stripColor(Placeholders.replace(skinName, null, npc).toLowerCase()); + if (!filled.equalsIgnoreCase(skinName)) { + filledPlaceholder = filled; + } else { + filledPlaceholder = null; + } + } + /** * Sets the skin data directly, respawning the NPC if spawned * @@ -156,7 +193,7 @@ public void setSkinPersistent(String skinName, String signature, String data) { Preconditions.checkNotNull(signature); Preconditions.checkNotNull(data); - this.skinName = skinName.toLowerCase(); + setSkinNameInternal(skinName); this.signature = signature; this.textureRaw = data; this.updateSkins = false;