diff --git a/README.md b/README.md
index d223206..20f6066 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,37 @@
-# This project is undergoing a major refactoring
-
# Custom-Nameplates

-
-
-
-
[](https://jitpack.io/#Xiao-MoMi/Custom-Nameplates)
-
-CustomNameplates is a Paper plugin that provides unlimited customization for nametags, bossbars, actionbars and chat bubbles.
+[](https://github.com/Xiao-MoMi/Custom-Nameplates/)
+
+
+
+
## How to build
-### Windows
-
#### Command Line
-Install JDK 17 and set the JDK installation path to JAVA_HOME as an environment variable.\
-Start powershell and change directory to the project folder.\
-Execute ".\gradlew build" and get the jar at /target/CustomNameplates-universe-version.jar.
+Install JDK 17 & 21. \
+Start terminal and change directory to the project folder.\
+Execute ".\gradlew build" and get the artifact under /target folder
#### IDE
-Import the project and execute gradle build action.
-
-##### About Proxy
-If you are using a proxy, configurate the proxy in gradle.properties. Otherwise comment the lines in gradle.properties.
+Import the project and execute gradle build action. \
+Get the artifact under /target folder
## Support the developer
Polymart: https://polymart.org/resource/customnameplates.2543 \
-Afdian: https://afdian.net/@xiaomomi
+Afdian: https://afdian.com/@xiaomomi
-## Use CustomNameplates API
+## CustomNameplates API
### Maven
-```
+```html
jitpack
@@ -46,37 +39,37 @@ Afdian: https://afdian.net/@xiaomomi
```
-```
+```html
com.github.Xiao-MoMi
Custom-Nameplates
- {LATEST}
+ {VERSION}
provided
```
### Gradle (Groovy)
-```
+```groovy
repositories {
maven { url 'https://jitpack.io' }
}
```
-```
+```groovy
dependencies {
- compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}'
+ compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{VERSION}'
}
```
### Gradle (Kotlin)
-```
+```kotlin
repositories {
maven("https://jitpack.io/")
}
```
-```
+```kotlin
dependencies {
- compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}")
+ compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{VERSION}")
}
```
\ No newline at end of file
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
index ade419c..5eed195 100644
--- a/api/build.gradle.kts
+++ b/api/build.gradle.kts
@@ -1,8 +1,28 @@
+plugins {
+ id("io.github.goooler.shadow") version "8.1.8"
+}
+
+repositories {
+}
+
dependencies {
- compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
- compileOnly("me.clip:placeholderapi:2.11.5")
implementation(project(":common"))
- implementation("net.kyori:adventure-api:4.16.0")
+ // Adventure
+ implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}")
+ compileOnly("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}")
+ compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}")
+ // YAML
+ implementation(files("libs/boosted-yaml-${rootProject.properties["boosted_yaml_version"]}.jar"))
+ // Cache
+ compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}")
+ // Netty
+ compileOnly("io.netty:netty-all:4.1.113.Final")
+ // FOP
+ compileOnly("org.apache.pdfbox:fontbox:${rootProject.properties["fontbox_version"]}")
+ // COMMONS IO
+ compileOnly("commons-io:commons-io:${rootProject.properties["commons_io_version"]}")
+ // GSON
+ compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}")
}
tasks {
diff --git a/api/libs/boosted-yaml-1.3.7.jar b/api/libs/boosted-yaml-1.3.7.jar
new file mode 100644
index 0000000..ea489cb
Binary files /dev/null and b/api/libs/boosted-yaml-1.3.7.jar differ
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java
new file mode 100644
index 0000000..5d626c7
--- /dev/null
+++ b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) <2024>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.momirealms.customnameplates.api;
+
+import net.momirealms.customnameplates.api.feature.Feature;
+import net.momirealms.customnameplates.api.feature.TickStampData;
+import net.momirealms.customnameplates.api.network.Tracker;
+import net.momirealms.customnameplates.api.placeholder.Placeholder;
+import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder;
+import net.momirealms.customnameplates.api.placeholder.RelationalPlaceholder;
+import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder;
+import net.momirealms.customnameplates.api.requirement.Requirement;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public abstract class AbstractCNPlayer implements CNPlayer {
+
+ protected final CustomNameplates plugin;
+ protected final Object player;
+
+ private boolean isLoaded = false;
+
+ private boolean isPreviewing = false;
+
+ private String equippedNameplate;
+ private String equippedBubble;
+
+ /**
+ * 此处缓存了所有解析过的变量,Relational则是缓存了与某个玩家的关系变量,仅更新当前处于active状态的变量。
+ */
+ private final Map> cachedValues = new ConcurrentHashMap<>();
+ private final Map>> cachedRelationalValues = new ConcurrentHashMap<>();
+
+ /**
+ * 此处缓存了所有解析过的条件,Relational则是缓存了与某个玩家的关系条件
+ */
+ private final Map> cachedRequirements = new ConcurrentHashMap<>();
+ private final Map>> cachedRelationalRequirements = new ConcurrentHashMap<>();
+
+ /*
+ * 这里维护了一个双向的Map以方便更新对应的Feature。
+ * 插件会先获取当前处于活跃状态的变量(由Feature提供),根据变量的更新情况,判断是否需要反馈到对应的Feature以便只在必要的时刻进行更新
+ */
+ private final Set activeFeatures = new CopyOnWriteArraySet<>();
+ private final Map> placeholder2Features = new ConcurrentHashMap<>();
+ private final Map> feature2Placeholders = new ConcurrentHashMap<>();
+
+ private final Map trackers = Collections.synchronizedMap(new WeakHashMap<>());
+
+ protected AbstractCNPlayer(CustomNameplates plugin, Object player) {
+ this.plugin = plugin;
+ this.player = player;
+ }
+
+ /**
+ * 将所有处于激活状态的变量统筹起来并返回一个更新任务
+ *
+ * @return 更新任务
+ */
+ @Override
+ public List getRefreshValueTask() {
+ Placeholder[] activePlaceholders = activePlaceholders();
+ List placeholderWithChildren = new ArrayList<>();
+ for (Placeholder placeholder : activePlaceholders) {
+ childrenFirstList(placeholder, placeholderWithChildren);
+ }
+ List uniquePlaceholderWithChildren = placeholderWithChildren.stream().distinct().toList();
+ List placeholdersToUpdate = new ArrayList<>();
+ for (Placeholder placeholder : uniquePlaceholderWithChildren) {
+ int interval = placeholder.refreshInterval();
+ if (interval > 0 && MainTask.getTicks() % interval == 0) {
+ placeholdersToUpdate.add(placeholder);
+ }
+ }
+ return placeholdersToUpdate;
+ }
+
+ @Override
+ public void forceUpdate(Set placeholders, Set others) {
+ if (placeholders.isEmpty()) return;
+ List placeholderWithChildren = new ArrayList<>();
+ for (Placeholder placeholder : placeholders) {
+ childrenFirstList(placeholder, placeholderWithChildren);
+ }
+ for (Placeholder placeholder : placeholderWithChildren) {
+ if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
+ TickStampData value = getValue(placeholder);
+ if (value == null) {
+ value = new TickStampData<>(playerPlaceholder.request(this), MainTask.getTicks(), true);
+ setValue(placeholder, value);
+ continue;
+ }
+ if (value.ticks() != MainTask.getTicks()) {
+ String newValue = playerPlaceholder.request(this);
+ value.updateTicks(!value.data().equals(newValue));
+ value.data(newValue);
+ }
+ } else if (placeholder instanceof RelationalPlaceholder relational) {
+ for (CNPlayer player : others) {
+ TickStampData value = getRelationalValue(placeholder, player);
+ if (value == null) {
+ value = new TickStampData<>(relational.request(this, player), MainTask.getTicks(), true);
+ setRelationalValue(placeholder, player, value);
+ continue;
+ }
+ if (value.ticks() != MainTask.getTicks()) {
+ String newValue = relational.request(this, player);
+ value.updateTicks(!value.data().equals(newValue));
+ value.data(newValue);
+ }
+ }
+ } else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) {
+ TickStampData value = getValue(placeholder);
+ if (value == null) {
+ String latest;
+ if (MainTask.hasRequested(sharedPlaceholder.countId())) {
+ latest = sharedPlaceholder.getLatestValue();
+ } else {
+ latest = sharedPlaceholder.request();
+ }
+ value = new TickStampData<>(latest, MainTask.getTicks(), true);
+ setValue(placeholder, value);
+ continue;
+ }
+ if (value.ticks() != MainTask.getTicks()) {
+ String latest;
+ if (MainTask.hasRequested(sharedPlaceholder.countId())) {
+ latest = sharedPlaceholder.getLatestValue();
+ } else {
+ latest = sharedPlaceholder.request();
+ }
+ value.updateTicks(!value.data().equals(latest));
+ value.data(latest);
+ }
+ }
+ }
+ }
+
+ private void childrenFirstList(Placeholder placeholder, List list) {
+ if (placeholder.children().isEmpty()) {
+ list.add(placeholder);
+ } else {
+ for (Placeholder child : placeholder.children()) {
+ childrenFirstList(child, list);
+ }
+ list.add(placeholder);
+ }
+ }
+
+ public void reload() {
+ cachedValues.clear();
+ cachedRelationalValues.clear();
+ activeFeatures.clear();
+ placeholder2Features.clear();
+ feature2Placeholders.clear();
+ }
+
+ @Override
+ public Set getUsedFeatures(Placeholder placeholder) {
+ return placeholder2Features.getOrDefault(placeholder, Collections.emptySet());
+ }
+
+ @Override
+ public Object player() {
+ return player;
+ }
+
+ public void setLoaded(boolean loaded) {
+ isLoaded = loaded;
+ }
+
+ public void setPreviewing(boolean previewing) {
+ isPreviewing = previewing;
+ }
+
+ @Override
+ public boolean isPreviewing() {
+ return isPreviewing;
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return isLoaded;
+ }
+
+ @Override
+ public void addFeature(Feature feature) {
+ activeFeatures.add(feature);
+ Set allPlaceholdersUsedInFeature = feature.allPlaceholders();
+ feature2Placeholders.put(feature, allPlaceholdersUsedInFeature);
+ for (Placeholder placeholder : allPlaceholdersUsedInFeature) {
+ Set featureSet = placeholder2Features.computeIfAbsent(placeholder, k -> {
+ forceUpdate(Set.of(placeholder), nearbyPlayers());
+ return new HashSet<>();
+ });
+ featureSet.add(feature);
+ }
+ }
+
+ @Override
+ public void removeFeature(Feature feature) {
+ activeFeatures.remove(feature);
+ Set placeholders = feature2Placeholders.remove(feature);
+ if (placeholders != null) {
+ for (Placeholder placeholder : placeholders) {
+ Set featureSet = placeholder2Features.get(placeholder);
+ featureSet.remove(feature);
+ if (featureSet.isEmpty()) placeholder2Features.remove(placeholder);
+ }
+ }
+ }
+
+ @Override
+ public void setValue(Placeholder placeholder, TickStampData value) {
+ cachedValues.put(placeholder.countId(), value);
+ }
+
+ @Override
+ public boolean setValue(Placeholder placeholder, String value) {
+ TickStampData previous = cachedValues.get(placeholder.countId());
+ int currentTicks = MainTask.getTicks();
+ boolean changed = false;
+ if (previous != null) {
+ if (previous.ticks() == currentTicks) {
+ return false;
+ }
+ String data = previous.data();
+ if (!data.equals(value)) {
+ changed = true;
+ previous.data(value);
+ previous.updateTicks(true);
+ }
+ } else {
+ changed= true;
+ previous = new TickStampData<>(value, currentTicks, true);
+ cachedValues.put(placeholder.countId(), previous);
+ }
+ return changed;
+ }
+
+ @Override
+ public void setRelationalValue(Placeholder placeholder, CNPlayer another, TickStampData value) {
+ WeakHashMap> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
+ map.put(another, value);
+ }
+
+ @Override
+ public boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value) {
+ WeakHashMap> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
+ TickStampData previous = map.get(another);
+ int currentTicks = MainTask.getTicks();
+ boolean changed = false;
+ if (previous != null) {
+ if (previous.ticks() == currentTicks) {
+ return false;
+ }
+ String data = previous.data();
+ if (!data.equals(value)) {
+ changed = true;
+ previous.data(value);
+ previous.updateTicks(true);
+ }
+ } else {
+ changed= true;
+ previous = new TickStampData<>(value, currentTicks, true);
+ map.put(another, previous);
+ }
+ return changed;
+ }
+
+ @Override
+ public String getData(Placeholder placeholder) {
+ return Optional.ofNullable(cachedValues.get(placeholder.countId())).map(TickStampData::data).orElse(placeholder.id());
+ }
+
+ @Override
+ public TickStampData getValue(Placeholder placeholder) {
+ return cachedValues.get(placeholder.countId());
+ }
+
+ @Override
+ public String getRelationalData(Placeholder placeholder, CNPlayer another) {
+ WeakHashMap> map = cachedRelationalValues.get(placeholder.countId());
+ if (map == null) {
+ return null;
+ }
+ return Optional.ofNullable(map.get(another)).map(TickStampData::data).orElse(placeholder.id());
+ }
+
+ @Override
+ public TickStampData getRelationalValue(Placeholder placeholder, CNPlayer another) {
+ WeakHashMap> map = cachedRelationalValues.get(placeholder.countId());
+ if (map == null) {
+ return null;
+ }
+ return map.get(another);
+ }
+
+ @Override
+ public Placeholder[] activePlaceholders() {
+ HashSet placeholders = new HashSet<>();
+ for (Feature feature : activeFeatures) {
+ placeholders.addAll(feature.activePlaceholders());
+ }
+ return placeholders.toArray(new Placeholder[0]);
+ }
+
+ @Override
+ public boolean isMet(Requirement[] requirements) {
+ int currentTicks = MainTask.getTicks();
+ for (Requirement requirement : requirements) {
+ TickStampData data = cachedRequirements.get(requirement);
+ if (data != null) {
+ if (data.ticks() + requirement.refreshInterval() > currentTicks) {
+ if (!data.data()) {
+ return false;
+ }
+ } else {
+ boolean satisfied = requirement.isSatisfied(this, this);
+ data.updateTicks(false);
+ data.data(satisfied);
+ if (!satisfied) {
+ return false;
+ }
+ }
+ } else {
+ boolean satisfied = requirement.isSatisfied(this, this);
+ data = new TickStampData<>(satisfied, currentTicks, true);
+ cachedRequirements.put(requirement, data);
+ if (!satisfied) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isMet(CNPlayer another, Requirement[] requirements) {
+ int currentTicks = MainTask.getTicks();
+ for (Requirement requirement : requirements) {
+ WeakHashMap> innerMap = cachedRelationalRequirements.computeIfAbsent(requirement, k -> new WeakHashMap<>());
+ TickStampData data = innerMap.get(another);
+ if (data != null) {
+ if (data.ticks() + requirement.refreshInterval() > currentTicks) {
+ if (!data.data()) {
+ return false;
+ }
+ } else {
+ boolean satisfied = requirement.isSatisfied(this, another);
+ data.updateTicks(false);
+ data.data(satisfied);
+ if (!satisfied) {
+ return false;
+ }
+ }
+ } else {
+ boolean satisfied = requirement.isSatisfied(this, another);
+ data = new TickStampData<>(satisfied, currentTicks, true);
+ innerMap.put(another, data);
+ if (!satisfied) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Tracker addPlayerToTracker(CNPlayer another) {
+ Tracker tracker = new Tracker(another);
+ trackers.put(another, tracker);
+ for (Placeholder placeholder : activePlaceholders()) {
+ if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) {
+ String value = relationalPlaceholder.request(this, another);
+ setRelationalValue(placeholder, another, value);
+ }
+ }
+ return tracker;
+ }
+
+ @Override
+ public void removePlayerFromTracker(CNPlayer another) {
+ trackers.remove(another);
+ }
+
+ @Override
+ public Set nearbyPlayers() {
+ return new HashSet<>(trackers.keySet());
+ }
+
+ @Override
+ public void trackPassengers(CNPlayer another, int... passengers) {
+ Tracker tracker = trackers.get(another);
+ if (tracker != null) {
+ for (int passenger : passengers) {
+ tracker.addPassengerID(passenger);
+ }
+ }
+ }
+
+ @Override
+ public void untrackPassengers(CNPlayer another, int... passengers) {
+ Optional.ofNullable(trackers.get(another)).ifPresent(tracker -> {
+ for (int passenger : passengers) {
+ tracker.removePassengerID(passenger);
+ }
+ });
+ }
+
+ @Override
+ public Set getTrackedPassengerIds(CNPlayer another) {
+ return Optional.ofNullable(trackers.get(another)).map(tracker -> new HashSet<>(tracker.getPassengerIDs())).orElse(new HashSet<>());
+ }
+
+ @Override
+ public Tracker getTracker(CNPlayer another) {
+ return trackers.get(another);
+ }
+
+ @Override
+ public String equippedBubble() {
+ if (equippedNameplate == null) return "none";
+ return equippedBubble;
+ }
+
+ @Override
+ public void equippedBubble(String equippedBubble) {
+ if (!equippedBubble.equals(this.equippedBubble)) {
+ this.equippedBubble = equippedBubble;
+ // TODO 更新变量
+ }
+ }
+
+ @Override
+ public String equippedNameplate() {
+ if (equippedNameplate == null) return "none";
+ return equippedNameplate;
+ }
+
+ @Override
+ public void equippedNameplate(String equippedNameplate) {
+ if (!equippedNameplate.equals(this.equippedNameplate)) {
+ this.equippedNameplate = equippedNameplate;
+ // TODO 更新变量
+ }
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) return true;
+ if (object == null || getClass() != object.getClass()) return false;
+ AbstractCNPlayer that = (AbstractCNPlayer) object;
+ return entityID() == that.entityID() && uuid().equals(that.uuid());
+ }
+
+ @Override
+ public int hashCode() {
+ return entityID();
+ }
+}
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java
new file mode 100644
index 0000000..3f74c7e
--- /dev/null
+++ b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) <2024>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.momirealms.customnameplates.api;
+
+import net.momirealms.customnameplates.api.feature.Feature;
+import net.momirealms.customnameplates.api.feature.TickStampData;
+import net.momirealms.customnameplates.api.network.Tracker;
+import net.momirealms.customnameplates.api.placeholder.Placeholder;
+import net.momirealms.customnameplates.api.requirement.Requirement;
+import net.momirealms.customnameplates.api.util.Vector3;
+
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+public interface CNPlayer {
+
+ String name();
+
+ UUID uuid();
+
+ int entityID();
+
+ Vector3 position();
+
+ String world();
+
+ void forceUpdate(Set placeholders, Set another);
+
+ Set getUsedFeatures(Placeholder placeholder);
+
+ Object player();
+
+ double scale();
+
+ boolean isCrouching();
+
+ boolean isLoaded();
+
+ boolean isPreviewing();
+
+ boolean hasPermission(String permission);
+
+ long playerTime();
+
+ List getRefreshValueTask();
+
+ Placeholder[] activePlaceholders();
+
+ boolean isOnline();
+
+ String getData(Placeholder placeholder);
+
+ TickStampData getValue(Placeholder placeholder);
+
+ String getRelationalData(Placeholder placeholder, CNPlayer another);
+
+ TickStampData getRelationalValue(Placeholder placeholder, CNPlayer another);
+
+ void setValue(Placeholder placeholder, TickStampData value);
+
+ boolean setValue(Placeholder placeholder, String value);
+
+ void setRelationalValue(Placeholder placeholder, CNPlayer another, TickStampData value);
+
+ boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value);
+
+ void addFeature(Feature feature);
+
+ void removeFeature(Feature feature);
+
+ boolean isMet(Requirement[] requirements);
+
+ boolean isMet(CNPlayer another, Requirement[] requirements);
+
+ Tracker addPlayerToTracker(CNPlayer another);
+
+ void removePlayerFromTracker(CNPlayer another);
+
+ Set nearbyPlayers();
+
+ void trackPassengers(CNPlayer another, int... passengers);
+
+ void untrackPassengers(CNPlayer another, int... passengers);
+
+ Set getTrackedPassengerIds(CNPlayer another);
+
+ Set passengers();
+
+ boolean isFlying();
+
+ Tracker getTracker(CNPlayer another);
+
+ String equippedBubble();
+
+ void equippedBubble(String equippedBubble);
+
+ String equippedNameplate();
+
+ void equippedNameplate(String equippedNameplate);
+
+ int remainingAir();
+}
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java b/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java
new file mode 100644
index 0000000..58f88f8
--- /dev/null
+++ b/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) <2024>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.momirealms.customnameplates.api;
+
+import dev.dejvokep.boostedyaml.YamlDocument;
+import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
+import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle;
+import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag;
+import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
+import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
+import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
+import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
+import dev.dejvokep.boostedyaml.utils.format.NodeRole;
+import net.momirealms.customnameplates.api.feature.bossbar.BossBar;
+import net.momirealms.customnameplates.api.feature.pack.CharacterArranger;
+import net.momirealms.customnameplates.api.helper.AdventureHelper;
+import net.momirealms.customnameplates.common.locale.TranslationManager;
+import net.momirealms.customnameplates.common.plugin.CustomNameplatesProperties;
+import net.momirealms.customnameplates.common.plugin.config.ConfigLoader;
+import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Locale;
+
+public abstract class ConfigManager implements ConfigLoader, Reloadable {
+
+ private static YamlDocument MAIN_CONFIG;
+
+ public static YamlDocument getMainConfig() {
+ if (MAIN_CONFIG == null) {
+ throw new IllegalStateException("Main config not loaded");
+ }
+ return MAIN_CONFIG;
+ }
+
+ private static ConfigManager instance;
+
+ protected final CustomNameplates plugin;
+
+ protected boolean debug;
+ protected boolean checkUpdate;
+ protected boolean metrics;
+ protected boolean actionbarModule;
+ protected boolean imageModule;
+ protected boolean bossBarModule;
+ protected boolean bubbleModule;
+ protected boolean nameplateModule;
+ protected boolean backgroundModule;
+ protected int defaultRefreshInterval;
+ protected int delaySend;
+ protected boolean catchOtherActionBar;
+
+ protected String namespace;
+ protected String font;
+
+ protected boolean generateOnStart;
+
+ protected char initialChar;
+
+ protected String imagePath;
+ protected String nameplatePath;
+ protected String bubblePath;
+ protected String spaceSplitPath;
+ protected String backgroundPath;
+
+ protected boolean enableShader;
+ protected boolean hideScoreBoardNumber;
+ protected boolean animatedText;
+ protected boolean itemsAdderEffect;
+
+ protected BossBar.Color removedBarColor;
+ protected boolean bossBar1_20_2;
+ protected boolean bossBar1_17;
+
+ protected boolean legacyUnicodes;
+
+ protected boolean packItemsAdder;
+ protected boolean packItemsAdderLegacy;
+ protected boolean packOraxen;
+
+ protected boolean chatUnsafe;
+ protected boolean chatTR;
+ protected boolean chatVenture;
+ protected boolean chatHusk;
+ protected boolean chatCarbon;
+ protected boolean chatAdvanced;
+ protected boolean chatEss;
+
+ public ConfigManager(CustomNameplates plugin) {
+ this.plugin = plugin;
+ instance = this;
+ }
+
+ @Override
+ public void load() {
+ String configVersion = CustomNameplatesProperties.getValue("config");
+ try (InputStream inputStream = new FileInputStream(resolveConfig("config.yml").toFile())) {
+ MAIN_CONFIG = YamlDocument.create(
+ inputStream,
+ plugin.getResourceStream("config.yml"),
+ GeneralSettings.builder()
+ .setRouteSeparator('.')
+ .setUseDefaults(false)
+ .build(),
+ LoaderSettings
+ .builder()
+ .setAutoUpdate(true)
+ .build(),
+ DumperSettings.builder()
+ .setEscapeUnprintable(false)
+ .setScalarFormatter((tag, value, role, def) -> {
+ if (role == NodeRole.KEY) {
+ return ScalarStyle.PLAIN;
+ } else {
+ return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
+ }
+ })
+ .build(),
+ UpdaterSettings
+ .builder()
+ .setVersioning(new BasicVersioning("config-version"))
+ .addIgnoredRoute(configVersion, "other-settings.placeholder-refresh-interval", '.')
+ .addIgnoredRoute(configVersion, "other-settings.font-templates", '.')
+ .addIgnoredRoute(configVersion, "other-settings.shift-fonts", '.')
+ .build()
+ );
+ MAIN_CONFIG.save(resolveConfig("config.yml").toFile());
+ loadSettings();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void loadSettings() {
+ YamlDocument config = MAIN_CONFIG;
+ AdventureHelper.legacySupport = config.getBoolean("other-settings.legacy-color-code-support", true);
+ TranslationManager.forceLocale(TranslationManager.parseLocale(config.getString("force-locale", "")));
+
+ // Basics
+ debug = config.getBoolean("debug", false);
+ metrics = config.getBoolean("metrics", false);
+ checkUpdate = config.getBoolean("update-checker", false);
+
+ // Modules
+ actionbarModule = config.getBoolean("modules.actionbars", true);
+ imageModule = config.getBoolean("modules.images", true);
+ bossBarModule = config.getBoolean("modules.bossbars", true);
+ bubbleModule = config.getBoolean("modules.bubbles", true);
+ backgroundModule = config.getBoolean("modules.backgrounds", true);
+ nameplateModule = config.getBoolean("modules.nameplates", true);
+
+ // Integrations
+ packItemsAdder = config.getBoolean("integrations.resource-pack.ItemsAdder", false);
+ packItemsAdderLegacy = config.getBoolean("integrations.resource-pack.ItemsAdder-old-method", false);
+ if (packItemsAdder) packItemsAdderLegacy = false;
+ packOraxen = config.getBoolean("integrations.resource-pack.Oraxen", false);
+
+ chatUnsafe = config.getBoolean("other-settings.unsafe-chat-event", false);
+ chatEss = config.getBoolean("integrations.chat.Essentials", false);
+ chatCarbon = config.getBoolean("integrations.chat.CarbonChat", false);
+ chatHusk = config.getBoolean("integrations.chat.HuskChat", false);
+ chatAdvanced = config.getBoolean("integrations.chat.AdvancedChat", false);
+ chatTR = config.getBoolean("integrations.chat.TrChat", false);
+ chatVenture = config.getBoolean("integrations.chat.VentureChat", false);
+
+ // Packs
+ generateOnStart = !config.getBoolean("resource-pack.disable-generation-on-start", false);
+ namespace = config.getString("resource-pack.namespace", "nameplates");
+ font = config.getString("resource-pack.font", "default");
+ initialChar = config.getChar("resource-pack.initial-char", '뀁');
+ CharacterArranger.reset(initialChar);
+
+ nameplatePath = config.getString("resource-pack.image-path.nameplates", "font/nameplates/");
+ backgroundPath = config.getString("resource-pack.image-path.backgrounds", "font/backgrounds/");
+ imagePath = config.getString("resource-pack.image-path.images", "font/images/");
+ bubblePath = config.getString("resource-pack.image-path.bubbles", "font/bubbles/");
+ spaceSplitPath = config.getString("resource-pack.image-path.space-split", "font/base/");
+
+ enableShader = config.getBoolean("resource-pack.shader.enable", true);
+ hideScoreBoardNumber = config.getBoolean("resource-pack.shader.hide-scoreboard-number", false);
+ animatedText = config.getBoolean("resource-pack.shader.animated-text", false);
+ itemsAdderEffect = config.getBoolean("resource-pack.shader.ItemsAdder-text-effects", false);
+
+ removedBarColor = BossBar.Color.valueOf(config.getString("resource-pack.transparent-bossbar.color", "YELLOW").toUpperCase(Locale.ENGLISH));
+ bossBar1_20_2 = config.getBoolean("resource-pack.transparent-bossbar.1_20_2+", true);
+ bossBar1_17 = config.getBoolean("resource-pack.transparent-bossbar.1_17-1_20_1", true);
+
+ legacyUnicodes = config.getBoolean("resource-pack.legacy-unicodes", true);
+
+ // Other settings
+ delaySend = config.getInt("other-settings.send-delay", 0);
+ defaultRefreshInterval = config.getInt("other-settings.default-placeholder-refresh-interval", 10);
+ catchOtherActionBar = config.getBoolean("other-settings.catch-other-plugin-actionbar", true);
+ }
+
+ @Override
+ public void unload() {
+ Reloadable.super.unload();
+ }
+
+ public static int delaySend() {
+ return instance.delaySend;
+ }
+
+ public static String spaceSplitPath() {
+ return instance.spaceSplitPath;
+ }
+
+ public static String bubblePath() {
+ return instance.bubblePath;
+ }
+
+ public static String imagePath() {
+ return instance.imagePath;
+ }
+
+ public static String backgroundPath() {
+ return instance.backgroundPath;
+ }
+
+ public static String nameplatePath() {
+ return instance.nameplatePath;
+ }
+
+ public static boolean generateOnStart() {
+ return instance.generateOnStart;
+ }
+
+ public static String namespace() {
+ return instance.namespace;
+ }
+
+ public static String font() {
+ return instance.font;
+ }
+
+ public static char initialChar() {
+ return instance.initialChar;
+ }
+
+ public static boolean actionbarModule() {
+ return instance.actionbarModule;
+ }
+
+ public static boolean catchOtherActionBar() {
+ return instance.catchOtherActionBar;
+ }
+
+ public static boolean packItemsAdder() {
+ return instance.packItemsAdder;
+ }
+
+ public static boolean packItemsAdderLegacy() {
+ return instance.packItemsAdderLegacy;
+ }
+
+ public static boolean packOraxen() {
+ return instance.packOraxen;
+ }
+
+ public static boolean hideScoreBoardNumber() {
+ return instance.hideScoreBoardNumber;
+ }
+
+ public static boolean animatedText() {
+ return instance.animatedText;
+ }
+
+ public static boolean itemsAdderEffect() {
+ return instance.itemsAdderEffect;
+ }
+
+ public static BossBar.Color removedBarColor() {
+ return instance.removedBarColor;
+ }
+
+ public static boolean imageModule() {
+ return instance.imageModule;
+ }
+
+ public static boolean bossbarModule() {
+ return instance.bossBarModule;
+ }
+
+ public static boolean bubbleModule() {
+ return instance.bubbleModule;
+ }
+
+ public static boolean backgroundModule() {
+ return instance.backgroundModule;
+ }
+
+ public static boolean nameplateModule() {
+ return instance.nameplateModule;
+ }
+
+ public static boolean debug() {
+ return instance.debug;
+ }
+
+ public static boolean checkUpdate() {
+ return instance.checkUpdate;
+ }
+
+ public static boolean metrics() {
+ return instance.metrics;
+ }
+
+ public static int defaultRefreshInterval() {
+ return instance.defaultRefreshInterval;
+ }
+
+ public static boolean enableShader() {
+ return instance.enableShader;
+ }
+
+ public static boolean legacyUnicodes() {
+ return instance.legacyUnicodes;
+ }
+
+ public static boolean bossBar1_20_2() {
+ return instance.bossBar1_20_2;
+ }
+
+ public static boolean bossBar1_17() {
+ return instance.bossBar1_17;
+ }
+
+ public static boolean chatUnsafe() {
+ return instance.chatUnsafe;
+ }
+
+ public static boolean chatTrChat() {
+ return instance.chatTR;
+ }
+
+ public static boolean chatVenture() {
+ return instance.chatVenture;
+ }
+
+ public static boolean chatHusk() {
+ return instance.chatHusk;
+ }
+
+ public static boolean chatEss() {
+ return instance.chatEss;
+ }
+
+ public static boolean chatCarbon() {
+ return instance.chatCarbon;
+ }
+
+ public static boolean chatAdvanced() {
+ return instance.chatAdvanced;
+ }
+
+ @Override
+ public YamlDocument loadConfig(String filePath) {
+ return loadConfig(filePath, '.');
+ }
+
+ @Override
+ public YamlDocument loadConfig(String filePath, char routeSeparator) {
+ try (InputStream inputStream = new FileInputStream(resolveConfig(filePath).toFile())) {
+ return YamlDocument.create(
+ inputStream,
+ plugin.getResourceStream(filePath),
+ GeneralSettings.builder().setRouteSeparator(routeSeparator).build(),
+ LoaderSettings
+ .builder()
+ .setAutoUpdate(true)
+ .build(),
+ DumperSettings.builder()
+ .setScalarFormatter((tag, value, role, def) -> {
+ if (role == NodeRole.KEY) {
+ return ScalarStyle.PLAIN;
+ } else {
+ return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
+ }
+ })
+ .build(),
+ UpdaterSettings
+ .builder()
+ .setVersioning(new BasicVersioning("config-version"))
+ .build()
+ );
+ } catch (IOException e) {
+ plugin.getPluginLogger().severe("Failed to load config " + filePath, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public YamlDocument loadData(File file) {
+ try (InputStream inputStream = new FileInputStream(file)) {
+ return YamlDocument.create(inputStream);
+ } catch (IOException e) {
+ plugin.getPluginLogger().severe("Failed to load config " + file, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Path resolveConfig(String filePath) {
+ if (filePath == null || filePath.isEmpty()) {
+ throw new IllegalArgumentException("ResourcePath cannot be null or empty");
+ }
+ filePath = filePath.replace('\\', '/');
+ Path configFile = plugin.getConfigDirectory().resolve(filePath);
+ // if the config doesn't exist, create it based on the template in the resources dir
+ if (!Files.exists(configFile)) {
+ try {
+ Files.createDirectories(configFile.getParent());
+ } catch (IOException e) {
+ // ignore
+ }
+ try (InputStream is = plugin.getResourceStream(filePath)) {
+ if (is == null) {
+ throw new IllegalArgumentException("The embedded resource '" + filePath + "' cannot be found");
+ }
+ Files.copy(is, configFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return configFile;
+ }
+}
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplates.java b/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplates.java
new file mode 100644
index 0000000..0ce3f33
--- /dev/null
+++ b/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplates.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) <2024>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.momirealms.customnameplates.api;
+
+import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManager;
+import net.momirealms.customnameplates.api.feature.advance.AdvanceManager;
+import net.momirealms.customnameplates.api.feature.background.BackgroundManager;
+import net.momirealms.customnameplates.api.feature.bossbar.BossBarManager;
+import net.momirealms.customnameplates.api.feature.bubble.BubbleManager;
+import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager;
+import net.momirealms.customnameplates.api.feature.image.ImageManager;
+import net.momirealms.customnameplates.api.feature.nameplate.NameplateManager;
+import net.momirealms.customnameplates.api.feature.pack.ResourcePackManager;
+import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManager;
+import net.momirealms.customnameplates.api.network.PacketSender;
+import net.momirealms.customnameplates.api.network.PipelineInjector;
+import net.momirealms.customnameplates.api.placeholder.PlaceholderManager;
+import net.momirealms.customnameplates.api.requirement.RequirementManager;
+import net.momirealms.customnameplates.api.storage.StorageManager;
+import net.momirealms.customnameplates.common.dependency.DependencyManager;
+import net.momirealms.customnameplates.common.event.EventManager;
+import net.momirealms.customnameplates.common.locale.TranslationManager;
+import net.momirealms.customnameplates.common.plugin.NameplatesPlugin;
+import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public abstract class CustomNameplates implements NameplatesPlugin {
+
+ private static CustomNameplates instance;
+
+ protected DependencyManager dependencyManager;
+ protected TranslationManager translationManager;
+ protected Consumer> debugger = (s) -> getPluginLogger().info("[DEBUG] " + s.get());
+ protected ConfigManager configManager;
+ protected PacketSender packetSender;
+ protected PipelineInjector pipelineInjector;
+ protected PlaceholderManager placeholderManager;
+ protected RequirementManager requirementManager;
+ protected ActionBarManager actionBarManager;
+ protected BossBarManager bossBarManager;
+ protected UnlimitedTagManager unlimitedTagManager;
+ protected Platform platform;
+ protected MainTask mainTask = new MainTask(this);
+ protected SchedulerTask scheduledMainTask;
+ protected ConcurrentHashMap onlinePlayerMap = new ConcurrentHashMap<>();
+ protected HashMap entityIDFastLookup = new HashMap<>();
+ protected AdvanceManager advanceManager;
+ protected BackgroundManager backgroundManager;
+ protected EventManager eventManager;
+ protected StorageManager storageManager;
+ protected BubbleManager bubbleManager;
+ protected ChatManager chatManager;
+ protected ImageManager imageManager;
+ protected NameplateManager nameplateManager;
+ protected ResourcePackManager resourcePackManager;
+
+ protected CustomNameplates() {
+ instance = this;
+ }
+
+ @Override
+ public void reload() {
+ if (scheduledMainTask != null)
+ scheduledMainTask.cancel();
+ scheduledMainTask = getScheduler().asyncRepeating(mainTask, 50, 50, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void disable() {
+ if (this.scheduledMainTask != null) this.scheduledMainTask.cancel();
+ }
+
+ @Override
+ public DependencyManager getDependencyManager() {
+ return dependencyManager;
+ }
+
+ @Override
+ public TranslationManager getTranslationManager() {
+ return translationManager;
+ }
+
+ @Override
+ public ConfigManager getConfigManager() {
+ return configManager;
+ }
+
+ @Override
+ public void debug(Supplier supplier) {
+ this.debugger.accept(supplier);
+ }
+
+ public PlaceholderManager getPlaceholderManager() {
+ return placeholderManager;
+ }
+
+ public PacketSender getPacketSender() {
+ return packetSender;
+ }
+
+ public RequirementManager getRequirementManager() {
+ return requirementManager;
+ }
+
+ public ActionBarManager getActionBarManager() {
+ return actionBarManager;
+ }
+
+ public UnlimitedTagManager getUnlimitedTagManager() {
+ return unlimitedTagManager;
+ }
+
+ public AdvanceManager getAdvanceManager() {
+ return advanceManager;
+ }
+
+ public BackgroundManager getBackgroundManager() {
+ return backgroundManager;
+ }
+
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+ public StorageManager getStorageManager() {
+ return storageManager;
+ }
+
+ public NameplateManager getNameplateManager() {
+ return nameplateManager;
+ }
+
+ public ImageManager getImageManager() {
+ return imageManager;
+ }
+
+ public BubbleManager getBubbleManager() {
+ return bubbleManager;
+ }
+
+ public ChatManager getChatManager() {
+ return chatManager;
+ }
+
+ public ResourcePackManager getResourcePackManager() {
+ return resourcePackManager;
+ }
+
+ public Platform getPlatform() {
+ return platform;
+ }
+
+ public Collection getOnlinePlayers() {
+ return new HashSet<>(onlinePlayerMap.values());
+ }
+
+ public CNPlayer getPlayer(UUID uuid) {
+ return onlinePlayerMap.get(uuid);
+ }
+
+ public CNPlayer getPlayer(int entityID) {
+ return entityIDFastLookup.get(entityID);
+ }
+
+ public static CustomNameplates getInstance() {
+ return instance;
+ }
+}
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplatesPlugin.java b/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplatesPlugin.java
deleted file mode 100644
index 9212b3c..0000000
--- a/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplatesPlugin.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) <2022>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package net.momirealms.customnameplates.api;
-
-import net.momirealms.customnameplates.api.manager.*;
-import net.momirealms.customnameplates.api.scheduler.Scheduler;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.plugin.java.JavaPlugin;
-
-public abstract class CustomNameplatesPlugin extends JavaPlugin {
-
- protected static CustomNameplatesPlugin instance;
- protected Scheduler scheduler;
- protected StorageManager storageManager;
- protected VersionManager versionManager;
- protected AdventureManager adventureManager;
- protected RequirementManager requirementManager;
- protected BossBarManager bossBarManager;
- protected ImageManager imageManager;
- protected PlaceholderManager placeholderManager;
- protected ResourcePackManager resourcePackManager;
- protected BackGroundManager backGroundManager;
- protected TeamManager teamManager;
- protected NameplateManager nameplateManager;
- protected ActionBarManager actionBarManager;
- protected WidthManager widthManager;
- protected BubbleManager bubbleManager;
-
- protected CustomNameplatesPlugin() {
- instance = this;
- }
-
- /* Get plugin instance */
- public static CustomNameplatesPlugin getInstance() {
- return instance;
- }
-
- /* Get plugin instance */
- public static CustomNameplatesPlugin get() {
- return instance;
- }
-
- /* reload the plugin */
- public abstract void reload();
-
- /* Get the scheduler */
- public Scheduler getScheduler() {
- return scheduler;
- }
-
- /* Get the storage manager */
- public StorageManager getStorageManager() {
- return storageManager;
- }
-
- /* Get the requirement manager */
- public RequirementManager getRequirementManager() {
- return requirementManager;
- }
-
- /* Get the image manager */
- public ImageManager getImageManager() {
- return imageManager;
- }
-
- /* Get the background manager */
- public BackGroundManager getBackGroundManager() {
- return backGroundManager;
- }
-
- /* Get the resource pack manager */
- public ResourcePackManager getResourcePackManager() {
- return resourcePackManager;
- }
-
- /* Get the adventure manager */
- public AdventureManager getAdventure() {
- return adventureManager;
- }
-
- /* Get the version manager */
- public VersionManager getVersionManager() {
- return versionManager;
- }
-
- /* Get the bossbar manager */
- public BossBarManager getBossBarManager() {
- return bossBarManager;
- }
-
- /* Get the placeholder manager */
- public PlaceholderManager getPlaceholderManager() {
- return placeholderManager;
- }
-
- /* Get the team manager */
- public TeamManager getTeamManager() {
- return teamManager;
- }
-
- /* Get the actionbar manager */
- public ActionBarManager getActionBarManager() {
- return actionBarManager;
- }
-
- /* Get the nameplate manager */
- public NameplateManager getNameplateManager() {
- return nameplateManager;
- }
-
- /* Get the width manager */
- public WidthManager getWidthManager() {
- return widthManager;
- }
-
- /* Get the bubble manager */
- public BubbleManager getBubbleManager() {
- return bubbleManager;
- }
-
- /* debug get config by file name */
- public abstract YamlConfiguration getConfig(String file);
-
- /* debug message */
- public abstract void debug(String s);
-}
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/MainTask.java b/api/src/main/java/net/momirealms/customnameplates/api/MainTask.java
new file mode 100644
index 0000000..12de0f7
--- /dev/null
+++ b/api/src/main/java/net/momirealms/customnameplates/api/MainTask.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) <2024>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.momirealms.customnameplates.api;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class MainTask implements Runnable {
+
+ // The system would break if a server hasn't stopped for 3 years
+ private static int TICKS = 0;
+
+ private static final Map TIME_1 = new ConcurrentHashMap<>(2048, 1F);
+ private static final Map TIME_2 = new ConcurrentHashMap<>(2048, 1F);
+ private static final Set requestedSharedPlaceholders = Collections.synchronizedSet(new HashSet<>());
+ private int timer;
+
+ private final CustomNameplates plugin;
+
+ public MainTask(CustomNameplates plugin) {
+ this.plugin = plugin;
+ }
+
+ public static int getTicks() {
+ return TICKS;
+ }
+
+ public static boolean hasRequested(int countId) {
+ boolean result = requestedSharedPlaceholders.contains(countId);
+ if (!result) requestedSharedPlaceholders.add(countId);
+ return result;
+ }
+
+ @Override
+ public void run() {
+ TICKS++;
+ requestedSharedPlaceholders.clear();
+ long time1 = System.nanoTime();
+ plugin.actionBarManager.refreshConditions();
+ plugin.bossBarManager.onTick();
+ plugin.unlimitedTagManager.onTick();
+ long time2 = System.nanoTime();
+ plugin.placeholderManager.refreshPlaceholders();
+ long time3 = System.nanoTime();
+ plugin.actionBarManager.checkHeartBeats();
+ int diff1 = (int) (time2 - time1);
+ TIME_1.put(timer, diff1);
+ int diff2 = (int) (time3 - time2);
+ TIME_2.put(timer, diff2);
+ timer++;
+ if (timer >= 1200) timer = 0;
+ }
+
+ public static HealthyProfile getHealthyProfile() {
+ long total1 = 0;
+ long total2 = 0;
+ for (int value : TIME_1.values()) {
+ total1 += value;
+ }
+ for (int value : TIME_2.values()) {
+ total2 += value;
+ }
+ long total = total1 + total2;
+ double size = TIME_1.size();
+ double load = total / size / 50_000_000;
+ return new HealthyProfile(
+ load,
+ (long) (total / size),
+ (int) (total1 / size),
+ (int) (total2 / size)
+ );
+ }
+
+ public static class HealthyProfile {
+
+ private final double load;
+ private final long totalTimeNS;
+
+ private final int actionBarConditionNS;
+ private final int refreshPlaceholderNS;
+
+ public HealthyProfile(
+ double load,
+ long totalTimeNS,
+ int actionBarConditionNS,
+ int refreshPlaceholderNS
+ ) {
+ this.actionBarConditionNS = actionBarConditionNS;
+ this.refreshPlaceholderNS = refreshPlaceholderNS;
+ this.load = load;
+ this.totalTimeNS = totalTimeNS;
+ }
+
+ public int getActionBarConditionNS() {
+ return actionBarConditionNS;
+ }
+
+ public int getRefreshPlaceholderNS() {
+ return refreshPlaceholderNS;
+ }
+
+ public double getLoad() {
+ return load;
+ }
+
+ public long getTotalTimeNS() {
+ return totalTimeNS;
+ }
+ }
+}
diff --git a/api/src/main/java/net/momirealms/customnameplates/api/Platform.java b/api/src/main/java/net/momirealms/customnameplates/api/Platform.java
new file mode 100644
index 0000000..4e4d088
--- /dev/null
+++ b/api/src/main/java/net/momirealms/customnameplates/api/Platform.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) <2024>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.momirealms.customnameplates.api;
+
+import net.momirealms.customnameplates.api.feature.bossbar.BossBar;
+import net.momirealms.customnameplates.api.network.PacketEvent;
+import net.momirealms.customnameplates.api.placeholder.Placeholder;
+import net.momirealms.customnameplates.api.util.Alignment;
+import net.momirealms.customnameplates.api.util.Vector3;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+public interface Platform {
+
+ Object jsonToMinecraftComponent(String json);
+
+ String minecraftComponentToJson(Object component);
+
+ Object vec3(double x, double y, double z);
+
+ Placeholder registerPlatformPlaceholder(String id);
+
+ void onPacketSend(CNPlayer player, PacketEvent event);
+
+ Object setActionBarTextPacket(Object component);
+
+ Object createBossBarPacket(UUID uuid, Object component, float progress, BossBar.Overlay overlay, BossBar.Color color);
+
+ Object removeBossBarPacket(UUID uuid);
+
+ Object updateBossBarNamePacket(UUID uuid, Object component);
+
+ List