mirror of
https://github.com/YouHaveTrouble/NotJustNameplates.git
synced 2026-05-12 06:26:58 +00:00
animations, config, built-in display name placeholder, update improvements
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package me.youhavetrouble.notjustnameplates;
|
||||
|
||||
import me.youhavetrouble.notjustnameplates.displays.DisplayContent;
|
||||
import me.youhavetrouble.notjustnameplates.displays.DisplayFrame;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Display;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class NJNConfig {
|
||||
|
||||
private final NotJustNameplates plugin;
|
||||
private FileConfiguration config;
|
||||
|
||||
private final HashMap<String, DisplayContent> displayContents = new HashMap<>();
|
||||
|
||||
protected NJNConfig(NotJustNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.saveDefaultConfig();
|
||||
plugin.reloadConfig();
|
||||
this.config = plugin.getConfig();
|
||||
reload();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
displayContents.clear();
|
||||
this.config = plugin.getConfig();
|
||||
|
||||
ConfigurationSection namePlatesSection = config.getConfigurationSection("nameplates");
|
||||
if (namePlatesSection == null) {
|
||||
plugin.getLogger().severe("No nameplates section found in config! Correct your config and reload.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (String sectionName : namePlatesSection.getKeys(false)) {
|
||||
ConfigurationSection displayContentSection = namePlatesSection.getConfigurationSection(sectionName);
|
||||
if (displayContentSection == null) continue;
|
||||
DisplayContent displayContent = createDisplayContent(displayContentSection);
|
||||
if (displayContent == null) continue;
|
||||
displayContents.put(sectionName, displayContent);
|
||||
}
|
||||
}
|
||||
|
||||
public DisplayContent getDisplayContent(String name) {
|
||||
return displayContents.get(name);
|
||||
}
|
||||
|
||||
protected HashMap<String, DisplayContent> getDisplayContents() {
|
||||
return displayContents;
|
||||
}
|
||||
|
||||
private DisplayContent createDisplayContent(ConfigurationSection displayContentSection) {
|
||||
|
||||
ConfigurationSection framesSection = displayContentSection.getConfigurationSection("frames");
|
||||
if (framesSection == null) {
|
||||
plugin.getLogger().severe("No frames section found in " + displayContentSection.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
DisplayContent displayContent = new DisplayContent();
|
||||
|
||||
displayContent.setRefreshRate(displayContentSection.getInt("refresh-rate", 0));
|
||||
|
||||
Display.Billboard billboard = Display.Billboard.HORIZONTAL;
|
||||
try {
|
||||
billboard = Display.Billboard.valueOf(displayContentSection.getString("billboard", "horizontal").toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.getLogger().warning("Invalid billboard type in " + displayContentSection.getName() + ": " + displayContentSection.getString("billboard")+". Using horizontal.");
|
||||
}
|
||||
displayContent.setBillboard(billboard);
|
||||
|
||||
framesSection.getKeys(false).forEach(frameName -> {
|
||||
ConfigurationSection frameSection = framesSection.getConfigurationSection(frameName);
|
||||
if (frameSection == null) return;
|
||||
String text = frameSection.getString("text");
|
||||
String backgroundColor = frameSection.getString("background");
|
||||
displayContent.addFrame(new DisplayFrame(text, colorFromHex(backgroundColor)));
|
||||
});
|
||||
return displayContent;
|
||||
}
|
||||
|
||||
private Color colorFromHex(@Nullable String hex) {
|
||||
if (hex == null) return null;
|
||||
if (!hex.startsWith("#")) {
|
||||
plugin.getLogger().warning("Invalid hex color: " + hex + " (does not start with '#')");
|
||||
return null;
|
||||
}
|
||||
|
||||
hex = hex.substring(1); // Remove the '#' character
|
||||
|
||||
int r, g, b, a;
|
||||
|
||||
return switch (hex.length()) {
|
||||
case 3 -> {
|
||||
r = Integer.parseInt(String.valueOf(hex.charAt(0) + hex.charAt(0)), 16);
|
||||
g = Integer.parseInt(String.valueOf(hex.charAt(1) + hex.charAt(1)), 16);
|
||||
b = Integer.parseInt(String.valueOf(hex.charAt(2) + hex.charAt(2)), 16);
|
||||
yield Color.fromRGB(r, g, b);
|
||||
}
|
||||
case 6 -> {
|
||||
r = Integer.parseInt(hex.substring(0, 2), 16);
|
||||
g = Integer.parseInt(hex.substring(2, 4), 16);
|
||||
b = Integer.parseInt(hex.substring(4, 6), 16);
|
||||
yield Color.fromRGB(r, g, b);
|
||||
}
|
||||
case 8 -> {
|
||||
r = Integer.parseInt(hex.substring(0, 2), 16);
|
||||
g = Integer.parseInt(hex.substring(2, 4), 16);
|
||||
b = Integer.parseInt(hex.substring(4, 6), 16);
|
||||
a = Integer.parseInt(hex.substring(6, 8), 16);
|
||||
yield Color.fromARGB(a, r, g, b);
|
||||
}
|
||||
default -> {
|
||||
plugin.getLogger().warning("Invalid hex color: " + hex + " (invalid length)");
|
||||
yield null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,46 @@
|
||||
package me.youhavetrouble.notjustnameplates;
|
||||
|
||||
import me.youhavetrouble.notjustnameplates.nameplates.TeamManagementListener;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.util.permissions.DefaultPermissions;
|
||||
|
||||
public final class NotJustNameplates extends JavaPlugin {
|
||||
|
||||
private static NotJustNameplates instance;
|
||||
private static NJNConfig config;
|
||||
private static long time = Long.MIN_VALUE;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
config = new NJNConfig(this);
|
||||
|
||||
DefaultPermissions.registerPermission("notjustnameplates.seeown", "Allows a player to see their own nameplate", PermissionDefault.FALSE);
|
||||
|
||||
getServer().getPluginManager().registerEvents(new TeamManagementListener(this), this);
|
||||
getServer().getScheduler().runTaskTimerAsynchronously(this, () -> {
|
||||
time++;
|
||||
if (config == null) return;
|
||||
config.getDisplayContents().values().forEach(displayContent -> {
|
||||
if (displayContent == null) return;
|
||||
if (displayContent.getRefreshRate() <= 0) return;
|
||||
if (time % displayContent.getRefreshRate() != 0) return;
|
||||
displayContent.advanceFrame();
|
||||
});
|
||||
}, 1, 1);
|
||||
}
|
||||
|
||||
public static NotJustNameplates getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public static NJNConfig getPluginConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.youhavetrouble.notjustnameplates.displays;
|
||||
|
||||
import org.bukkit.entity.Display;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DisplayContent {
|
||||
|
||||
private final List<DisplayFrame> frames = new ArrayList<>();
|
||||
private int refreshRate = 0;
|
||||
private int currentFrame = 0;
|
||||
|
||||
private Display.Billboard billboard = Display.Billboard.HORIZONTAL;
|
||||
|
||||
public DisplayContent() {}
|
||||
|
||||
/**
|
||||
* Set the refresh rate of the display in ticks. 0 means no refresh rate.
|
||||
*/
|
||||
public void setRefreshRate(int refreshRate) {
|
||||
this.refreshRate = refreshRate;
|
||||
}
|
||||
|
||||
public int getRefreshRate() {
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
public void setBillboard(@NotNull Display.Billboard billboard) {
|
||||
this.billboard = billboard;
|
||||
}
|
||||
|
||||
public Display.Billboard getBillboard() {
|
||||
return billboard;
|
||||
}
|
||||
|
||||
public void addFrame(DisplayFrame frame) {
|
||||
frames.add(frame);
|
||||
}
|
||||
|
||||
public List<DisplayFrame> getFrames() {
|
||||
return Collections.unmodifiableList(frames);
|
||||
}
|
||||
|
||||
public DisplayFrame getCurrentFrame() {
|
||||
if (frames.isEmpty()) return null;
|
||||
return frames.get(currentFrame);
|
||||
}
|
||||
|
||||
public void advanceFrame() {
|
||||
if (frames.isEmpty()) return;
|
||||
currentFrame = currentFrame + 1 >= frames.size() ? 0 : currentFrame + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package me.youhavetrouble.notjustnameplates.displays;
|
||||
|
||||
import org.bukkit.Color;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record DisplayFrame(String text, Color backgroundColor) {
|
||||
|
||||
public DisplayFrame(@Nullable String text, @Nullable Color backgroundColor) {
|
||||
this.text = text;
|
||||
this.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package me.youhavetrouble.notjustnameplates.nameplates;
|
||||
|
||||
import me.youhavetrouble.notjustnameplates.NotJustNameplates;
|
||||
import me.youhavetrouble.notjustnameplates.displays.DisplayContent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.entity.Display;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TextDisplay;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.AxisAngle4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
@@ -19,25 +19,26 @@ import java.util.UUID;
|
||||
|
||||
public class Nameplate {
|
||||
|
||||
private Component name;
|
||||
private DisplayContent content;
|
||||
private final UUID playerUuid;
|
||||
private float heightOffset = 0.7f;
|
||||
private Color backgroundColor = null;
|
||||
private Display.Billboard billboard = Display.Billboard.CENTER;
|
||||
private final float heightOffset = 0.7f;
|
||||
private TextDisplay.TextAlignment alignment = TextDisplay.TextAlignment.CENTER;
|
||||
private boolean visibleForOwner = true;
|
||||
private boolean visibleForOwner = false;
|
||||
|
||||
private TextDisplay textDisplay;
|
||||
|
||||
public Nameplate(@NotNull UUID playerUuid, Component name) {
|
||||
public Nameplate(@NotNull UUID playerUuid, DisplayContent content) {
|
||||
this.playerUuid = playerUuid;
|
||||
this.name = name;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
private void createDisplayEntity() {
|
||||
if (textDisplay != null && !textDisplay.isDead()) return;
|
||||
if (this.content == null) return;
|
||||
if (content.getCurrentFrame().text() == null) return;
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player == null) return;
|
||||
|
||||
this.textDisplay = (TextDisplay) player.getWorld().spawnEntity(
|
||||
player.getEyeLocation(),
|
||||
EntityType.TEXT_DISPLAY,
|
||||
@@ -46,9 +47,14 @@ public class Nameplate {
|
||||
textDisplay.setInvulnerable(true);
|
||||
textDisplay.setPersistent(false);
|
||||
textDisplay.setAlignment(alignment);
|
||||
textDisplay.setBillboard(billboard);
|
||||
textDisplay.setBillboard(this.content.getBillboard());
|
||||
textDisplay.setShadowRadius(0);
|
||||
if (this.backgroundColor != null) textDisplay.setBackgroundColor(this.backgroundColor);
|
||||
|
||||
Color backgroundColor = this.content.getCurrentFrame().backgroundColor();
|
||||
if (backgroundColor != null) textDisplay.setBackgroundColor(backgroundColor);
|
||||
|
||||
textDisplay.text(parseText(this.content.getCurrentFrame().text(), player));
|
||||
|
||||
textDisplay.setTransformation(
|
||||
new Transformation(
|
||||
new Vector3f(0, heightOffset, 0), // offset
|
||||
@@ -63,31 +69,8 @@ public class Nameplate {
|
||||
player.addPassenger(textDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content of the nameplate
|
||||
* @return content of the nameplate
|
||||
*/
|
||||
public Component getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set content of the nameplate
|
||||
*/
|
||||
public void setName(Component name) {
|
||||
this.name = name;
|
||||
if (textDisplay == null || textDisplay.isDead()) return;
|
||||
textDisplay.text(name);
|
||||
}
|
||||
|
||||
public void setBillboard(@NotNull Display.Billboard billboard) {
|
||||
this.billboard = billboard;
|
||||
if (textDisplay == null || textDisplay.isDead()) return;
|
||||
textDisplay.setBillboard(billboard);
|
||||
}
|
||||
|
||||
public Display.Billboard getBillboard() {
|
||||
return billboard;
|
||||
public void setContent(@NotNull DisplayContent content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public void setAlignment(@NotNull TextDisplay.TextAlignment alignment) {
|
||||
@@ -100,20 +83,6 @@ public class Nameplate {
|
||||
return alignment;
|
||||
}
|
||||
|
||||
public void setBackgroundColor(@Nullable Color color) {
|
||||
this.backgroundColor = color;
|
||||
if (textDisplay == null || textDisplay.isDead()) return;
|
||||
if (this.backgroundColor == null) {
|
||||
textDisplay.setDefaultBackground(true);
|
||||
return;
|
||||
}
|
||||
textDisplay.setBackgroundColor(this.backgroundColor);
|
||||
}
|
||||
|
||||
public Color getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
public void setVisibleForOwner(boolean visible) {
|
||||
this.visibleForOwner = visible;
|
||||
if (textDisplay == null || textDisplay.isDead()) return;
|
||||
@@ -130,31 +99,35 @@ public class Nameplate {
|
||||
return this.visibleForOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set height offset from the player's eye location
|
||||
*/
|
||||
public void setHeightOffset(float heightOffset) {
|
||||
this.heightOffset = heightOffset;
|
||||
}
|
||||
|
||||
public float getHeightOffset() {
|
||||
return heightOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the nameplate position
|
||||
*/
|
||||
public void update() {
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player == null || player.isDead() || name == null) {
|
||||
if (player == null || player.isDead() || content.getCurrentFrame().text() == null) {
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
createDisplayEntity();
|
||||
if (textDisplay == null || textDisplay.isDead()) return;
|
||||
if (!player.getPassengers().contains(textDisplay)) {
|
||||
player.addPassenger(textDisplay);
|
||||
}
|
||||
textDisplay.text(this.name);
|
||||
|
||||
textDisplay.text(parseText(this.content.getCurrentFrame().text(), player));
|
||||
|
||||
textDisplay.setBillboard(this.content.getBillboard());
|
||||
textDisplay.setInterpolationDuration(content.getRefreshRate());
|
||||
|
||||
Color backgroundColor = this.content.getCurrentFrame().backgroundColor();
|
||||
if (backgroundColor == null) {
|
||||
textDisplay.setDefaultBackground(true);
|
||||
} else {
|
||||
textDisplay.setBackgroundColor(backgroundColor);
|
||||
}
|
||||
|
||||
setVisibleForOwner(this.visibleForOwner || player.hasPermission("notjustnameplates.seeown"));
|
||||
|
||||
}
|
||||
|
||||
protected void remove() {
|
||||
@@ -162,4 +135,18 @@ public class Nameplate {
|
||||
textDisplay.remove();
|
||||
}
|
||||
|
||||
private Component parseText(String text, Player player) {
|
||||
|
||||
Component component = MiniMessage.miniMessage().deserialize(text);
|
||||
|
||||
if (player == null || !player.isOnline()) return component;
|
||||
|
||||
component = component.replaceText(builder -> {
|
||||
builder.matchLiteral("%displayname%");
|
||||
builder.replacement(player.displayName());
|
||||
});
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
package me.youhavetrouble.notjustnameplates.nameplates;
|
||||
|
||||
import me.youhavetrouble.notjustnameplates.NotJustNameplates;
|
||||
import me.youhavetrouble.notjustnameplates.displays.DisplayContent;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
|
||||
import net.minecraft.world.scores.PlayerTeam;
|
||||
import net.minecraft.world.scores.Scoreboard;
|
||||
@@ -32,7 +33,8 @@ public class TeamManagementListener implements Listener {
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
Player joiner = event.getPlayer();
|
||||
players.put(joiner.getName(), new Nameplate(joiner.getUniqueId(), joiner.displayName()));
|
||||
DisplayContent displayContent = NotJustNameplates.getPluginConfig().getDisplayContent("default");
|
||||
players.put(joiner.getName(), new Nameplate(joiner.getUniqueId(), displayContent != null ? displayContent : new DisplayContent()));
|
||||
for (Player player : event.getPlayer().getServer().getOnlinePlayers()) {
|
||||
addPlayerToTeam(joiner, player);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
# Config is subject to change, take regular backups
|
||||
nameplates:
|
||||
default:
|
||||
text:
|
||||
- "%player_displayname%"
|
||||
# How often to switch betwwen frames (in ticks)
|
||||
# If set to 0, transitions will be disabled
|
||||
refresh-rate: 10
|
||||
|
||||
# Animation frames
|
||||
frames:
|
||||
1:
|
||||
text: "%displayname%"
|
||||
background: "#0000FFAA"
|
||||
2:
|
||||
text: "%displayname%"
|
||||
background: "#FF0000AA"
|
||||
|
||||
# Billboard options are as follows:
|
||||
# "center" - pivots around center point
|
||||
# "vertical" - pivots around vertical axis
|
||||
# "horizontal" - pivots around horizontal axis
|
||||
# "fixed" - no rotation
|
||||
billboard: "center"
|
||||
billboard: "horizontal"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: NotJustNameplates
|
||||
version: '${version}'
|
||||
version: "${version}"
|
||||
main: me.youhavetrouble.notjustnameplates.NotJustNameplates
|
||||
api-version: "1.20"
|
||||
authors: ["YouHaveTrouble"]
|
||||
|
||||
Reference in New Issue
Block a user