9
0
mirror of https://github.com/Xiao-MoMi/Custom-Nameplates.git synced 2025-12-19 15:09:23 +00:00
This commit is contained in:
XiaoMoMi
2024-10-05 22:42:28 +08:00
parent d8324da1d9
commit 3aa7e5c012
859 changed files with 28657 additions and 22513 deletions

View File

@@ -1,44 +1,37 @@
# This project is undergoing a major refactoring
# Custom-Nameplates # Custom-Nameplates
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Nameplates) ![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Nameplates)
![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Nameplates)
![bStats Servers](https://img.shields.io/bstats/servers/16649)
![bStats Players](https://img.shields.io/bstats/players/16649)
![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Nameplates)
[![](https://jitpack.io/v/Xiao-MoMi/Custom-Nameplates.svg)](https://jitpack.io/#Xiao-MoMi/Custom-Nameplates) [![](https://jitpack.io/v/Xiao-MoMi/Custom-Nameplates.svg)](https://jitpack.io/#Xiao-MoMi/Custom-Nameplates)
<a href="https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates" alt="GitBook"> <a href="https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates" alt="GitBook">
<img src="https://img.shields.io/badge/docs-gitbook-brightgreen" alt="Gitbook"/> <img src="https://img.shields.io/badge/docs-gitbook-brightgreen" alt="Gitbook"/>
</a> </a>
[![Scc Count Badge](https://sloc.xyz/github/Xiao-MoMi/Custom-Nameplates/?category=codes)](https://github.com/Xiao-MoMi/Custom-Nameplates/)
CustomNameplates is a Paper plugin that provides unlimited customization for nametags, bossbars, actionbars and chat bubbles. ![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Nameplates)
![bStats Servers](https://img.shields.io/bstats/servers/16649)
![bStats Players](https://img.shields.io/bstats/players/16649)
![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Nameplates)
## How to build ## How to build
### Windows
#### Command Line #### Command Line
Install JDK 17 and set the JDK installation path to JAVA_HOME as an environment variable.\ Install JDK 17 & 21. \
Start powershell and change directory to the project folder.\ Start terminal and change directory to the project folder.\
Execute ".\gradlew build" and get the jar at /target/CustomNameplates-universe-version.jar. Execute ".\gradlew build" and get the artifact under /target folder
#### IDE #### IDE
Import the project and execute gradle build action. Import the project and execute gradle build action. \
Get the artifact under /target folder
##### About Proxy
If you are using a proxy, configurate the proxy in gradle.properties. Otherwise comment the lines in gradle.properties.
## Support the developer ## Support the developer
Polymart: https://polymart.org/resource/customnameplates.2543 \ Polymart: https://polymart.org/resource/customnameplates.2543 \
Afdian: https://afdian.net/@xiaomomi Afdian: https://afdian.com/@xiaomomi
## Use CustomNameplates API ## CustomNameplates API
### Maven ### Maven
``` ```html
<repositories> <repositories>
<repository> <repository>
<id>jitpack</id> <id>jitpack</id>
@@ -46,37 +39,37 @@ Afdian: https://afdian.net/@xiaomomi
</repository> </repository>
</repositories> </repositories>
``` ```
``` ```html
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.github.Xiao-MoMi</groupId> <groupId>com.github.Xiao-MoMi</groupId>
<artifactId>Custom-Nameplates</artifactId> <artifactId>Custom-Nameplates</artifactId>
<version>{LATEST}</version> <version>{VERSION}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
### Gradle (Groovy) ### Gradle (Groovy)
``` ```groovy
repositories { repositories {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
``` ```
``` ```groovy
dependencies { dependencies {
compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}' compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{VERSION}'
} }
``` ```
### Gradle (Kotlin) ### Gradle (Kotlin)
``` ```kotlin
repositories { repositories {
maven("https://jitpack.io/") maven("https://jitpack.io/")
} }
``` ```
``` ```kotlin
dependencies { dependencies {
compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}") compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{VERSION}")
} }
``` ```

View File

@@ -1,8 +1,28 @@
plugins {
id("io.github.goooler.shadow") version "8.1.8"
}
repositories {
}
dependencies { dependencies {
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.5")
implementation(project(":common")) 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 { tasks {

Binary file not shown.

View File

@@ -0,0 +1,478 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<Integer, TickStampData<String>> cachedValues = new ConcurrentHashMap<>();
private final Map<Integer, WeakHashMap<CNPlayer, TickStampData<String>>> cachedRelationalValues = new ConcurrentHashMap<>();
/**
* 此处缓存了所有解析过的条件Relational则是缓存了与某个玩家的关系条件
*/
private final Map<Requirement, TickStampData<Boolean>> cachedRequirements = new ConcurrentHashMap<>();
private final Map<Requirement, WeakHashMap<CNPlayer, TickStampData<Boolean>>> cachedRelationalRequirements = new ConcurrentHashMap<>();
/*
* 这里维护了一个双向的Map以方便更新对应的Feature。
* 插件会先获取当前处于活跃状态的变量由Feature提供根据变量的更新情况判断是否需要反馈到对应的Feature以便只在必要的时刻进行更新
*/
private final Set<Feature> activeFeatures = new CopyOnWriteArraySet<>();
private final Map<Placeholder, Set<Feature>> placeholder2Features = new ConcurrentHashMap<>();
private final Map<Feature, Set<Placeholder>> feature2Placeholders = new ConcurrentHashMap<>();
private final Map<CNPlayer, Tracker> trackers = Collections.synchronizedMap(new WeakHashMap<>());
protected AbstractCNPlayer(CustomNameplates plugin, Object player) {
this.plugin = plugin;
this.player = player;
}
/**
* 将所有处于激活状态的变量统筹起来并返回一个更新任务
*
* @return 更新任务
*/
@Override
public List<Placeholder> getRefreshValueTask() {
Placeholder[] activePlaceholders = activePlaceholders();
List<Placeholder> placeholderWithChildren = new ArrayList<>();
for (Placeholder placeholder : activePlaceholders) {
childrenFirstList(placeholder, placeholderWithChildren);
}
List<Placeholder> uniquePlaceholderWithChildren = placeholderWithChildren.stream().distinct().toList();
List<Placeholder> 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<Placeholder> placeholders, Set<CNPlayer> others) {
if (placeholders.isEmpty()) return;
List<Placeholder> placeholderWithChildren = new ArrayList<>();
for (Placeholder placeholder : placeholders) {
childrenFirstList(placeholder, placeholderWithChildren);
}
for (Placeholder placeholder : placeholderWithChildren) {
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
TickStampData<String> 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<String> 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<String> 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<Placeholder> 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<Feature> 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<Placeholder> allPlaceholdersUsedInFeature = feature.allPlaceholders();
feature2Placeholders.put(feature, allPlaceholdersUsedInFeature);
for (Placeholder placeholder : allPlaceholdersUsedInFeature) {
Set<Feature> 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<Placeholder> placeholders = feature2Placeholders.remove(feature);
if (placeholders != null) {
for (Placeholder placeholder : placeholders) {
Set<Feature> featureSet = placeholder2Features.get(placeholder);
featureSet.remove(feature);
if (featureSet.isEmpty()) placeholder2Features.remove(placeholder);
}
}
}
@Override
public void setValue(Placeholder placeholder, TickStampData<String> value) {
cachedValues.put(placeholder.countId(), value);
}
@Override
public boolean setValue(Placeholder placeholder, String value) {
TickStampData<String> 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<String> value) {
WeakHashMap<CNPlayer, TickStampData<String>> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
map.put(another, value);
}
@Override
public boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value) {
WeakHashMap<CNPlayer, TickStampData<String>> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
TickStampData<String> 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<String> getValue(Placeholder placeholder) {
return cachedValues.get(placeholder.countId());
}
@Override
public String getRelationalData(Placeholder placeholder, CNPlayer another) {
WeakHashMap<CNPlayer, TickStampData<String>> 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<String> getRelationalValue(Placeholder placeholder, CNPlayer another) {
WeakHashMap<CNPlayer, TickStampData<String>> map = cachedRelationalValues.get(placeholder.countId());
if (map == null) {
return null;
}
return map.get(another);
}
@Override
public Placeholder[] activePlaceholders() {
HashSet<Placeholder> 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<Boolean> 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<CNPlayer, TickStampData<Boolean>> innerMap = cachedRelationalRequirements.computeIfAbsent(requirement, k -> new WeakHashMap<>());
TickStampData<Boolean> 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<CNPlayer> 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<Integer> 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();
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<Placeholder> placeholders, Set<CNPlayer> another);
Set<Feature> getUsedFeatures(Placeholder placeholder);
Object player();
double scale();
boolean isCrouching();
boolean isLoaded();
boolean isPreviewing();
boolean hasPermission(String permission);
long playerTime();
List<Placeholder> getRefreshValueTask();
Placeholder[] activePlaceholders();
boolean isOnline();
String getData(Placeholder placeholder);
TickStampData<String> getValue(Placeholder placeholder);
String getRelationalData(Placeholder placeholder, CNPlayer another);
TickStampData<String> getRelationalValue(Placeholder placeholder, CNPlayer another);
void setValue(Placeholder placeholder, TickStampData<String> value);
boolean setValue(Placeholder placeholder, String value);
void setRelationalValue(Placeholder placeholder, CNPlayer another, TickStampData<String> 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<CNPlayer> nearbyPlayers();
void trackPassengers(CNPlayer another, int... passengers);
void untrackPassengers(CNPlayer another, int... passengers);
Set<Integer> getTrackedPassengerIds(CNPlayer another);
Set<Integer> passengers();
boolean isFlying();
Tracker getTracker(CNPlayer another);
String equippedBubble();
void equippedBubble(String equippedBubble);
String equippedNameplate();
void equippedNameplate(String equippedNameplate);
int remainingAir();
}

View File

@@ -0,0 +1,447 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<Supplier<String>> 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<UUID, CNPlayer> onlinePlayerMap = new ConcurrentHashMap<>();
protected HashMap<Integer, CNPlayer> 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<String> 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<CNPlayer> 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;
}
}

View File

@@ -1,141 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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);
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<Integer, Integer> TIME_1 = new ConcurrentHashMap<>(2048, 1F);
private static final Map<Integer, Integer> TIME_2 = new ConcurrentHashMap<>(2048, 1F);
private static final Set<Integer> 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;
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
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<Object> createTextDisplayPacket(
int entityID, UUID uuid,
Vector3 position, float pitch, float yaw, double headYaw,
int interpolationDelay, int transformationInterpolationDuration, int positionRotationInterpolationDuration,
Object component, int backgroundColor, byte opacity,
boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackgroundColor, Alignment alignment,
float viewRange, float shadowRadius, float shadowStrength,
Vector3 scale, Vector3 translation, int lineWidth, boolean isCrouching
);
Consumer<List<Object>> createInterpolationDelayModifier(int delay);
Consumer<List<Object>> createTransformationInterpolationDurationModifier(int duration);
Consumer<List<Object>> createTextComponentModifier(Object component);
Consumer<List<Object>> createScaleModifier(Vector3 scale);
Consumer<List<Object>> createTranslationModifier(Vector3 translation);
Consumer<List<Object>> createOpacityModifier(byte opacity);
Object updateTextDisplayPacket(int entityID, List<Consumer<List<Object>>> modifiers);
Object setPassengersPacket(int vehicle, int[] passengers);
Object removeEntityPacket(int... entityID);
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.data;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface DataStorageInterface {
/**
* Initialize the data resource
*/
void initialize();
/**
* Close the data resource
*/
void disable();
/**
* Get the storage data source type
*
* @return {@link StorageType}
*/
StorageType getStorageType();
/**
* Get a player's data by uuid
* This player can be an offline one
*
* @param uuid uuid
* @return player data
*/
CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid);
CompletableFuture<Boolean> updatePlayerData(UUID uuid, PlayerData playerData);
CompletableFuture<Boolean> updateOrInsertPlayerData(UUID uuid, PlayerData playerData);
Set<UUID> getUniqueUsers(boolean legacy);
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.data;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface LegacyDataStorageInterface extends DataStorageInterface {
/**
* Retrieve legacy player data from the SQL database.
*
* @param uuid The UUID of the player.
* @return A CompletableFuture containing the optional legacy player data.
*/
CompletableFuture<Optional<PlayerData>> getLegacyPlayerData(UUID uuid);
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.data;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.mechanic.bubble.Bubble;
import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class OnlineUser {
private final Player player;
private String nameplate;
private String bubble;
public OnlineUser(Player player, PlayerData playerData) {
this.player = player;
this.nameplate = playerData.getNameplate();
this.bubble = playerData.getBubble();
}
public PlayerData toPlayerData() {
return PlayerData.builder()
.setBubble(bubble)
.setNameplate(nameplate)
.build();
}
public UUID getUUID() {
return player.getUniqueId();
}
public Player getPlayer() {
return player;
}
/**
* Get the original nameplate key from data
*/
@NotNull
public String getNameplateKey() {
return nameplate;
}
/**
* Get the original bubble key from data
*/
@NotNull
public String getBubbleKey() {
return bubble;
}
/**
* This value might be inconsistent with the key get by "getNameplateKey()"
* Because if a player doesn't have a nameplate, his nameplate would be the default one
*/
@Nullable
public Nameplate getNameplate() {
String temp = nameplate;
if (temp.equals("none")) {
temp = CustomNameplatesPlugin.get().getNameplateManager().getDefaultNameplate();
}
return CustomNameplatesPlugin.get().getNameplateManager().getNameplate(temp);
}
/**
* This value might be inconsistent with the key get by "getBubbleKey()"
* Because if a player doesn't have a bubble, his bubble would be the default one
*/
@Nullable
public Bubble getBubble() {
String temp = nameplate;
if (temp.equals("none")) {
temp = CustomNameplatesPlugin.get().getBubbleManager().getDefaultBubble();
}
return CustomNameplatesPlugin.get().getBubbleManager().getBubble(temp);
}
/**
* Set nameplate for a player
*
* @param nameplate nameplate
*/
public void setNameplate(String nameplate) {
this.nameplate = nameplate;
}
/**
* Set bubble for a player
*
* @param bubble bubble
*/
public void setBubble(String bubble) {
this.bubble = bubble;
}
}

View File

@@ -1,75 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.data;
import com.google.gson.annotations.SerializedName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerData {
@SerializedName("nameplate")
private String nameplate;
@SerializedName("bubble")
private String bubble;
public static Builder builder() {
return new Builder();
}
public static PlayerData empty() {
return new PlayerData.Builder()
.setNameplate("none")
.setBubble("none")
.build();
}
public String getNameplate() {
return nameplate;
}
public String getBubble() {
return bubble;
}
public static class Builder {
private final PlayerData playerData;
public Builder() {
this.playerData = new PlayerData();
}
@NotNull
public Builder setNameplate(@Nullable String nameplate) {
this.playerData.nameplate = nameplate;
return this;
}
@NotNull
public Builder setBubble(@Nullable String bubble) {
this.playerData.bubble = bubble;
return this;
}
@NotNull
public PlayerData build() {
return this.playerData;
}
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* An event triggered when player sends a message
*/
public class BubblesSpawnEvent extends PlayerEvent implements Cancellable {
private boolean cancelled;
private String bubble;
private String text;
private static final HandlerList handlerList = new HandlerList();
public BubblesSpawnEvent(@NotNull Player who, String bubble, String text) {
super(who, true);
this.cancelled = false;
this.bubble = bubble;
this.text = text;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
public static HandlerList getHandlerList() {
return handlerList;
}
@NotNull
@Override
public HandlerList getHandlers() {
return getHandlerList();
}
/**
* Get the bubble's key
*/
public String getBubble() {
return bubble;
}
/**
* Set the bubble
*
* @param bubble bubble's key
*/
public void setBubble(String bubble) {
this.bubble = bubble;
}
/**
* Get the bubble text content
*/
public String getText() {
return text;
}
/**
* Set the bubble text content
*
* @param text text
*/
public void setText(String text) {
this.text = text;
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.event;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* Plugin reload event
*/
public class CustomNameplatesReloadEvent extends Event {
private static final HandlerList handlerList = new HandlerList();
private final CustomNameplatesPlugin plugin;
public CustomNameplatesReloadEvent(CustomNameplatesPlugin plugin) {
this.plugin = plugin;
}
public static HandlerList getHandlerList() {
return handlerList;
}
@NotNull
@Override
public HandlerList getHandlers() {
return getHandlerList();
}
public CustomNameplatesPlugin getPlugin() {
return plugin;
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,17 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.api.mechanic.tag; package net.momirealms.customnameplates.api.event;
import org.bukkit.entity.Player; import net.momirealms.customnameplates.api.storage.data.PlayerData;
import net.momirealms.customnameplates.common.event.Param;
public interface NameplatePlayer { public interface DataLoadEvent {
void setPreview(boolean preview); @Param(0)
PlayerData data();
boolean isPreviewing();
Player getPlayer();
void updateText();
} }

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.event;
import net.momirealms.customnameplates.api.data.OnlineUser;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Player data loaded event
*/
public class NameplateDataLoadEvent extends Event {
private static final HandlerList handlerList = new HandlerList();
private final UUID uuid;
private final OnlineUser onlineUser;
public NameplateDataLoadEvent(UUID uuid, OnlineUser onlineUser) {
super(true);
this.uuid = uuid;
this.onlineUser = onlineUser;
}
/**
* Get the UUID
*
* @return uuid
*/
public UUID getUUID() {
return uuid;
}
/**
* Get the online user
*
* @return online user
*/
public OnlineUser getOnlineUser() {
return onlineUser;
}
public static HandlerList getHandlerList() {
return handlerList;
}
@NotNull
@Override
public HandlerList getHandlers() {
return getHandlerList();
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,11 +15,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.common.message; package net.momirealms.customnameplates.api.event;
public class MessageType { import net.momirealms.customnameplates.common.event.Cancellable;
import net.momirealms.customnameplates.common.event.NameplatesEvent;
public static final String CREATE = "0"; public interface NameplatesReloadEvent extends NameplatesEvent, Cancellable {
public static final String REMOVE = "1";
public static final String UPDATE = "2";
} }

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
public interface AdaptiveImage {
String createImagePrefix(float advance, float leftMargin, float rightMargin);
String createImageSuffix(float advance, float leftMargin, float rightMargin);
String createImage(float advance, float leftMargin, float rightMargin);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.requirement.Requirement;
public class CarouselText {
private final int duration;
private final Requirement[] requirements;
private final String rawText;
private final PreParsedDynamicText preParsedDynamicText;
private final boolean updateOnDisplay;
public CarouselText(int duration, Requirement[] requirements, String rawText, boolean updateOnDisplay) {
this.duration = duration;
this.requirements = requirements;
this.rawText = rawText;
this.preParsedDynamicText = new PreParsedDynamicText(rawText);
this.preParsedDynamicText.init();
this.updateOnDisplay = updateOnDisplay;
}
public int duration() {
return duration;
}
public Requirement[] requirements() {
return requirements;
}
public String text() {
return rawText;
}
public PreParsedDynamicText preParsedDynamicText() {
return preParsedDynamicText;
}
public boolean updateOnDisplay() {
return updateOnDisplay;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
public interface ChatListener {
void onPlayerChat(CNPlayer player, String message, String channel);
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.pack.CharacterArranger;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class ConfiguredCharacter {
private final char character;
private final File imageFile;
private final int height;
private final int ascent;
private final float advance;
public static ConfiguredCharacter create(File imageFile, int ascent, int height) {
return new ConfiguredCharacter(CharacterArranger.getAndIncrease(), imageFile, ascent, height);
}
public static ConfiguredCharacter create(File imageFile, int ascent, int height, float advance) {
return new ConfiguredCharacter(CharacterArranger.getAndIncrease(), imageFile, ascent, height, advance);
}
public ConfiguredCharacter(char character, File imageFile, int ascent, int height, float advance) {
this.character = character;
this.imageFile = imageFile;
this.ascent = ascent;
this.height = height;
this.advance = advance;
}
public ConfiguredCharacter(char character, File imageFile, int ascent, int height) {
this.character = character;
this.imageFile = imageFile;
this.ascent = ascent;
this.height = height;
if (this.ascent > this.height) {
CustomNameplates.getInstance().getPluginLogger().severe(String.format("Found an issue in the image config. Ascent(%s) is higher than height(%s). " + imageFile.getAbsolutePath(), ascent, height));
}
try {
BufferedImage bufferedImage = ImageIO.read(imageFile);
int imageHeight = bufferedImage.getHeight();
int imageWidth = bufferedImage.getWidth();
float scale = (float) height / (float) imageHeight;
int i;
outer:
for (i = imageWidth - 1; i >= 0; --i) {
for (int k = 0; k < imageHeight; ++k) {
int rgb = bufferedImage.getRGB(i, k);
int alpha = (rgb >> 24) & 0xff;
if (alpha != 0) {
break outer;
}
}
}
advance = (int) (0.5 + (double) ((i+1f) * scale)) + 1f;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public char character() {
return character;
}
public int ascent() {
return ascent;
}
public float advance() {
return advance;
}
public int height() {
return height;
}
public File imageFile() {
return imageFile;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
public class DynamicText {
private final String text;
private final List<Function<CNPlayer, String>> texts;
private final Set<Placeholder> placeholders;
public DynamicText(String text, List<Function<CNPlayer, String>> texts, Set<Placeholder> placeholders) {
this.text = text;
this.texts = texts;
this.placeholders = placeholders;
}
public Set<Placeholder> placeholders() {
return placeholders;
}
public String render(CNPlayer viewer) {
StringBuilder builder = new StringBuilder();
for (Function<CNPlayer, String> function : texts) {
builder.append(function.apply(viewer));
}
return builder.toString();
}
public String text() {
return text;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import java.util.Set;
public interface Feature {
String name();
Set<Placeholder> activePlaceholders();
Set<Placeholder> allPlaceholders();
void notifyPlaceholderUpdates(CNPlayer p1, boolean force);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
public interface JoinQuitListener {
void onPlayerJoin(CNPlayer player);
void onPlayerQuit(CNPlayer player);
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import java.util.HashMap;
public enum OffsetFont {
NEG_1('\uf800', -1, -3),
NEG_2('\uf801', -2, -4),
NEG_3('\uf802', -3, -5),
NEG_4('\uf803', -4, -6),
NEG_5('\uf804', -5, -7),
NEG_6('\uf805', -6, -8),
NEG_7('\uf806', -7, -9),
NEG_8('\uf807', -8, -10),
NEG_9('\uf808', -9, -11),
NEG_10('\uf809', -10, -12),
NEG_11('\uf80a', -11, -13),
NEG_12('\uf80b', -12, -14),
NEG_13('\uf80c', -13, -15),
NEG_14('\uf80d', -14, -16),
NEG_15('\uf80e', -15, -17),
NEG_16('\uf80f', -16, -18),
NEG_24('\uf810', -24, -26),
NEG_32('\uf811', -32, -34),
NEG_48('\uf812', -48, -50),
NEG_64('\uf813', -64, -66),
NEG_128('\uf814', -128, -130),
NEG_256('\uf815', -256, -258),
POS_1('\uf830', 1, -1),
POS_2('\uf831', 2, 1),
POS_3('\uf832', 3, 2),
POS_4('\uf833', 4, 3),
POS_5('\uf834', 5, 4),
POS_6('\uf835', 6, 5),
POS_7('\uf836', 7, 6),
POS_8('\uf837', 8, 7),
POS_9('\uf838', 9, 8),
POS_10('\uf839', 10, 9),
POS_11('\uf83a', 11, 10),
POS_12('\uf83b', 12, 11),
POS_13('\uf83c', 13, 12),
POS_14('\uf83d', 14, 13),
POS_15('\uf83e', 15, 14),
POS_16('\uf83f', 16, 15),
POS_24('\uf840', 24, 23),
POS_32('\uf841', 32, 31),
POS_48('\uf842', 48, 47),
POS_64('\uf843', 64, 63),
POS_128('\uf844', 128, 127),
POS_256('\uf845', 256, 255);
private static final HashMap<Integer, OffsetFont> negFastLookup = new HashMap<>();
private static final HashMap<Integer, OffsetFont> posFastLookup = new HashMap<>();
static {
for (OffsetFont f : OffsetFont.values()) {
if (f.advance > 0) {
posFastLookup.put(f.advance, f);
} else {
negFastLookup.put(-f.advance, f);
}
}
}
private final char character;
private final int advance;
private final int height;
OffsetFont(char character, int advance, int height) {
this.character = character;
this.advance = advance;
this.height = height;
}
public char character() {
return this.character;
}
public float advance() {
return this.advance;
}
public int height() {
return this.height;
}
public static String createOffsets(float offset) {
if (offset >= 0) {
return shortestPosChars(offset);
} else {
return shortestNegChars(-offset);
}
}
public static String shortestNegChars(float advance) {
int n = (int) Math.ceil(advance);
StringBuilder stringBuilder = new StringBuilder();
while (n >= 256) {
stringBuilder.append(OffsetFont.NEG_256.character());
n -= 256;
}
if (n - 128 >= 0) {
stringBuilder.append(OffsetFont.NEG_128.character());
n -= 128;
}
if (n - 64 >= 0) {
stringBuilder.append(OffsetFont.NEG_64.character());
n -= 64;
}
if (n - 48 >= 0) {
stringBuilder.append(OffsetFont.NEG_48.character());
n -= 48;
}
if (n - 32 >= 0) {
stringBuilder.append(OffsetFont.NEG_32.character());
n -= 32;
}
if (n - 24 >= 0) {
stringBuilder.append(OffsetFont.NEG_24.character());
n -= 24;
}
if (n - 16 >= 0) {
stringBuilder.append(OffsetFont.NEG_16.character());
n -= 16;
}
if (n == 0) return stringBuilder.toString();
stringBuilder.append(negFastLookup.get(n).character());
return stringBuilder.toString();
}
public static String shortestPosChars(float advance) {
int n = (int) Math.ceil(advance);
StringBuilder stringBuilder = new StringBuilder();
while (n >= 256) {
stringBuilder.append(OffsetFont.POS_256.character());
n -= 256;
}
if (n - 128 >= 0) {
stringBuilder.append(OffsetFont.POS_128.character());
n -= 128;
}
if (n - 64 >= 0) {
stringBuilder.append(OffsetFont.POS_64.character());
n -= 64;
}
if (n - 48 >= 0) {
stringBuilder.append(OffsetFont.POS_48.character());
n -= 48;
}
if (n - 32 >= 0) {
stringBuilder.append(OffsetFont.POS_32.character());
n -= 32;
}
if (n - 24 >= 0) {
stringBuilder.append(OffsetFont.POS_24.character());
n -= 24;
}
if (n - 16 >= 0) {
stringBuilder.append(OffsetFont.POS_16.character());
n -= 16;
}
if (n == 0) return stringBuilder.toString();
stringBuilder.append(posFastLookup.get(n).character());
return stringBuilder.toString();
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.placeholder.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
public class PreParsedDynamicText {
private final String text;
private final List<Function<CNPlayer, Function<CNPlayer, String>>> textFunctions = new ArrayList<>();
private final Set<Placeholder> set = new HashSet<>();
private boolean init = false;
public PreParsedDynamicText(String text) {
this.text = text;
}
public PreParsedDynamicText(String text, boolean init) {
this.text = text;
if (init) init();
}
public void init() {
if (init) return;
init = true;
PlaceholderManager manager = CustomNameplates.getInstance().getPlaceholderManager();
List<String> detectedPlaceholders = manager.detectPlaceholders(text);
List<Function<CNPlayer, Function<CNPlayer, String>>> convertor = new ArrayList<>(detectedPlaceholders.size());
List<Placeholder> placeholders = new ArrayList<>(detectedPlaceholders.size());
for (String id : detectedPlaceholders) {
Placeholder placeholder = manager.getPlaceholder(id);
placeholders.add(placeholder);
if (placeholder instanceof RelationalPlaceholder) {
convertor.add((owner) -> (viewer) -> owner.getRelationalData(placeholder, viewer));
} else if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
convertor.add((owner) -> (viewer) -> {
if (owner != null) {
return owner.getData(placeholder);
} else {
return playerPlaceholder.request(null);
}
});
} else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) {
convertor.add((owner) -> (viewer) -> sharedPlaceholder.getLatestValue());
} else {
convertor.add((owner) -> (viewer) -> id);
}
}
int placeholderSize = placeholders.size();
StringBuilder original0 = new StringBuilder(text);
int lastIndex = 0;
for (int i = 0; i < placeholderSize; i++) {
String id = placeholders.get(i).id();
int index = original0.indexOf(id, lastIndex);
if (index == -1) {
throw new RuntimeException("Placeholder ID not found in text");
}
if (index != lastIndex) {
String textBefore = original0.substring(lastIndex, index);
textFunctions.add((owner) -> (viewer) -> textBefore);
}
textFunctions.add(convertor.get(i));
lastIndex = index + id.length();
}
if (lastIndex < original0.length()) {
String remaining = original0.substring(lastIndex);
textFunctions.add((owner) -> (viewer) -> remaining);
}
// To optimize the tree height, call new HashSet twice here
set.addAll(new HashSet<>(placeholders));
}
public DynamicText fastCreate(CNPlayer player) {
List<Function<CNPlayer, String>> functions = new ArrayList<>();
for (Function<CNPlayer, Function<CNPlayer, String>> textFunction : textFunctions) {
functions.add(textFunction.apply(player));
}
return new DynamicText(text, functions, set);
}
public Set<Placeholder> placeholders() {
return set;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
PreParsedDynamicText that = (PreParsedDynamicText) object;
return text.equals(that.text);
}
@Override
public int hashCode() {
return text.hashCode();
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
public interface RelationalFeature extends Feature {
void notifyPlaceholderUpdates(CNPlayer p1, CNPlayer p2, boolean force);
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.MainTask;
public class TickStampData<D> {
private D data;
private int packedTicks;
public TickStampData(D data, int ticks, boolean changed) {
this.data = data;
this.packedTicks = packBooleanAndTicks(changed, ticks);
}
public void data(D data) {
this.data = data;
}
public void updateTicks(boolean changed) {
this.packedTicks = packBooleanAndTicks(changed, MainTask.getTicks());
}
public D data() {
return data;
}
public boolean hasValueChanged() {
return unpackBoolean(packedTicks);
}
public int ticks() {
return unpackTicks(packedTicks);
}
public static int packBooleanAndTicks(boolean hasValueChanged, int ticks) {
int numberMasked = ticks & 0x7FFFFFFF;
int booleanBit = hasValueChanged ? 1 << 31 : 0;
return booleanBit | numberMasked;
}
public static int unpackTicks(int packed) {
return packed & 0x7FFFFFFF;
}
public static boolean unpackBoolean(int packed) {
return (packed & 0x80000000) != 0;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.actionbar;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.requirement.Requirement;
public interface ActionBarConfig {
String id();
Requirement[] requirements();
CarouselText[] carouselTexts();
static Builder builder() {
return new ActionBarConfigImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder requirement(Requirement[] requirements);
Builder carouselText(CarouselText[] carouselTexts);
ActionBarConfig build();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.actionbar;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.requirement.Requirement;
import static java.util.Objects.requireNonNull;
public class ActionBarConfigImpl implements ActionBarConfig {
private final String id;
private final Requirement[] requirements;
private final CarouselText[] carouselTexts;
public ActionBarConfigImpl(String id, Requirement[] requirements, CarouselText[] carouselTexts) {
this.id = requireNonNull(id);
this.requirements = requireNonNull(requirements);
this.carouselTexts = requireNonNull(carouselTexts);
}
@Override
public String id() {
return id;
}
@Override
public Requirement[] requirements() {
return requirements;
}
@Override
public CarouselText[] carouselTexts() {
return carouselTexts;
}
public static class BuilderImpl implements Builder {
private String id;
private Requirement[] requirements;
private CarouselText[] carouselTexts;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder requirement(Requirement[] requirements) {
this.requirements = requirements;
return this;
}
@Override
public Builder carouselText(CarouselText[] carouselTexts) {
this.carouselTexts = carouselTexts;
return this;
}
@Override
public ActionBarConfig build() {
return new ActionBarConfigImpl(id, requirements, carouselTexts);
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.actionbar;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
public interface ActionBarManager extends Reloadable {
void refreshConditions();
void checkHeartBeats();
ActionBarConfig getConfig(String name);
ActionBarConfig[] allConfigs();
String getExternalActionBar(CNPlayer player);
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.actionbar;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class ActionBarManagerImpl implements ActionBarManager, JoinQuitListener {
protected final CustomNameplates plugin;
private final LinkedHashMap<String, ActionBarConfig> configs = new LinkedHashMap<>();
private final ConcurrentHashMap<UUID, ActionBarSender> senders = new ConcurrentHashMap<>();
private ActionBarConfig[] configArray = new ActionBarConfig[0];
public ActionBarManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void load() {
// ignore disabled modules
if (!ConfigManager.actionbarModule()) return;
this.loadConfig();
this.resetArray();
for (CNPlayer online : plugin.getOnlinePlayers()) {
onPlayerJoin(online);
}
}
@Override
public void unload() {
for (ActionBarSender sender : senders.values()) {
sender.destroy();
}
this.senders.clear();
this.configs.clear();
this.resetArray();
}
@Override
public void refreshConditions() {
for (ActionBarSender sender : senders.values()) {
sender.onConditionTimerCheck();
}
}
@Override
public void checkHeartBeats() {
for (ActionBarSender sender : senders.values()) {
sender.onHeartBeatTimer();
}
}
private void resetArray() {
configArray = configs.values().toArray(new ActionBarConfig[0]);
}
@Override
public ActionBarConfig getConfig(String name) {
return configs.get(name);
}
@Override
public ActionBarConfig[] allConfigs() {
return configArray;
}
public void handleActionBarPacket(CNPlayer player, String miniMessage) {
ActionBarSender sender = senders.get(player.uuid());
if (sender != null) {
sender.externalActionBar(miniMessage);
}
}
@Override
public String getExternalActionBar(CNPlayer player) {
ActionBarSender sender = senders.get(player.uuid());
if (sender != null) {
return sender.externalActionBar();
}
return "";
}
@Override
public void onPlayerJoin(CNPlayer player) {
if (!ConfigManager.actionbarModule()) return;
plugin.getScheduler().asyncLater(() -> {
if (!player.isOnline()) return;
ActionBarSender sender = new ActionBarSender(this, player);
ActionBarSender previous = senders.put(player.uuid(), sender);
if (previous != null) {
previous.destroy();
}
}, ConfigManager.delaySend() * 50L, TimeUnit.MILLISECONDS);
}
@Override
public void onPlayerQuit(CNPlayer player) {
ActionBarSender sender = senders.remove(player.uuid());
if (sender != null) {
sender.destroy();
}
}
private void loadConfig() {
plugin.getConfigManager().saveResource("configs" + File.separator + "actionbar.yml");
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "actionbar.yml"));
for (Map.Entry<String, Object> entry : document.getStringRouteMappedValues(false).entrySet()) {
if (!(entry.getValue() instanceof Section section))
return;
this.configs.put(entry.getKey(),
ActionBarConfig.builder()
.id(entry.getKey())
.requirement(plugin.getRequirementManager().parseRequirements(section.getSection("conditions")))
.carouselText(
section.contains("text") ?
new CarouselText[]{new CarouselText(-1, new Requirement[0], section.getString("text"), false)} :
ConfigUtils.carouselTexts(section.getSection("text-display-order"))
)
.build()
);
}
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.actionbar;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.feature.DynamicText;
import net.momirealms.customnameplates.api.feature.Feature;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static java.util.Objects.requireNonNull;
public class ActionBarSender implements Feature {
private final ActionBarManager manager;
private final CNPlayer owner;
private long lastUpdateTime;
private ActionBarConfig currentConfig;
private int order;
private int timeLeft;
private DynamicText currentActionBar;
private String externalActionBar;
private long externalExpireTime;
private String latestContent;
private boolean textChangeFlag = false;
@Override
public String name() {
return "ActionBarSender";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActionBarSender sender = (ActionBarSender) o;
return owner == sender.owner;
}
@Override
public int hashCode() {
return owner.name().hashCode();
}
public ActionBarSender(ActionBarManager manager, CNPlayer owner) {
this.owner = owner;
this.manager = manager;
this.owner.addFeature(this);
this.onConditionTimerCheck();
}
public void onConditionTimerCheck() {
ActionBarConfig[] configs = manager.allConfigs();
outer: {
for (ActionBarConfig config : configs) {
if (owner.isMet(config.requirements())) {
if (config != currentConfig) {
currentConfig = config;
order = config.carouselTexts().length - 1;
timeLeft = 0;
}
break outer;
}
}
// no actionbar available
currentConfig = null;
currentActionBar = null;
latestContent = null;
}
if (currentConfig != null) {
if (timeLeft > 0)
timeLeft--;
if (timeLeft == 0) {
int triedTimes = 0;
do {
if (triedTimes == currentConfig.carouselTexts().length) {
timeLeft = 20;
latestContent = null;
CustomNameplates.getInstance().getPluginLogger().warn("No text in order is available for player " + owner.name() + ". Please check your actionbar's conditions.");
return;
}
order++;
if (order >= currentConfig.carouselTexts().length) {
order = 0;
}
triedTimes++;
} while (
!owner.isMet(currentConfig.carouselTexts()[order].requirements())
);
CarouselText carouselText = currentConfig.carouselTexts()[order];
timeLeft = carouselText.duration();
currentActionBar = carouselText.preParsedDynamicText().fastCreate(owner);
if (carouselText.updateOnDisplay()) {
owner.forceUpdate(currentActionBar.placeholders(), Collections.emptySet());
}
textChangeFlag = true;
}
}
}
public void onHeartBeatTimer() {
if (textChangeFlag) {
refresh();
sendLatestActionBar();
return;
}
if (shouldSendBeatPacket()) {
sendLatestActionBar();
}
}
public void refresh() {
latestContent = this.currentActionBar.render(owner);
textChangeFlag = false;
}
public void sendLatestActionBar() {
if (latestContent != null) {
updateLastUpdateTime();
Object packet = CustomNameplates.getInstance().getPlatform().setActionBarTextPacket(AdventureHelper.miniMessageToMinecraftComponent(latestContent, "np", "ab"));
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
}
}
public void destroy() {
this.owner.removeFeature(this);
}
@Override
public Set<Placeholder> activePlaceholders() {
if (currentActionBar == null) return Collections.emptySet();
return currentActionBar.placeholders();
}
@Override
public Set<Placeholder> allPlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
for (ActionBarConfig config : manager.allConfigs()) {
for (CarouselText text : config.carouselTexts()) {
placeholders.addAll(text.preParsedDynamicText().placeholders());
}
}
return placeholders;
}
@Override
public void notifyPlaceholderUpdates(CNPlayer player, boolean force) {
refresh();
sendLatestActionBar();
}
public void updateLastUpdateTime() {
lastUpdateTime = System.currentTimeMillis();
}
public boolean shouldSendBeatPacket() {
return System.currentTimeMillis() - lastUpdateTime > 1500;
}
@Nullable
public String externalActionBar() {
if (System.currentTimeMillis() > externalExpireTime) {
externalActionBar = "";
}
return externalActionBar;
}
public void externalActionBar(@NotNull String externalActionBar) {
requireNonNull(externalActionBar);
this.externalActionBar = externalActionBar;
this.externalExpireTime = System.currentTimeMillis() + 3000;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.advance;
import net.kyori.adventure.key.Key;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
import net.momirealms.customnameplates.common.util.Tuple;
import java.util.List;
public interface AdvanceManager extends Reloadable {
void loadTemplates();
ConfigurableFontAdvanceData getCustomFontData(String id);
CharacterFontAdvanceData getCharacterFontData(String id);
float getLineAdvance(String text);
float getCharAdvance(char[] chars, Key font, boolean bold);
int getLines(String text, int width);
List<Tuple<String, Key, Boolean>> miniMessageToIterable(String text);
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.advance;
import com.google.gson.JsonObject;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public interface CharacterFontAdvanceData {
int size();
Float getAdvance(int codePoint);
Map<Integer, Float> data();
String id();
List<JsonObject> fontProvider(Map<String, Object> properties);
static Builder builder() {
return new CharacterFontAdvanceDataImpl.BuilderImpl();
}
void close();
interface Builder {
Builder advance(Map<Integer, Float> data);
Builder id(String id);
Builder fontProviderFunction(Function<Map<String, Object>, List<JsonObject>> function);
CharacterFontAdvanceData build();
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.advance;
import com.google.gson.JsonObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.Objects.requireNonNull;
public class CharacterFontAdvanceDataImpl implements CharacterFontAdvanceData {
private final HashMap<Integer, Float> data;
private final String id;
private final Function<Map<String, Object>, List<JsonObject>> fontProviderFunction;
public CharacterFontAdvanceDataImpl(String id, HashMap<Integer, Float> data, Function<Map<String, Object>, List<JsonObject>> fontProviderFunction) {
this.data = data;
this.id = requireNonNull(id);
this.fontProviderFunction = requireNonNull(fontProviderFunction);
}
@Override
public int size() {
return data.size();
}
@Override
public Float getAdvance(int codePoint) {
return data.get(codePoint);
}
@Override
public Map<Integer, Float> data() {
return data;
}
@Override
public String id() {
return id;
}
@Override
public List<JsonObject> fontProvider(Map<String, Object> properties) {
return fontProviderFunction.apply(properties);
}
@Override
public void close() {
data.clear();
}
public static class BuilderImpl implements Builder {
private final HashMap<Integer, Float> data = new HashMap<>();
private String id;
private Function<Map<String, Object>, List<JsonObject>> fontProviderFunction = (stringObjectMap -> null);
@Override
public Builder advance(Map<Integer, Float> data) {
this.data.putAll(data);
return this;
}
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder fontProviderFunction(Function<Map<String, Object>, List<JsonObject>> function) {
this.fontProviderFunction = function;
return this;
}
@Override
public CharacterFontAdvanceData build() {
return new CharacterFontAdvanceDataImpl(id, data, fontProviderFunction);
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,19 +15,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.paper.mechanic.requirement.papi; package net.momirealms.customnameplates.api.feature.advance;
import org.bukkit.OfflinePlayer;
import java.util.List; import java.util.List;
public record ExpressionOr(List<PapiRequirement> requirements) implements PapiRequirement{ public interface ConfigurableFontAdvanceData {
@Override float defaultAdvance();
public boolean isMet(OfflinePlayer player) {
for (PapiRequirement requirement : requirements) { float getAdvance(int codePoint);
if (requirement.isMet(player)) return true;
String id();
static Builder builder() {
return new ConfigurableFontAdvanceDataImpl.BuilderImpl();
} }
return false;
interface Builder {
Builder id(String id);
Builder defaultAdvance(float width);
Builder advance(int codePoint, float width);
Builder parentFont(List<CharacterFontAdvanceData> font);
ConfigurableFontAdvanceData build();
} }
} }

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.advance;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import static java.util.Objects.requireNonNull;
public class ConfigurableFontAdvanceDataImpl implements ConfigurableFontAdvanceData {
private final float defaultAdvance;
private final HashMap<Integer, Float> data = new HashMap<>();
private final List<CharacterFontAdvanceData> parents = new ArrayList<>();
private final String id;
public ConfigurableFontAdvanceDataImpl(String id, float defaultAdvance, HashMap<Integer, Float> customData, List<CharacterFontAdvanceData> parentFonts) {
this.id = requireNonNull(id);
this.defaultAdvance = defaultAdvance;
for (CharacterFontAdvanceData parent : parentFonts) {
// To optimize memory, especially for those fonts that have over 100,000 characters
if (parent.size() > 2048) {
parents.add(parent);
} else {
data.putAll(parent.data());
}
}
data.putAll(customData);
Collections.reverse(parents);
}
@Override
public float defaultAdvance() {
return defaultAdvance;
}
@Override
public float getAdvance(int codePoint) {
Float width = data.get(codePoint);
if (width == null) {
for (CharacterFontAdvanceData parent : parents) {
width = parent.getAdvance(codePoint);
if (width != null) {
return width;
}
}
return defaultAdvance;
}
return width;
}
@Override
public String id() {
return id;
}
public static class BuilderImpl implements Builder {
private final HashMap<Integer, Float> customData = new HashMap<>();
private final List<CharacterFontAdvanceData> parents = new ArrayList<>();
private float defaultAdvance = 0;
private String id;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder defaultAdvance(float width) {
this.defaultAdvance = width;
return this;
}
@Override
public Builder advance(int codePoint, float width) {
this.customData.put(codePoint, width);
return this;
}
@Override
public Builder parentFont(List<CharacterFontAdvanceData> font) {
this.parents.addAll(font);
return this;
}
@Override
public ConfigurableFontAdvanceData build() {
return new ConfigurableFontAdvanceDataImpl(id, defaultAdvance, customData, parents);
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,11 +15,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.paper.mechanic.bubble.image; package net.momirealms.customnameplates.api.feature.advance;
import org.bukkit.entity.Player; public record SizeOverrides(int startCodePoint, int endCodePoint, int left, int right) {
public interface ImageParser {
String parse(Player player, String text);
} }

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.background;
import net.momirealms.customnameplates.api.feature.AdaptiveImage;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
public interface Background extends AdaptiveImage {
String id();
ConfiguredCharacter left();
ConfiguredCharacter width_1();
ConfiguredCharacter width_2();
ConfiguredCharacter width_4();
ConfiguredCharacter width_8();
ConfiguredCharacter width_16();
ConfiguredCharacter width_32();
ConfiguredCharacter width_64();
ConfiguredCharacter width_128();
ConfiguredCharacter right();
static Builder builder() {
return new BackgroundImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder left(ConfiguredCharacter character);
Builder width_1(ConfiguredCharacter character);
Builder width_2(ConfiguredCharacter character);
Builder width_4(ConfiguredCharacter character);
Builder width_8(ConfiguredCharacter character);
Builder width_16(ConfiguredCharacter character);
Builder width_32(ConfiguredCharacter character);
Builder width_64(ConfiguredCharacter character);
Builder width_128(ConfiguredCharacter character);
Builder right(ConfiguredCharacter character);
Background build();
}
}

View File

@@ -0,0 +1,324 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.background;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.feature.OffsetFont;
import java.util.Objects;
@SuppressWarnings("DuplicatedCode")
public class BackgroundImpl implements Background {
private final String id;
private final ConfiguredCharacter left;
private final ConfiguredCharacter width_1;
private final ConfiguredCharacter width_2;
private final ConfiguredCharacter width_4;
private final ConfiguredCharacter width_8;
private final ConfiguredCharacter width_16;
private final ConfiguredCharacter width_32;
private final ConfiguredCharacter width_64;
private final ConfiguredCharacter width_128;
private final ConfiguredCharacter right;
public BackgroundImpl(
String id,
ConfiguredCharacter left,
ConfiguredCharacter width_1,
ConfiguredCharacter width_2,
ConfiguredCharacter width_4,
ConfiguredCharacter width_8,
ConfiguredCharacter width_16,
ConfiguredCharacter width_32,
ConfiguredCharacter width_64,
ConfiguredCharacter width_128,
ConfiguredCharacter right
) {
this.id = id;
this.left = left;
this.width_1 = width_1;
this.width_2 = width_2;
this.width_4 = width_4;
this.width_8 = width_8;
this.width_16 = width_16;
this.width_32 = width_32;
this.width_64 = width_64;
this.width_128 = width_128;
this.right = right;
}
@Override
public String id() {
return id;
}
@Override
public ConfiguredCharacter left() {
return left;
}
@Override
public ConfiguredCharacter width_1() {
return width_1;
}
@Override
public ConfiguredCharacter width_2() {
return width_2;
}
@Override
public ConfiguredCharacter width_4() {
return width_4;
}
@Override
public ConfiguredCharacter width_8() {
return width_8;
}
@Override
public ConfiguredCharacter width_16() {
return width_16;
}
@Override
public ConfiguredCharacter width_32() {
return width_32;
}
@Override
public ConfiguredCharacter width_64() {
return width_64;
}
@Override
public ConfiguredCharacter width_128() {
return width_128;
}
@Override
public ConfiguredCharacter right() {
return right;
}
@Override
public String createImagePrefix(float n, float leftMargin, float rightMargin) {
String offset1 = OffsetFont.shortestNegChars(n + rightMargin + right.advance());
n = leftMargin + n + rightMargin;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(left.character());
while (n >= 128) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_128.character());
n -= 128;
}
if (n - 64 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_64.character());
n -= 64;
}
if (n - 32 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_32.character());
n -= 32;
}
if (n - 16 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_16.character());
n -= 16;
}
if (n - 8 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_8.character());
n -= 8;
}
if (n - 4 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_4.character());
n -= 4;
}
if (n - 2 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_2.character());
n -= 2;
}
if (n - 1 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_1.character());
}
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(right.character());
stringBuilder.append(offset1);
return stringBuilder.toString();
}
@Override
public String createImageSuffix(float advance, float leftMargin, float rightMargin) {
return OffsetFont.shortestPosChars(rightMargin + right.advance());
}
@Override
public String createImage(float n, float leftMargin, float rightMargin) {
n = n + leftMargin + rightMargin + 2;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(left.character());
while (n >= 128) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_128.character());
n -= 128;
}
if (n - 64 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_64.character());
n -= 64;
}
if (n - 32 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_32.character());
n -= 32;
}
if (n - 16 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_16.character());
n -= 16;
}
if (n - 8 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_8.character());
n -= 8;
}
if (n - 4 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_4.character());
n -= 4;
}
if (n - 2 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_2.character());
n -= 2;
}
if (n - 1 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_1.character());
}
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(right.character());
return stringBuilder.toString();
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
BackgroundImpl that = (BackgroundImpl) object;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
public static class BuilderImpl implements Builder {
private String id;
private ConfiguredCharacter left;
private ConfiguredCharacter width_1;
private ConfiguredCharacter width_2;
private ConfiguredCharacter width_4;
private ConfiguredCharacter width_8;
private ConfiguredCharacter width_16;
private ConfiguredCharacter width_32;
private ConfiguredCharacter width_64;
private ConfiguredCharacter width_128;
private ConfiguredCharacter right;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder left(ConfiguredCharacter character) {
this.left = character;
return this;
}
@Override
public Builder width_1(ConfiguredCharacter character) {
this.width_1 = character;
return this;
}
@Override
public Builder width_2(ConfiguredCharacter character) {
this.width_2 = character;
return this;
}
@Override
public Builder width_4(ConfiguredCharacter character) {
this.width_4 = character;
return this;
}
@Override
public Builder width_8(ConfiguredCharacter character) {
this.width_8 = character;
return this;
}
@Override
public Builder width_16(ConfiguredCharacter character) {
this.width_16 = character;
return this;
}
@Override
public Builder width_32(ConfiguredCharacter character) {
this.width_32 = character;
return this;
}
@Override
public Builder width_64(ConfiguredCharacter character) {
this.width_64 = character;
return this;
}
@Override
public Builder width_128(ConfiguredCharacter character) {
this.width_128 = character;
return this;
}
@Override
public Builder right(ConfiguredCharacter character) {
this.right = character;
return this;
}
@Override
public Background build() {
return new BackgroundImpl(id, left, width_1, width_2, width_4, width_8, width_16, width_32, width_64, width_128, right);
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.background;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface BackgroundManager extends Reloadable {
@Nullable
Background getBackground(String id);
Collection<Background> getBackgrounds();
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.background;
import dev.dejvokep.boostedyaml.YamlDocument;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
public class BackgroundManagerImpl implements BackgroundManager {
private final CustomNameplates plugin;
private final Map<String, Background> backgrounds = new HashMap<>();
public BackgroundManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void unload() {
this.backgrounds.clear();
}
@Override
public void load() {
this.loadConfigs();
}
@Nullable
@Override
public Background getBackground(String id) {
return this.backgrounds.get(id);
}
@Override
public Collection<Background> getBackgrounds() {
return new HashSet<>(backgrounds.values());
}
private void loadConfigs() {
File backgroundFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "backgrounds");
if (!backgroundFolder.exists() && backgroundFolder.mkdirs()) {
saveDefaultBackgrounds();
}
List<File> configFiles = ConfigUtils.getConfigsDeeply(backgroundFolder);
for (File configFile : configFiles) {
YamlDocument config = plugin.getConfigManager().loadData(configFile);
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
int height = config.getInt("middle.height", 14);
int ascent = config.getInt("middle.ascent", 8);
Background background = Background.builder()
.id(id)
.left(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
config.getInt("left.ascent", 8),
config.getInt("left.height", 14)
))
.right(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("right.image") + ".png"),
config.getInt("right.ascent", 8),
config.getInt("right.height", 14)
))
.width_1(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.1") + ".png"),
ascent, height
))
.width_2(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.2") + ".png"),
ascent, height
))
.width_4(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.4") + ".png"),
ascent, height
))
.width_8(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.8") + ".png"),
ascent, height
))
.width_16(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.16") + ".png"),
ascent, height
))
.width_32(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.32") + ".png"),
ascent, height
))
.width_64(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.64") + ".png"),
ascent, height
))
.width_128(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.128") + ".png"),
ascent, height
))
.build();
this.backgrounds.put(id, background);
}
}
private void saveDefaultBackgrounds() {
String[] bg_list = new String[]{"b0", "b1", "b2", "b4", "b8", "b16","b32","b64","b128"};
for (String bg : bg_list) {
plugin.getConfigManager().saveResource("contents" + File.separator + "backgrounds" + File.separator + bg + ".png");
}
String[] config_list = new String[]{"bedrock_1", "bedrock_2"};
for (String config : config_list) {
plugin.getConfigManager().saveResource("contents" + File.separator + "backgrounds" + File.separator + config + ".yml");
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,19 +15,35 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.paper.storage.method.database.sql; package net.momirealms.customnameplates.api.feature.bossbar;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin; import java.util.UUID;
import net.momirealms.customnameplates.api.data.StorageType;
public class MySQLImpl extends AbstractHikariDatabase { public interface BossBar {
public MySQLImpl(CustomNameplatesPlugin plugin) { UUID uuid();
super(plugin);
float progress();
Color color();
Overlay overlay();
enum Overlay {
PROGRESS,
NOTCHED_6,
NOTCHED_10,
NOTCHED_12,
NOTCHED_20
} }
@Override enum Color {
public StorageType getStorageType() { BLUE,
return StorageType.MySQL; GREEN,
PINK,
PURPLE,
RED,
WHITE,
YELLOW
} }
} }

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bossbar;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.requirement.Requirement;
public interface BossBarConfig {
BossBar.Overlay DEFAULT_OVERLAY = BossBar.Overlay.PROGRESS;
BossBar.Color DEFAULT_COLOR = BossBar.Color.YELLOW;
String id();
Requirement[] requirements();
CarouselText[] carouselTexts();
BossBar.Overlay overlay();
BossBar.Color color();
float progress();
static BossBarConfig.Builder builder() {
return new BossBarConfigImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder requirement(Requirement[] requirements);
Builder carouselText(CarouselText[] carouselTexts);
Builder overlay(BossBar.Overlay overlay);
Builder color(BossBar.Color color);
Builder progress(float progress);
BossBarConfig build();
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bossbar;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.requirement.Requirement;
import static java.util.Objects.requireNonNull;
public class BossBarConfigImpl implements BossBarConfig {
private final String id;
private final Requirement[] requirements;
private final CarouselText[] carouselTexts;
private final BossBar.Overlay overlay;
private final BossBar.Color color;
private final float progress;
public BossBarConfigImpl(String id, Requirement[] requirements, CarouselText[] carouselTexts, BossBar.Overlay overlay, BossBar.Color color, float progress) {
this.id = requireNonNull(id);
this.requirements = requireNonNull(requirements);
this.carouselTexts = requireNonNull(carouselTexts);
this.overlay = requireNonNull(overlay);
this.color = requireNonNull(color);
this.progress = progress;
}
@Override
public String id() {
return id;
}
@Override
public Requirement[] requirements() {
return requirements;
}
@Override
public CarouselText[] carouselTexts() {
return carouselTexts;
}
@Override
public BossBar.Overlay overlay() {
return overlay;
}
@Override
public BossBar.Color color() {
return color;
}
@Override
public float progress() {
return progress;
}
public static class BuilderImpl implements Builder {
private String id;
private Requirement[] requirements;
private CarouselText[] carouselTexts;
private BossBar.Overlay overlay = DEFAULT_OVERLAY;
private BossBar.Color color = DEFAULT_COLOR;
private float progress = 0f;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder requirement(Requirement[] requirements) {
this.requirements = requirements;
return this;
}
@Override
public Builder carouselText(CarouselText[] carouselTexts) {
this.carouselTexts = carouselTexts;
return this;
}
@Override
public Builder overlay(BossBar.Overlay overlay) {
this.overlay = overlay;
return this;
}
@Override
public Builder color(BossBar.Color color) {
this.color = color;
return this;
}
@Override
public Builder progress(float progress) {
this.progress = progress;
return this;
}
@Override
public BossBarConfig build() {
return new BossBarConfigImpl(id, requirements, carouselTexts, overlay, color, progress);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bossbar;
import net.momirealms.customnameplates.api.CNPlayer;
import java.util.ArrayList;
import java.util.List;
public class BossBarDisplayController {
private final CNPlayer owner;
private final BossBarManager manager;
private final BossBarSender[] senders;
public BossBarDisplayController(BossBarManager manager, CNPlayer owner) {
this.owner = owner;
this.manager = manager;
List<BossBarSender> senderList = new ArrayList<>();
for (BossBarConfig config : manager.allConfigs()) {
BossBarSender sender = new BossBarSender(owner, config);
senderList.add(sender);
this.owner.addFeature(sender);
}
this.senders = senderList.toArray(new BossBarSender[0]);
}
public void onTick() {
int size = senders.length;
int[] states = new int[size];
int index = size;
for (int i = 0; i < size; i++) {
BossBarSender sender = senders[i];
boolean canShow = sender.checkConditions();
if (canShow) {
if (!sender.isShown()) {
states[i] = 1;
sender.init();
sender.tick();
if (index == size) {
index = i;
}
} else {
states[i] = 2;
if (i > index) {
sender.hide();
}
sender.tick();
}
} else {
if (sender.isShown()) {
sender.hide();
}
states[i] = 0;
}
}
if (index != size) {
for (int i = index; i < size; i++) {
if (states[i] != 0) {
senders[i].show();
}
}
}
}
public void destroy() {
for (BossBarSender sender : this.senders) {
sender.hide();
this.owner.removeFeature(sender);
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bossbar;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
public interface BossBarManager extends Reloadable {
void onTick();
BossBarConfig getConfig(String name);
BossBarConfig[] allConfigs();
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bossbar;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class BossBarManagerImpl implements BossBarManager, JoinQuitListener {
private final CustomNameplates plugin;
private final LinkedHashMap<String, BossBarConfig> configs = new LinkedHashMap<>();
private final ConcurrentHashMap<UUID, BossBarDisplayController> senders = new ConcurrentHashMap<>();
private BossBarConfig[] configArray = new BossBarConfig[0];
public BossBarManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void load() {
// ignore disabled modules
if (!ConfigManager.bossbarModule()) return;
this.loadConfig();
this.resetArray();
for (CNPlayer online : plugin.getOnlinePlayers()) {
onPlayerJoin(online);
}
}
@Override
public void unload() {
for (BossBarDisplayController sender : senders.values()) {
sender.destroy();
}
this.senders.clear();
this.configs.clear();
this.resetArray();
}
@Override
public void onTick() {
for (BossBarDisplayController sender : senders.values()) {
sender.onTick();
}
}
private void resetArray() {
configArray = configs.values().toArray(new BossBarConfig[0]);
}
@Override
public void onPlayerJoin(CNPlayer player) {
if (!ConfigManager.bossbarModule()) return;
plugin.getScheduler().asyncLater(() -> {
if (!player.isOnline()) return;
BossBarDisplayController sender = new BossBarDisplayController(this, player);
BossBarDisplayController previous = senders.put(player.uuid(), sender);
if (previous != null) {
previous.destroy();
}
}, ConfigManager.delaySend() * 50L, TimeUnit.MILLISECONDS);
}
@Override
public void onPlayerQuit(CNPlayer player) {
BossBarDisplayController sender = senders.remove(player.uuid());
if (sender != null) {
sender.destroy();
}
}
@Override
public BossBarConfig getConfig(String name) {
return configs.get(name);
}
@Override
public BossBarConfig[] allConfigs() {
return configArray;
}
private void loadConfig() {
plugin.getConfigManager().saveResource("configs" + File.separator + "bossbar.yml");
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "bossbar.yml"));
for (Map.Entry<String, Object> entry : document.getStringRouteMappedValues(false).entrySet()) {
if (!(entry.getValue() instanceof Section section))
return;
this.configs.put(entry.getKey(),
BossBarConfig.builder()
.id(entry.getKey())
.overlay(BossBar.Overlay.valueOf(section.getString("overlay", "PROGRESS").toUpperCase(Locale.ENGLISH)))
.color(BossBar.Color.valueOf(section.getString("color", "YELLOW").toUpperCase(Locale.ENGLISH)))
.requirement(plugin.getRequirementManager().parseRequirements(section.getSection("conditions")))
.carouselText(
section.contains("text") ?
new CarouselText[]{new CarouselText(-1, new Requirement[0], section.getString("text"), false)} :
ConfigUtils.carouselTexts(section.getSection("text-display-order"))
)
.progress(section.getFloat("progress", 0f))
.build()
);
}
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bossbar;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.feature.DynamicText;
import net.momirealms.customnameplates.api.feature.Feature;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class BossBarSender implements Feature, BossBar {
private final UUID uuid = UUID.randomUUID();
private final CNPlayer owner;
private boolean isShown = false;
private final BossBarConfig config;
private int order;
private int timeLeft;
private DynamicText currentBossBar;
private String latestContent;
public BossBarSender(CNPlayer owner, BossBarConfig config) {
this.owner = owner;
this.config = config;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BossBarSender that = (BossBarSender) o;
return owner == that.owner && uuid == that.uuid;
}
@Override
public int hashCode() {
return uuid.hashCode();
}
@Override
public UUID uuid() {
return uuid;
}
@Override
public String name() {
return "BossBarSender";
}
@Override
public float progress() {
return config.progress();
}
@Override
public Color color() {
return config.color();
}
@Override
public Overlay overlay() {
return config.overlay();
}
public boolean checkConditions() {
return owner.isMet(config.requirements());
}
public void init() {
order = config.carouselTexts().length - 1;
timeLeft = 0;
}
public void tick() {
if (timeLeft > 0)
timeLeft--;
if (timeLeft == 0) {
int triedTimes = 0;
do {
if (triedTimes == config.carouselTexts().length) {
timeLeft = 20;
currentBossBar = null;
CustomNameplates.getInstance().getPluginLogger().warn("No text in order is available for player " + owner.name() + ". Please check your bossbar's conditions.");
return;
}
order++;
if (order >= config.carouselTexts().length) {
order = 0;
}
triedTimes++;
} while (
!owner.isMet(config.carouselTexts()[order].requirements())
);
CarouselText carouselText = config.carouselTexts()[order];
timeLeft = carouselText.duration();
currentBossBar = carouselText.preParsedDynamicText().fastCreate(owner);
if (carouselText.updateOnDisplay()) {
owner.forceUpdate(currentBossBar.placeholders(), Collections.emptySet());
}
refresh();
if (isShown()) {
sendLatestBossBarName();
}
}
}
public void hide() {
isShown = false;
Object packet = CustomNameplates.getInstance().getPlatform().removeBossBarPacket(uuid);
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
}
public void show() {
isShown = true;
if (latestContent == null) {
refresh();
}
Object packet = CustomNameplates.getInstance().getPlatform().createBossBarPacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(latestContent), progress(), overlay(), color());
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
}
public boolean isShown() {
return isShown;
}
@Override
public Set<Placeholder> activePlaceholders() {
if (currentBossBar == null || !isShown()) return Collections.emptySet();
return currentBossBar.placeholders();
}
@Override
public Set<Placeholder> allPlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
for (CarouselText text : config.carouselTexts()) {
placeholders.addAll(text.preParsedDynamicText().placeholders());
}
return placeholders;
}
@Override
public void notifyPlaceholderUpdates(CNPlayer p1, boolean force) {
refresh();
if (isShown()) {
sendLatestBossBarName();
}
}
public void refresh() {
latestContent = this.currentBossBar.render(owner);
}
public void sendLatestBossBarName() {
if (latestContent != null) {
Object packet = CustomNameplates.getInstance().getPlatform().updateBossBarNamePacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(latestContent));
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import net.momirealms.customnameplates.api.feature.AdaptiveImage;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
public interface Bubble extends AdaptiveImage {
String id();
ConfiguredCharacter left();
ConfiguredCharacter right();
ConfiguredCharacter middle();
ConfiguredCharacter tail();
static Builder builder() {
return new BubbleImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder left(ConfiguredCharacter left);
Builder right(ConfiguredCharacter right);
Builder middle(ConfiguredCharacter middle);
Builder tail(ConfiguredCharacter tail);
Bubble build();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import net.momirealms.customnameplates.api.util.Vector3;
public interface BubbleConfig {
String id();
int backgroundColor();
int lineWidth();
int maxLines();
Bubble[] bubbles();
String textPrefix();
String textSuffix();
String displayName();
Vector3 scale();
static Builder builder() {
return new BubbleConfigImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder displayName(String displayName);
Builder backgroundColor(int backgroundColor);
Builder lineWidth(int lineWidth);
Builder maxLines(int maxLines);
Builder bubbles(Bubble[] bubbles);
Builder textPrefix(String textPrefix);
Builder textSuffix(String textSuffix);
Builder scale(Vector3 scale);
BubbleConfig build();
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import net.momirealms.customnameplates.api.util.Vector3;
import static java.util.Objects.requireNonNull;
public class BubbleConfigImpl implements BubbleConfig {
private final String id;
private final int backgroundColor;
private final int lineWidth;
private final int maxLines;
private final Bubble[] bubbles;
private final String textPrefix;
private final String textSuffix;
private final String displayName;
private final Vector3 scale;
public BubbleConfigImpl(String id, String displayName, int backgroundColor, int lineWidth, int maxLines, Bubble[] bubbles, String textPrefix, String textSuffix, Vector3 scale) {
this.backgroundColor = backgroundColor;
this.lineWidth = lineWidth;
this.maxLines = maxLines;
this.id = id;
this.bubbles = requireNonNull(bubbles);
this.textPrefix = requireNonNull(textPrefix);
this.textSuffix = requireNonNull(textSuffix);
this.displayName = requireNonNull(displayName);
this.scale = requireNonNull(scale);
}
@Override
public String id() {
return id;
}
@Override
public int backgroundColor() {
return backgroundColor;
}
@Override
public int lineWidth() {
return lineWidth;
}
@Override
public int maxLines() {
return maxLines;
}
@Override
public Bubble[] bubbles() {
return bubbles;
}
@Override
public String textPrefix() {
return textPrefix;
}
@Override
public String textSuffix() {
return textSuffix;
}
@Override
public String displayName() {
return displayName;
}
@Override
public Vector3 scale() {
return scale;
}
public static class BuilderImpl implements Builder {
private int backgroundColor;
private int lineWidth;
private int maxLines;
private Bubble[] bubbles;
private String textPrefix;
private String textSuffix;
private String id;
private String displayName;
private Vector3 scale;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder displayName(String displayName) {
this.displayName = displayName;
return this;
}
@Override
public Builder backgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
return this;
}
@Override
public Builder lineWidth(int lineWidth) {
this.lineWidth = lineWidth;
return this;
}
@Override
public Builder maxLines(int maxLines) {
this.maxLines = maxLines;
return this;
}
@Override
public Builder bubbles(Bubble[] bubbles) {
this.bubbles = bubbles;
return this;
}
@Override
public Builder textPrefix(String textPrefix) {
this.textPrefix = textPrefix;
return this;
}
@Override
public Builder textSuffix(String textSuffix) {
this.textSuffix = textSuffix;
return this;
}
@Override
public Builder scale(Vector3 scale) {
this.scale = scale;
return this;
}
@Override
public BubbleConfig build() {
return new BubbleConfigImpl(id, displayName, backgroundColor, lineWidth, maxLines, bubbles, textPrefix, textSuffix, scale);
}
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.feature.OffsetFont;
import java.util.Objects;
public class BubbleImpl implements Bubble {
private final String id;
private final ConfiguredCharacter left;
private final ConfiguredCharacter right;
private final ConfiguredCharacter middle;
private final ConfiguredCharacter tail;
public BubbleImpl(String id, ConfiguredCharacter left, ConfiguredCharacter right, ConfiguredCharacter middle, ConfiguredCharacter tail) {
this.id = id;
this.left = left;
this.right = right;
this.middle = middle;
this.tail = tail;
}
@Override
public String id() {
return id;
}
@Override
public ConfiguredCharacter left() {
return left;
}
@Override
public ConfiguredCharacter right() {
return right;
}
@Override
public ConfiguredCharacter middle() {
return middle;
}
@Override
public ConfiguredCharacter tail() {
return tail;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
BubbleImpl bubble = (BubbleImpl) object;
return Objects.equals(id, bubble.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@SuppressWarnings("DuplicatedCode")
@Override
public String createImagePrefix(float advance, float leftMargin, float rightMargin) {
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin - (tail.advance() - 1)) / (middle.advance() - 1));
float actualMiddleWidth = 0;
if (mid_amount > 512) return "";
if (mid_amount <= 0) {
sb.append(tail.character()).append(OffsetFont.NEG_1.character());
actualMiddleWidth = tail.advance() - 1;
} else {
actualMiddleWidth += (middle.advance() - 1) * mid_amount;
actualMiddleWidth += (tail.advance() - 1);
int tailPos = mid_amount / 2;
for (int i = 0; i < mid_amount; i++) {
sb.append(middle.character()).append(OffsetFont.NEG_1.character());
if (i == tailPos) {
sb.append(tail.character()).append(OffsetFont.NEG_1.character());
}
}
}
float exceed = actualMiddleWidth - advance;
sb.append(right.character());
float delta = (right.advance() + rightMargin + advance + exceed / 2);
if (delta != 0) {
sb.append(OffsetFont.createOffsets(-delta));
}
return sb.toString();
}
@Override
public String createImageSuffix(float advance, float leftMargin, float rightMargin) {
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin - (tail.advance() - 1)) / (middle.advance() - 1));
float actualMiddleWidth = 0;
actualMiddleWidth += (tail.advance() - 1);
actualMiddleWidth += (middle.advance() - 1) * mid_amount;
float exceed = actualMiddleWidth - advance;
float delta = (right.advance() + rightMargin + exceed / 2);
return OffsetFont.shortestPosChars(delta);
}
@SuppressWarnings("DuplicatedCode")
@Override
public String createImage(float advance, float leftMargin, float rightMargin) {
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin - (tail.advance() - 1)) / (middle.advance() - 1));
if (mid_amount > 512) return "";
if (mid_amount <= 0) {
sb.append(tail.character()).append(OffsetFont.NEG_1.character());
} else {
int tailPos = mid_amount / 2;
for (int i = 0; i < mid_amount; i++) {
sb.append(middle.character()).append(OffsetFont.NEG_1.character());
if (i == tailPos) {
sb.append(tail.character()).append(OffsetFont.NEG_1.character());
}
}
}
sb.append(right.character());
float delta = (right.advance() + rightMargin - (left.advance() - 1) - leftMargin);
if (delta != 0) {
sb.append(OffsetFont.createOffsets(-delta));
}
return sb.toString();
}
public static class BuilderImpl implements Builder {
private String id;
private ConfiguredCharacter left;
private ConfiguredCharacter right;
private ConfiguredCharacter middle;
private ConfiguredCharacter tail;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder left(ConfiguredCharacter left) {
this.left = left;
return this;
}
@Override
public Builder right(ConfiguredCharacter right) {
this.right = right;
return this;
}
@Override
public Builder middle(ConfiguredCharacter middle) {
this.middle = middle;
return this;
}
@Override
public Builder tail(ConfiguredCharacter tail) {
this.tail = tail;
return this;
}
@Override
public Bubble build() {
return new BubbleImpl(id, left, right, middle, tail);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Set;
public interface BubbleManager extends Reloadable {
Collection<BubbleConfig> getBubbleConfigs();
@Nullable
Bubble getBubble(String id);
@Nullable BubbleConfig getBubbleConfig(String id);
Collection<Bubble> getBubbles();
boolean hasBubble(CNPlayer player, String id);
Collection<BubbleConfig> availableBubbles(CNPlayer player);
Set<String> blacklistChannels();
String defaultBubbleId();
Requirement[] sendBubbleRequirements();
Requirement[] viewBubbleRequirements();
String omittedText();
double verticalOffset();
int stayDuration();
int appearDuration();
int disappearDuration();
float viewRange();
ChannelMode channelMode();
}

View File

@@ -0,0 +1,298 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.ChatListener;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class BubbleManagerImpl implements BubbleManager, ChatListener {
private final CustomNameplates plugin;
private final Map<String, Bubble> bubbles = new HashMap<>();
private Requirement[] sendBubbleRequirements;
private Requirement[] viewBubbleRequirements;
private String defaultBubbleId;
private String omittedText;
private double yOffset;
private int stayDuration;
private int appearDuration;
private int disappearDuration;
private float viewRange;
private Set<String> blacklistChannels;
private ChannelMode channelMode;
private final HashMap<String, BubbleConfig> bubbleConfigs = new HashMap<>();
public BubbleManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void unload() {
this.bubbles.clear();
this.bubbleConfigs.clear();
}
@Override
public void load() {
if (!ConfigManager.bubbleModule()) return;
this.loadConfigs();
this.loadConfig();
}
@Nullable
@Override
public Bubble getBubble(String id) {
return this.bubbles.get(id);
}
@Override
public @Nullable BubbleConfig getBubbleConfig(String id) {
return this.bubbleConfigs.get(id);
}
@Override
public Collection<Bubble> getBubbles() {
return new HashSet<>(bubbles.values());
}
@Override
public Collection<BubbleConfig> getBubbleConfigs() {
return new HashSet<>(bubbleConfigs.values());
}
@Override
public boolean hasBubble(CNPlayer player, String id) {
if (!this.bubbleConfigs.containsKey(id)) {
return false;
}
return player.hasPermission("bubbles.equip." + id);
}
@Override
public Collection<BubbleConfig> availableBubbles(CNPlayer player) {
ArrayList<BubbleConfig> available = new ArrayList<>();
for (BubbleConfig bubble : bubbleConfigs.values()) {
if (player.hasPermission("bubbles.equip." + bubble.id())) {
available.add(bubble);
}
}
return available;
}
@Override
public Set<String> blacklistChannels() {
return blacklistChannels;
}
@Override
public String defaultBubbleId() {
return defaultBubbleId;
}
@Override
public Requirement[] sendBubbleRequirements() {
return sendBubbleRequirements;
}
@Override
public Requirement[] viewBubbleRequirements() {
return viewBubbleRequirements;
}
@Override
public String omittedText() {
return omittedText;
}
@Override
public double verticalOffset() {
return yOffset;
}
@Override
public int stayDuration() {
return stayDuration;
}
@Override
public int appearDuration() {
return appearDuration;
}
@Override
public int disappearDuration() {
return disappearDuration;
}
@Override
public float viewRange() {
return viewRange;
}
@Override
public ChannelMode channelMode() {
return channelMode;
}
private void loadConfig() {
plugin.getConfigManager().saveResource("configs" + File.separator + "bubble.yml");
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "bubble.yml"));
sendBubbleRequirements = plugin.getRequirementManager().parseRequirements(document.getSection("sender-requirements"));
viewBubbleRequirements = plugin.getRequirementManager().parseRequirements(document.getSection("viewer-requirements"));
defaultBubbleId = document.getString("default-bubble", "chat");
omittedText = document.getString("omitted-text", "...");
yOffset = document.getDouble("y-offset", 0.2);
stayDuration = document.getInt("stay-duration", 160);
appearDuration = document.getInt("appear-duration", 20);
disappearDuration = document.getInt("disappear-duration", 10);
viewRange = document.getFloat("view-range", 0.5f);
blacklistChannels = new HashSet<>(document.getStringList("blacklist-channels"));
channelMode = ChannelMode.valueOf(document.getString("channel-mode", "ALL").toUpperCase(Locale.ENGLISH));
Section bubbleSettings = document.getSection("bubble-settings");
if (bubbleSettings != null) {
for (Map.Entry<String, Object> entry : bubbleSettings.getStringRouteMappedValues(false).entrySet()) {
String key = entry.getKey();
if (entry.getValue() instanceof Section inner) {
int maxLines = inner.getInt("max-lines", 1);
Bubble[] bubbleArray = new Bubble[maxLines];
for (int i = 0; i < maxLines; i++) {
bubbleArray[i] = getBubble(inner.getString("lines." + (i+1)));
}
this.bubbleConfigs.put(key, BubbleConfig.builder()
.id(key)
.maxLines(maxLines)
.bubbles(bubbleArray)
.displayName(inner.getString("display-name", key))
.lineWidth(inner.getInt("line-width", 100))
.backgroundColor(ConfigUtils.argb(inner.getString("background-color", "0,0,0,0")))
.textPrefix(inner.getString("text-prefix", ""))
.textSuffix(inner.getString("text-suffix", ""))
.scale(ConfigUtils.vector3(inner.getString("scale", "1,1,1")))
.build());
}
}
}
}
private void loadConfigs() {
File bubbleFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "bubbles");
if (!bubbleFolder.exists() && bubbleFolder.mkdirs()) {
saveDefaultBubbles();
}
List<File> configFiles = ConfigUtils.getConfigsDeeply(bubbleFolder);
for (File configFile : configFiles) {
YamlDocument config = plugin.getConfigManager().loadData(configFile);
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
Bubble bubble = Bubble.builder()
.id(id)
.left(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
config.getInt("left.ascent", 12),
config.getInt("left.height", 16)
))
.middle(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.image") + ".png"),
config.getInt("middle.ascent", 12),
config.getInt("middle.height", 16)
))
.tail(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("tail.image") + ".png"),
config.getInt("tail.ascent", 12),
config.getInt("tail.height", 16)
))
.right(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("right.image") + ".png"),
config.getInt("right.ascent", 12),
config.getInt("right.height", 16)
))
.build();
this.bubbles.put(id, bubble);
}
}
private void saveDefaultBubbles() {
String[] png_list = new String[]{"chat_1", "chat_2", "chat_3"};
String[] part_list = new String[]{"_left.png", "_middle.png", "_right.png", "_tail.png", ".yml"};
for (String name : png_list) {
for (String part : part_list) {
plugin.getConfigManager().saveResource("contents" + File.separator + "bubbles" + File.separatorChar + name + part);
}
}
}
@Override
public void onPlayerChat(CNPlayer player, String message, String channel) {
if (!ConfigManager.bubbleModule()) return;
// ignore blacklist channels
if (blacklistChannels().contains(channel)) return;
// check requirements
if (!player.isMet(sendBubbleRequirements())) return;
String equippedBubble = player.equippedBubble();
if (equippedBubble.equals("none")) equippedBubble = defaultBubbleId;
BubbleConfig config = bubbleConfigs.get(equippedBubble);
if (config == null) {
return;
}
String fullText = config.textPrefix() + AdventureHelper.stripTags(message.replace("\\", "\\\\")) + config.textSuffix();
int lines = plugin.getAdvanceManager().getLines(fullText, config.lineWidth());
if (lines > config.maxLines()) return;
TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(player);
if (renderer == null) return;
int removed = renderer.removeTagIf(tag -> tag.id().equals("bubble"));
int delay = 0;
if (removed != 0) {
delay += disappearDuration;
}
Bubble bubble = config.bubbles()[lines - 1];
float advance;
if (lines == 1) {
advance = plugin.getAdvanceManager().getLineAdvance(fullText);
} else {
advance = config.lineWidth();
}
BubbleTag bubbleTagText = new BubbleTag(player, renderer, channel, config,
AdventureHelper.miniMessageToMinecraftComponent(fullText),
bubble == null ? null : AdventureHelper.miniMessageToMinecraftComponent(AdventureHelper.surroundWithNameplatesFont(bubble.createImage(advance, 1,1))), this);
renderer.addTag(bubbleTagText);
if (delay != 0) {
plugin.getScheduler().asyncLater(() -> bubbleTagText.setCanShow(true), delay * 50L, TimeUnit.MILLISECONDS);
} else {
bubbleTagText.setCanShow(true);
}
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.tag.AbstractTag;
import net.momirealms.customnameplates.api.feature.tag.Tag;
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
import net.momirealms.customnameplates.api.network.Tracker;
import net.momirealms.customnameplates.api.util.Alignment;
import net.momirealms.customnameplates.api.util.SelfIncreaseEntityID;
import net.momirealms.customnameplates.api.util.Vector3;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class BubbleTag extends AbstractTag {
private final Object text;
@Nullable
private final Object background;
private final BubbleManager manager;
private final BubbleConfig bubbleConfig;
private int ticker;
private boolean canShow;
private final String channel;
private final HashMap<UUID, Boolean> cachedVisibility = new HashMap<>();
protected final int subEntityID = SelfIncreaseEntityID.getAndIncrease();
protected final UUID subEntityUUID = UUID.randomUUID();
public BubbleTag(CNPlayer owner, TagRenderer renderer, String channel, BubbleConfig bubbleConfig, Object text, @Nullable Object background, BubbleManager bubbleManager) {
super(owner, renderer);
this.text = text;
this.manager = bubbleManager;
this.bubbleConfig = bubbleConfig;
this.channel = channel;
this.background = background;
}
@Override
protected List<Object> spawnPacket(CNPlayer viewer) {
Tracker tracker = owner.getTracker(viewer);
Vector3 translation = translation(viewer);
List<Object> packets = new ArrayList<>(CustomNameplates.getInstance().getPlatform().createTextDisplayPacket(
entityID, uuid,
owner.position().add(0, (1.8 + (affectedByCrouching() && tracker.isCrouching() && !owner.isFlying() ? -0.3 : 0) + renderer.hatOffset()) * (affectedByScaling() ? tracker.getScale() : 1), 0),
0f, 0f, 0d,
-1, 0, 0,
text, bubbleConfig.backgroundColor(), (byte) -1, false, false, false,
Alignment.CENTER, manager.viewRange(), 0.0f, 1.0f,
new Vector3(0.001, 0.001, 0.001),
affectedByScaling() ? translation.add(0.001, -manager.verticalOffset(), 0.001).multiply(tracker.getScale()) : translation.add(0.001, -manager.verticalOffset(), 0.001),
bubbleConfig.lineWidth(),
(affectedByCrouching() && tracker.isCrouching())
));
if (background != null) {
packets.addAll(CustomNameplates.getInstance().getPlatform().createTextDisplayPacket(
subEntityID, subEntityUUID,
owner.position().add(0,(1.8 + (affectedByCrouching() && tracker.isCrouching() && !owner.isFlying() ? -0.3 : 0) + renderer.hatOffset()) * (affectedByScaling() ? tracker.getScale() : 1),0),
0f, 0f, 0d,
-1, 0, 0,
background, 0, (byte) -1, false, false, false,
Alignment.CENTER, manager.viewRange(), 0.0f, 1.0f,
new Vector3(0.001, 0.001, 0.001),
affectedByScaling() ? translation.add(0, -manager.verticalOffset(), 0).multiply(tracker.getScale()) : translation.add(0, -manager.verticalOffset(), 0),
2048,
(affectedByCrouching() && tracker.isCrouching())
));
}
return packets;
}
public void setCanShow(boolean canShow) {
this.canShow = canShow;
}
@Override
public boolean canShow() {
return canShow;
}
@Override
public boolean canShow(CNPlayer viewer) {
if (!viewer.isMet(owner, manager.viewBubbleRequirements())) {
return false;
}
switch (manager.channelMode()) {
case ALL -> {
return true;
}
case JOINED -> {
Boolean previous = cachedVisibility.get(viewer.uuid());
if (previous != null) return previous;
boolean can = CustomNameplates.getInstance().getChatManager().chatProvider().hasJoinedChannel(viewer, channel);
cachedVisibility.put(viewer.uuid(), can);
return can;
}
case CAN_JOIN -> {
Boolean previous = cachedVisibility.get(viewer.uuid());
if (previous != null) return previous;
boolean can = CustomNameplates.getInstance().getChatManager().chatProvider().canJoinChannel(viewer, channel);
cachedVisibility.put(viewer.uuid(), can);
return can;
}
}
return true;
}
@Override
public void show(CNPlayer viewer) {
if (!isShown()) return;
viewers.add(viewer);
resetViewerArray();
owner.trackPassengers(viewer, entityID);
if (background != null)
owner.trackPassengers(viewer, subEntityID);
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, spawnPacket(viewer));
CustomNameplates.getInstance().getScheduler().asyncLater(() -> {
Consumer<List<Object>> modifier0 = CustomNameplates.getInstance().getPlatform().createInterpolationDelayModifier(-1);
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createTransformationInterpolationDurationModifier(manager.appearDuration());
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createScaleModifier(bubbleConfig.scale());
Consumer<List<Object>> modifier3 = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation(viewer));
Object packet1 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier0, modifier1, modifier2, modifier3));
if (background != null) {
Object packet2 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(subEntityID, List.of(modifier0, modifier1, modifier2, modifier3));
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, List.of(packet1, packet2));
} else {
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet1);
}
}, 100, TimeUnit.MILLISECONDS);
}
@Override
public void tick() {
if (!canShow) return;
if (ticker >= manager.stayDuration()) {
renderer.removeTag(this);
return;
}
ticker++;
}
@Override
public void hide() {
if (!isShown()) return;
CNPlayer[] viewers = viewerArray.clone();
CustomNameplates.getInstance().getScheduler().asyncLater(() -> {
Object removePacket = createRemovePacket();
for (CNPlayer viewer : viewers) {
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, removePacket);
}
}, manager.disappearDuration() * 50L, TimeUnit.MILLISECONDS);
List<Object> disappearPacket = createDisappearPacket();
for (CNPlayer viewer : viewers) {
if (background != null) {
owner.untrackPassengers(viewer, entityID, subEntityID);
} else {
owner.untrackPassengers(viewer, entityID);
}
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, disappearPacket);
}
this.cachedVisibility.clear();
this.isShown = false;
this.viewers.clear();
resetViewerArray();
}
@Override
public void hide(CNPlayer viewer) {
if (!isShown()) return;
viewers.remove(viewer);
resetViewerArray();
if (background != null) {
owner.untrackPassengers(viewer, entityID, subEntityID);
} else {
owner.untrackPassengers(viewer, entityID);
}
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, createDisappearPacket());
}
private Object createRemovePacket() {
Object packet;
if (background != null) {
packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID, subEntityID);
} else {
packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID);
}
return packet;
}
private List<Object> createDisappearPacket() {
Consumer<List<Object>> modifier0 = CustomNameplates.getInstance().getPlatform().createInterpolationDelayModifier(-1);
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createTransformationInterpolationDurationModifier(manager.disappearDuration());
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createScaleModifier(new Vector3(0.001,0.001,0.001));
if (background != null) {
return List.of(CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier0, modifier1, modifier2)), CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(subEntityID, List.of(modifier0, modifier1, modifier2)));
} else {
return List.of(CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier0, modifier1, modifier2)));
}
}
@Override
public double getTextHeight(CNPlayer viewer) {
return -Double.MAX_VALUE;
}
@Override
public Vector3 scale(CNPlayer viewer) {
return new Vector3(1,1,1);
}
@Override
public Vector3 translation(CNPlayer viewer) {
return new Vector3(0, manager.verticalOffset() + maxY(viewer), 0);
}
public double maxY(CNPlayer viewer) {
double y = 0;
for (Tag tag : renderer.tags()) {
if (tag.isShown() && tag.isShown(viewer) && !tag.id().equals(id())) {
double currentY = tag.translation(viewer).y() + tag.getTextHeight(viewer);
if (currentY > y) {
y = currentY;
}
}
}
return y;
}
@Override
public String id() {
return "bubble";
}
@Override
public boolean affectedByCrouching() {
return false;
}
@Override
public boolean affectedByScaling() {
return true;
}
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.api.mechanic.bubble; package net.momirealms.customnameplates.api.feature.bubble;
public enum ChannelMode { public enum ChannelMode {

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble.chat;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.ChatListener;
import net.momirealms.customnameplates.api.feature.bubble.emoji.EmojiProvider;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractChatManager implements ChatManager {
protected final CustomNameplates plugin;
protected final List<EmojiProvider> emojiProviders = new ArrayList<>();
protected final List<ChatListener> listeners = new ArrayList<>();
protected ChatMessageProvider chatProvider;
protected ChatMessageProvider customProvider;
public AbstractChatManager(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void unload() {
this.emojiProviders.clear();
if (chatProvider instanceof AbstractChatMessageProvider chatMessageProvider) {
chatMessageProvider.unregister();
}
chatProvider = null;
}
@Override
public void load() {
setUpPlatformEmojiProviders();
if (customProvider == null) {
setUpPlatformProvider();
} else {
chatProvider = customProvider;
}
if (chatProvider instanceof AbstractChatMessageProvider chatMessageProvider) {
chatMessageProvider.register();
}
}
protected abstract void setUpPlatformProvider();
protected abstract void setUpPlatformEmojiProviders();
@Override
public boolean setCustomChatProvider(ChatMessageProvider provider) {
if (this.customProvider != null)
return false;
this.customProvider = provider;
this.reload();
return true;
}
@Override
public boolean removeCustomChatProvider() {
if (this.customProvider != null) {
this.customProvider = null;
this.reload();
return true;
}
return false;
}
@Override
public void disable() {
unload();
this.listeners.clear();
}
@Override
public void registerListener(final ChatListener listener) {
this.listeners.add(listener);
}
@Override
public void unregisterListener(final ChatListener listener) {
this.listeners.remove(listener);
}
@Override
public ChatMessageProvider chatProvider() {
return chatProvider;
}
@Override
public void onChat(CNPlayer player, String message, String channel) {
String text = message;
for (EmojiProvider provider : emojiProviders) {
text = provider.replace(player, text);
}
text = AdventureHelper.legacyToMiniMessage(text);
for (ChatListener listener : listeners) {
listener.onPlayerChat(player, text, channel);
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,32 +15,23 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.common.team; package net.momirealms.customnameplates.api.feature.bubble.chat;
import java.util.Locale; import net.momirealms.customnameplates.api.CustomNameplates;
public enum TeamColor { public abstract class AbstractChatMessageProvider implements ChatMessageProvider {
BLACK, protected CustomNameplates plugin;
DARK_BLUE, protected ChatManager manager;
DARK_GREEN,
DARK_AQUA,
DARK_RED,
DARK_PURPLE,
GOLD,
GRAY,
DARK_GRAY,
BLUE,
GREEN,
AQUA,
RED,
LIGHT_PURPLE,
YELLOW,
WHITE,
NONE,
CUSTOM;
public TeamColor getById(String id) throws IllegalArgumentException { public AbstractChatMessageProvider(CustomNameplates plugin, ChatManager manager) {
return valueOf(id.toUpperCase(Locale.ENGLISH)); this.plugin = plugin;
this.manager = manager;
}
public void register() {
}
public void unregister() {
} }
} }

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble.chat;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.feature.ChatListener;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
public interface ChatManager extends Reloadable {
boolean setCustomChatProvider(ChatMessageProvider provider);
boolean removeCustomChatProvider();
void registerListener(ChatListener listener);
void unregisterListener(ChatListener listener);
ChatMessageProvider chatProvider();
void onChat(CNPlayer player, String message, String channel);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.bubble.chat;
import net.momirealms.customnameplates.api.CNPlayer;
public interface ChatMessageProvider {
boolean hasJoinedChannel(CNPlayer player, String channelID);
boolean canJoinChannel(CNPlayer player, String channelID);
boolean isIgnoring(CNPlayer sender, CNPlayer receiver);
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.paper.mechanic.requirement.papi; package net.momirealms.customnameplates.api.feature.bubble.emoji;
import org.bukkit.OfflinePlayer; import net.momirealms.customnameplates.api.CNPlayer;
public interface PapiRequirement { public interface EmojiProvider {
boolean isMet(OfflinePlayer player); String replace(CNPlayer player, String text);
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,28 +15,34 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.api.mechanic.tag.unlimited; package net.momirealms.customnameplates.api.feature.image;
import org.bukkit.entity.Entity; import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import org.bukkit.entity.Player;
import java.util.Collection; public interface Image {
public interface EntityTagEntity { String id();
void addTag(StaticTextEntity tag); boolean hasShadow();
StaticTextEntity addTag(StaticTextTagSetting setting); int opacity();
void removeTag(StaticTextEntity tag); ConfiguredCharacter character();
Collection<StaticTextEntity> getStaticTags(); static Builder builder() {
return new ImageImpl.BuilderImpl();
void forceAddNearbyPlayer(Player player); }
void forceRemoveNearbyPlayer(Player player); interface Builder {
Entity getEntity(); Builder id(String id);
void destroy(); Builder hasShadow(boolean has);
Builder opacity(int opacity);
Builder character(ConfiguredCharacter character);
Image build();
}
} }

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.image;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import java.util.Objects;
public class ImageImpl implements Image {
private final String id;
private final boolean hasShadow;
private final int opacity;
private final ConfiguredCharacter character;
public ImageImpl(String id, boolean hasShadow, int opacity, ConfiguredCharacter character) {
this.id = id;
this.hasShadow = hasShadow;
this.opacity = opacity;
this.character = character;
}
@Override
public String id() {
return id;
}
@Override
public boolean hasShadow() {
return hasShadow;
}
@Override
public int opacity() {
return opacity;
}
@Override
public ConfiguredCharacter character() {
return character;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
ImageImpl image = (ImageImpl) object;
return Objects.equals(id, image.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
public static class BuilderImpl implements Builder {
private String id;
private boolean hasShadow;
private int opacity;
private ConfiguredCharacter character;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder hasShadow(boolean has) {
this.hasShadow = has;
return this;
}
@Override
public Builder opacity(int opacity) {
this.opacity = opacity;
return this;
}
@Override
public Builder character(ConfiguredCharacter character) {
this.character = character;
return this;
}
@Override
public Image build() {
return new ImageImpl(id, hasShadow, opacity, character);
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.image;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface ImageManager extends Reloadable {
@Nullable
Image getImage(String id);
Collection<Image> getImages();
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.image;
import dev.dejvokep.boostedyaml.YamlDocument;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
public class ImageManagerImpl implements ImageManager {
private final CustomNameplates plugin;
private final HashMap<String, Image> images = new HashMap<>();
public ImageManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void unload() {
this.images.clear();
}
@Override
public void load() {
this.loadConfigs();
}
@Override
public @Nullable Image getImage(String id) {
return images.get(id);
}
@Override
public Collection<Image> getImages() {
return new HashSet<>(images.values());
}
private void loadConfigs() {
File imageFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "images");
if (!imageFolder.exists() && imageFolder.mkdirs()) {
saveDefaultImages();
}
List<File> configFiles = ConfigUtils.getConfigsDeeply(imageFolder);
for (File configFile : configFiles) {
YamlDocument config = plugin.getConfigManager().loadData(configFile);
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
Image image = Image.builder()
.id(id)
.hasShadow(!config.getBoolean("shadow.remove", false))
.opacity(config.getInt("shadow.opacity", 254))
.character(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("image") + ".png"),
config.getInt("ascent", 8),
config.getInt("height", 10)
))
.build();
this.images.put(id, image);
}
}
private void saveDefaultImages() {
String[] png_list = new String[]{"bell", "bubble", "clock", "coin", "compass", "weather", "stamina_0", "stamina_1", "stamina_2"};
String[] part_list = new String[]{".png", ".yml"};
for (String name : png_list) {
for (String part : part_list) {
plugin.getConfigManager().saveResource("contents" + File.separator + "images" + File.separator + name + part);
}
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.nameplate;
import net.momirealms.customnameplates.api.feature.AdaptiveImage;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
public interface Nameplate extends AdaptiveImage {
String id();
String displayName();
ConfiguredCharacter left();
ConfiguredCharacter middle();
ConfiguredCharacter right();
static Builder builder() {
return new NameplateImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder displayName(String displayName);
Builder left(ConfiguredCharacter left);
Builder middle(ConfiguredCharacter middle);
Builder right(ConfiguredCharacter right);
Nameplate build();
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.nameplate;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.feature.OffsetFont;
import java.util.Objects;
public class NameplateImpl implements Nameplate {
private final String id;
private final String displayName;
private final ConfiguredCharacter left;
private final ConfiguredCharacter middle;
private final ConfiguredCharacter right;
public NameplateImpl(String id, String displayName, ConfiguredCharacter left, ConfiguredCharacter middle, ConfiguredCharacter right) {
this.id = id;
this.displayName = displayName;
this.left = left;
this.middle = middle;
this.right = right;
}
@Override
public String id() {
return id;
}
@Override
public String displayName() {
return displayName;
}
@Override
public ConfiguredCharacter left() {
return left;
}
@Override
public ConfiguredCharacter middle() {
return middle;
}
@Override
public ConfiguredCharacter right() {
return right;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
NameplateImpl nameplate = (NameplateImpl) object;
return Objects.equals(id, nameplate.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@SuppressWarnings("DuplicatedCode")
@Override
public String createImagePrefix(float advance, float leftMargin, float rightMargin) {
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin) / (middle.advance() - 1));
float exceed = mid_amount * (middle.advance() - 1) - advance - leftMargin - rightMargin;
for (int i = 0; i < mid_amount; i++) {
sb.append(middle.character()).append(OffsetFont.NEG_1.character());
}
sb.append(right.character());
float delta = (right.advance() + rightMargin + advance + (float) Math.floor(exceed / 2));
if (delta != 0) {
sb.append(OffsetFont.shortestNegChars(delta));
}
return sb.toString();
}
@Override
public String createImageSuffix(float advance, float leftMargin, float rightMargin) {
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin) / (middle.advance() - 1));
float exceed = mid_amount * (middle.advance() - 1) - advance - leftMargin - rightMargin;
return OffsetFont.shortestPosChars((float) Math.ceil(exceed / 2) + rightMargin + right.advance());
}
@SuppressWarnings("DuplicatedCode")
@Override
public String createImage(float advance, float leftMargin, float rightMargin) {
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin) / (middle.advance() - 1));
if (mid_amount > 512) return "";
for (int i = 0; i < mid_amount; i++) {
sb.append(middle.character()).append(OffsetFont.NEG_1.character());
}
sb.append(right.character());
float delta = (right.advance() + rightMargin - (left.advance() - 1) - leftMargin);
if (delta != 0) {
sb.append(OffsetFont.createOffsets(-delta));
}
return sb.toString();
}
public static class BuilderImpl implements Builder {
private String id;
private String displayName;
private ConfiguredCharacter left;
private ConfiguredCharacter middle;
private ConfiguredCharacter right;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder displayName(String displayName) {
this.displayName = displayName;
return this;
}
@Override
public Builder left(ConfiguredCharacter left) {
this.left = left;
return this;
}
@Override
public Builder middle(ConfiguredCharacter middle) {
this.middle = middle;
return this;
}
@Override
public Builder right(ConfiguredCharacter right) {
this.right = right;
return this;
}
@Override
public Nameplate build() {
return new NameplateImpl(id, displayName, left, middle, right);
}
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,18 +15,26 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.velocity.team; package net.momirealms.customnameplates.api.feature.nameplate;
import com.velocitypowered.api.proxy.Player; import net.momirealms.customnameplates.api.CNPlayer;
import net.kyori.adventure.text.Component; import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
import net.momirealms.customnameplates.common.team.TeamColor;
import net.momirealms.customnameplates.common.team.TeamTagVisibility;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public interface VelocityTeamManager { import java.util.Collection;
public interface NameplateManager extends Reloadable {
@Nullable @Nullable
String getTeam(Player player, Player viewer); Nameplate getNameplate(String id);
void sendTeamUpdatePacket(Player receiver, String team, TeamColor color, TeamTagVisibility visibility, Component prefix, Component suffix); Collection<Nameplate> getNameplates();
boolean hasNameplate(CNPlayer player, String id);
Collection<Nameplate> availableNameplates(CNPlayer player);
String defaultNameplateId();
String playerNameTag();
} }

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.nameplate;
import dev.dejvokep.boostedyaml.YamlDocument;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.feature.DynamicText;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
public class NameplateManagerImpl implements NameplateManager {
private final CustomNameplates plugin;
private final Map<String, Nameplate> nameplates = new HashMap<>();
private final Map<UUID, DynamicText> tags = new HashMap<>();
private String defaultNameplateId;
private String nameTag;
public NameplateManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void unload() {
this.nameplates.clear();
}
@Override
public void load() {
this.loadConfig();
this.loadConfigs();
}
@Nullable
@Override
public Nameplate getNameplate(String id) {
return this.nameplates.get(id);
}
@Override
public Collection<Nameplate> getNameplates() {
return new HashSet<>(nameplates.values());
}
@Override
public boolean hasNameplate(CNPlayer player, String id) {
if (!this.nameplates.containsKey(id)) {
return false;
}
return player.hasPermission("nameplates.equip." + id);
}
@Override
public Collection<Nameplate> availableNameplates(CNPlayer player) {
ArrayList<Nameplate> available = new ArrayList<>();
for (Nameplate nameplate : nameplates.values()) {
if (player.hasPermission("nameplates.equip." + nameplate.id())) {
available.add(nameplate);
}
}
return available;
}
@Override
public String defaultNameplateId() {
return defaultNameplateId;
}
@Override
public String playerNameTag() {
return nameTag;
}
private void loadConfig() {
plugin.getConfigManager().saveResource("configs" + File.separator + "nameplate.yml");
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "nameplate.yml"));
defaultNameplateId = document.getString("default-nameplate", "none");
String prefix = document.getString("nameplate.prefix", "");
String name = document.getString("nameplate.player-name", "%player_name%");
String suffix = document.getString("nameplate.suffix", "");
nameTag = prefix + name + suffix;
}
private void loadConfigs() {
File nameplateFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "nameplates");
if (!nameplateFolder.exists() && nameplateFolder.mkdirs()) {
saveDefaultNameplates();
}
List<File> configFiles = ConfigUtils.getConfigsDeeply(nameplateFolder);
for (File configFile : configFiles) {
YamlDocument config = plugin.getConfigManager().loadData(configFile);
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
Nameplate nameplate = Nameplate.builder()
.id(id)
.displayName(config.getString("display-name", id))
.left(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
config.getInt("left.ascent", 12),
config.getInt("left.height", 16)
))
.middle(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.image") + ".png"),
config.getInt("middle.ascent", 12),
config.getInt("middle.height", 16)
))
.right(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("right.image") + ".png"),
config.getInt("right.ascent", 12),
config.getInt("right.height", 16)
))
.build();
this.nameplates.put(id, nameplate);
}
}
private void saveDefaultNameplates() {
String[] png_list = new String[]{"cat", "egg", "cheems", "wither", "xmas", "halloween", "hutao", "starsky", "trident", "rabbit"};
String[] part_list = new String[]{"_left.png", "_middle.png", "_right.png", ".yml"};
for (String name : png_list) {
for (String part : part_list) {
plugin.getConfigManager().saveResource("contents" + File.separator + "nameplates" + File.separator + name + part);
}
}
}
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.api.mechanic.character; package net.momirealms.customnameplates.api.feature.pack;
public class CharacterArranger { public class CharacterArranger {

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.pack;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
public interface ResourcePackManager extends Reloadable {
void generate();
}

View File

@@ -0,0 +1,615 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.pack;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import net.momirealms.customnameplates.api.feature.OffsetFont;
import net.momirealms.customnameplates.api.feature.advance.CharacterFontAdvanceData;
import net.momirealms.customnameplates.api.feature.background.Background;
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
import net.momirealms.customnameplates.api.feature.image.Image;
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
import net.momirealms.customnameplates.api.helper.VersionHelper;
import net.momirealms.customnameplates.api.util.CharacterUtils;
import org.apache.commons.io.FileUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class ResourcePackManagerImpl implements ResourcePackManager {
private final CustomNameplates plugin;
public ResourcePackManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void generate() {
File resourcePackFolder = new File(plugin.getDataFolder() + File.separator + "ResourcePack");
// delete the old one
this.deleteDirectory(resourcePackFolder);
// create folders
File fontFolder = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "assets" + File.separator + ConfigManager.namespace() + File.separatorChar + "font");
File texturesFolder = new File(plugin.getDataFolder(), "ResourcePack" + File.separator+ "assets" + File.separator + ConfigManager.namespace() + File.separatorChar + "textures");
if (!fontFolder.mkdirs() || !texturesFolder.mkdirs()) {
plugin.getPluginLogger().severe("Failed to generate resource pack folders");
return;
}
// save BossBars
this.saveBossBar();
// save unicodes
this.saveLegacyUnicodes();
if (!VersionHelper.isVersionNewerThan1_20_5()) {
this.generateShaders("ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, false);
this.generateShaders("ResourcePack" + File.separator + "overlay_1_20_5" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
} else {
this.generateShaders("ResourcePack" + File.separator + "overlay_1_20_5" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
try {
FileUtils.copyDirectory(
new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_20_5"),
new File(plugin.getDataFolder(), "ResourcePack")
);
FileUtils.deleteDirectory(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_20_5"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// create json object
JsonObject fontJson = new JsonObject();
JsonArray providers = new JsonArray();
fontJson.add("providers", providers);
// add offset characters
this.getOffsets(texturesFolder).forEach(providers::add);
// add nameplate characters
this.getNameplates(texturesFolder).forEach(providers::add);
// add bubble characters
this.getBubbles(texturesFolder).forEach(providers::add);
// add background characters
this.getBackgrounds(texturesFolder).forEach(providers::add);
// add image characters
this.getImages(texturesFolder).forEach(providers::add);
// save json object to file
this.saveFont(fontJson);
// generate shift fonts
this.generateFont();
// set pack.mcmeta/pack.png
this.setPackFormat();
// copy the resource pack to hooked plugins
this.copyResourcePackToHookedPlugins(resourcePackFolder);
}
private void saveFont(JsonObject fontJson) {
try (FileWriter fileWriter = new FileWriter(
plugin.getDataFolder() +
File.separator + "ResourcePack" +
File.separator + "assets" +
File.separator + ConfigManager.namespace() +
File.separator + "font" +
File.separator + ConfigManager.font() + ".json")
) {
fileWriter.write(fontJson.toString().replace("\\\\", "\\"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void generateFont() {
YamlDocument document = ConfigManager.getMainConfig();
Section section = document.getSection("other-settings.shift-fonts");
if (section != null) {
for (Object key : section.getKeys()) {
if (key instanceof String font) {
JsonObject jo = new JsonObject();
JsonArray providers = new JsonArray();
jo.add("providers", providers);
List<String> order = section.getStringList(font);
for (String f : order) {
String[] split = f.split(":", 2);
Map<String, Object> properties = new HashMap<>();
if (split.length == 2) {
properties.put("shift_y", Integer.parseInt(split[1]));
}
CharacterFontAdvanceData data = plugin.getAdvanceManager().getCharacterFontData(split[0]);
if (data == null) {
plugin.getPluginLogger().warn("Font template [" + split[0] + "] not found");
continue;
}
List<JsonObject> jsonObject = data.fontProvider(properties);
if (jsonObject == null) {
plugin.getPluginLogger().warn("Font template [" + split[0] + "] doesn't support shift");
continue;
}
for (JsonObject o : jsonObject) {
providers.add(o);
}
}
try (FileWriter file = new FileWriter(new File(plugin.getDataFolder(),
"ResourcePack" +
File.separator + "assets" +
File.separator + ConfigManager.namespace() +
File.separator + "font" +
File.separator + font + ".json"))) {
file.write(jo.toString().replace("\\\\", "\\"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private void setPackFormat() {
if (VersionHelper.isVersionNewerThan1_20_5()) {
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack_1_20_5.mcmeta");
File file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack_1_20_5.mcmeta");
file.renameTo(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta"));
} else {
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack.mcmeta");
}
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack.png");
}
private void copyResourcePackToHookedPlugins(File resourcePackFolder) {
File pluginsFolder = plugin.getDataFolder().getParentFile();
if (ConfigManager.packItemsAdder()) {
File file = new File(pluginsFolder, "ItemsAdder" + File.separator + "config.yml");
YamlDocument iaConfig = plugin.getConfigManager().loadData(file);
List<String> folders = iaConfig.getStringList("resource-pack.zip.merge_other_plugins_resourcepacks_folders");
boolean changed = false;
if (!folders.contains("CustomNameplates/ResourcePack")) {
folders.add("CustomNameplates/ResourcePack");
iaConfig.set("resource-pack.zip.merge_other_plugins_resourcepacks_folders", folders);
changed = true;
}
if (changed) {
try {
iaConfig.save(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
if (ConfigManager.packItemsAdderLegacy()){
try {
FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(pluginsFolder, "ItemsAdder" + File.separator + "contents" + File.separator + "nameplates" + File.separator + "resourcepack" + File.separator + "assets") );
} catch (IOException e){
plugin.getPluginLogger().warn("Failed to copy files to ItemsAdder", e);
}
}
if (ConfigManager.packOraxen()){
try {
FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(pluginsFolder, "Oraxen" + File.separator + "pack" + File.separator + "assets"));
} catch (IOException e){
plugin.getPluginLogger().warn("Failed to copy files to Oraxen", e);
}
}
}
private List<JsonObject> getBubbles(File texturesFolder) {
ArrayList<JsonObject> list = new ArrayList<>();
if (!ConfigManager.bubbleModule()) return list;
for (Bubble bubble : plugin.getBubbleManager().getBubbles()) {
for (ConfiguredCharacter configuredChar : new ConfiguredCharacter[]{bubble.left(), bubble.middle(), bubble.right(), bubble.tail()}) {
JsonObject jo = new JsonObject();
jo.add("type", new JsonPrimitive("bitmap"));
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.bubblePath().replace("\\", "/") + configuredChar.imageFile().getName()));
jo.add("ascent", new JsonPrimitive(configuredChar.ascent()));
jo.add("height", new JsonPrimitive(configuredChar.height()));
JsonArray ja = new JsonArray();
ja.add(CharacterUtils.char2Unicode(configuredChar.character()));
jo.add("chars", ja);
list.add(jo);
try {
FileUtils.copyFile(
new File(plugin.getDataFolder(),
"contents" + File.separator + "bubbles" + File.separator + configuredChar.imageFile().getName()),
new File(texturesFolder,
ConfigManager.bubblePath().replace("\\", File.separator) + configuredChar.imageFile().getName()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return list;
}
private List<JsonObject> getBackgrounds(File texturesFolder) {
ArrayList<JsonObject> list = new ArrayList<>();
if (!ConfigManager.backgroundModule()) return list;
for (Background backGround : plugin.getBackgroundManager().getBackgrounds()) {
for (ConfiguredCharacter configuredChar : new ConfiguredCharacter[]{
backGround.left(), backGround.width_1(),
backGround.width_2(), backGround.width_4(),
backGround.width_8(), backGround.width_16(),
backGround.width_32(), backGround.width_64(),
backGround.width_128(), backGround.right()}
) {
JsonObject jo = new JsonObject();
jo.add("type", new JsonPrimitive("bitmap"));
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.backgroundPath().replace("\\", "/") + configuredChar.imageFile().getName()));
jo.add("ascent", new JsonPrimitive(configuredChar.ascent()));
jo.add("height", new JsonPrimitive(configuredChar.height()));
JsonArray ja = new JsonArray();
ja.add(CharacterUtils.char2Unicode(configuredChar.character()));
jo.add("chars", ja);
list.add(jo);
try {
FileUtils.copyFile(
new File(plugin.getDataFolder(),
"contents" + File.separator + "backgrounds" + File.separator + configuredChar.imageFile().getName()),
new File(texturesFolder,
ConfigManager.backgroundPath().replace("\\", File.separator) + configuredChar.imageFile().getName()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return list;
}
private List<JsonObject> getImages(File texturesFolder) {
ArrayList<JsonObject> list = new ArrayList<>();
if (!ConfigManager.imageModule()) return list;
for (Image image : plugin.getImageManager().getImages()) {
ConfiguredCharacter character = image.character();
JsonObject jo = new JsonObject();
jo.add("type", new JsonPrimitive("bitmap"));
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.imagePath().replace("\\", "/") + character.imageFile().getName()));
jo.add("ascent", new JsonPrimitive(character.ascent()));
jo.add("height", new JsonPrimitive(character.height()));
JsonArray ja = new JsonArray();
ja.add(CharacterUtils.char2Unicode(character.character()));
jo.add("chars", ja);
list.add(jo);
try {
FileUtils.copyFile(
new File(plugin.getDataFolder(),
"contents" + File.separator + "images" + File.separator + character.imageFile().getName()),
new File(texturesFolder,
ConfigManager.imagePath().replace("\\", File.separator) + character.imageFile().getName()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return list;
}
private List<JsonObject> getNameplates(File texturesFolder) {
ArrayList<JsonObject> list = new ArrayList<>();
if (!ConfigManager.nameplateModule()) return list;
for (Nameplate nameplate : plugin.getNameplateManager().getNameplates()) {
for (ConfiguredCharacter configuredChar : new ConfiguredCharacter[]{nameplate.left(), nameplate.middle(), nameplate.right()}) {
JsonObject jo = new JsonObject();
jo.add("type", new JsonPrimitive("bitmap"));
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.nameplatePath().replace("\\", "/") + configuredChar.imageFile().getName()));
jo.add("ascent", new JsonPrimitive(configuredChar.ascent()));
jo.add("height", new JsonPrimitive(configuredChar.height()));
JsonArray ja = new JsonArray();
ja.add(CharacterUtils.char2Unicode(configuredChar.character()));
jo.add("chars", ja);
list.add(jo);
try {
FileUtils.copyFile(
new File(plugin.getDataFolder(),
"contents" + File.separator + "nameplates" + File.separator + configuredChar.imageFile().getName()),
new File(texturesFolder,
ConfigManager.nameplatePath().replace("\\", File.separator) + configuredChar.imageFile().getName()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return list;
}
private List<JsonObject> getOffsets(File texturesFolder) {
this.saveSplit(texturesFolder);
ArrayList<JsonObject> list = new ArrayList<>();
for (OffsetFont offsetFont : OffsetFont.values()) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("type", new JsonPrimitive("bitmap"));
jsonObject.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.spaceSplitPath().replace("\\","/") + "space_split.png"));
jsonObject.add("ascent", new JsonPrimitive(-5000));
jsonObject.add("height", new JsonPrimitive(offsetFont.height()));
final JsonArray jsonArray = new JsonArray();
jsonArray.add(CharacterUtils.char2Unicode(offsetFont.character()));
jsonObject.add("chars", jsonArray);
list.add(jsonObject);
}
return list;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private void saveSplit(File texturesFolder) {
try {
plugin.getConfigManager().saveResource("space_split.png");
FileUtils.copyFile(new File(plugin.getDataFolder(),"space_split.png"), new File(texturesFolder, ConfigManager.spaceSplitPath().replace("\\", File.separator) + "space_split.png"));
File file = new File(plugin.getDataFolder(),"space_split.png");
if (file.exists()) {
file.delete();
}
} catch (IOException e){
throw new RuntimeException(e);
}
}
public void deleteDirectory(File file){
if (file.exists()) {
try {
FileUtils.deleteDirectory(file);
} catch (IOException e){
throw new RuntimeException(e);
}
}
}
private void saveBossBar() {
if (ConfigManager.bossBar1_20_2()) {
String color = ConfigManager.removedBarColor().name().toLowerCase(Locale.ENGLISH);
String path = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "gui" + File.separator + "sprites" + File.separator + "boss_bar" + File.separator;
plugin.getConfigManager().saveResource(path + color + "_background.png");
plugin.getConfigManager().saveResource(path + color + "_progress.png");
}
if (ConfigManager.bossBar1_17()) {
String path = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "gui" + File.separator + "bars.png";
plugin.getConfigManager().saveResource(path);
try {
File inputFile = new File(plugin.getDataFolder(), path);
BufferedImage image = ImageIO.read(inputFile);
int y;
switch (ConfigManager.removedBarColor()) {
case PINK -> y = 0;
case BLUE -> y = 10;
case RED -> y = 20;
case GREEN -> y = 30;
case PURPLE -> y = 50;
case WHITE -> y = 60;
default -> y = 40;
}
int width = 182;
int height = 10;
for (int i = 0; i < width; i++) {
for (int j = y; j < y + height; j++) {
image.setRGB(i, j, 0);
}
}
ImageIO.write(image, "png", inputFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private void saveLegacyUnicodes() {
if (ConfigManager.legacyUnicodes()) {
for (int i = 0; i < 256; i++) {
var path = "font" + File.separator + "unicode_page_" + String.format("%02x", i) + ".png";
var destination = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "font" + File.separator + "unicode_page_" + String.format("%02x", i) + ".png";
File imageFile = new File(plugin.getDataFolder(), path);
File destinationFile = new File(plugin.getDataFolder(), destination);
if (imageFile.exists()) {
try {
FileUtils.copyFile(imageFile, destinationFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
private void generateShaders(String path, boolean v1_20_5) {
if (!ConfigManager.enableShader()) return;
plugin.getConfigManager().saveResource(path + "rendertype_text.fsh");
plugin.getConfigManager().saveResource(path + "rendertype_text.json");
plugin.getConfigManager().saveResource(path + "rendertype_text.vsh");
String line;
StringBuilder sb1 = new StringBuilder();
File shader1 = new File(plugin.getDataFolder(), path + "rendertype_text.vsh");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(shader1), StandardCharsets.UTF_8))) {
while ((line = reader.readLine()) != null) {
sb1.append(line).append(System.lineSeparator());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
String mainShader = v1_20_5 ? ShaderConstants.Nameplates_Shader_1_20_5 : ShaderConstants.Nameplates_Shader_1_20_4;
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(shader1), StandardCharsets.UTF_8))) {
writer.write(sb1.toString()
.replace("%SHADER_0%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_Out)
.replace("%SHADER_1%", !ConfigManager.itemsAdderEffect() ? mainShader : ShaderConstants.ItemsAdder_Text_Effects + mainShader)
.replace("%SHADER_2%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_VSH)
.replace("%SHADER_3%", !ConfigManager.hideScoreBoardNumber() ? "" : ShaderConstants.Hide_ScoreBoard_Numbers)
);
} catch (IOException e) {
throw new RuntimeException(e);
}
File shader2 = new File(plugin.getDataFolder(), path + "rendertype_text.fsh");
StringBuilder sb2 = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(shader2), StandardCharsets.UTF_8))) {
while ((line = reader.readLine()) != null) {
sb2.append(line).append(System.lineSeparator());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(shader2), StandardCharsets.UTF_8))) {
writer.write(sb2.toString()
.replace("%SHADER_0%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_In)
.replace("%SHADER_1%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_FSH)
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static class ShaderConstants {
public static final String Nameplates_Shader_1_20_5 =
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.y += 1;\n" +
" vertex.x += 1;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(254., 254., 254.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.z *= 1.001;\n" +
" vertex.x *= 1.001;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(253., 254., 254.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.z *= 1.001001;\n" +
" vertex.x *= 1.001001;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" }";
public static final String Nameplates_Shader_1_20_4 =
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.y += 1;\n" +
" vertex.x += 1;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(254., 254., 254.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.z -= 0.001;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(253., 254., 254.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.z -= 0.0011;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" }";
public static final String ItemsAdder_Text_Effects =
"if (Color.xyz == vec3(255., 255., 254.) / 255.) {\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" vertexColor = ((.6 + .6 * cos(6. * (gl_Position.x + GameTime * 1000.) + vec4(0, 23, 21, 1))) + vec4(0., 0., 0., 1.)) * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" } else if (Color.xyz == vec3(255., 255., 253.) / 255.) {\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" gl_Position.y = gl_Position.y + sin(GameTime * 12000. + (gl_Position.x * 6)) / 150.;\n" +
" } else if (Color.xyz == vec3(255., 255., 252.) / 255.) {\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" vertexColor = ((.6 + .6 * cos(6. * (gl_Position.x + GameTime * 1000.) + vec4(0, 23, 21, 1))) + vec4(0., 0., 0., 1.)) * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" gl_Position.y = gl_Position.y + sin(GameTime*12000. + (gl_Position.x*6)) / 150.;\n" +
" } else if (Color.xyz == vec3(255., 255., 251.) / 255.) {\n" +
" vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" float vertexId = mod(gl_VertexID, 4.0);\n" +
" if (vertex.z <= 0.) {\n" +
" if (vertexId == 3. || vertexId == 0.) vertex.y += cos(GameTime * 12000. / 4) * 0.1;\n" +
" vertex.y += max(cos(GameTime*12000. / 4) * 0.1, 0.);\n" +
" } else {\n" +
" if (vertexId == 3. || vertexId == 0.) vertex.y -= cos(GameTime * 12000. / 4) * 3;\n" +
" vertex.y -= max(cos(GameTime*12000. / 4) * 4, 0.);\n" +
" }\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(255., 254., 254.) / 255.) {\n" +
" float vertexId = mod(gl_VertexID, 4.0);\n" +
" if (vertex.z <= 0.) {\n" +
" if (vertexId == 3. || vertexId == 0.) vertex.y += cos(GameTime * 12000. / 4) * 0.1;\n" +
" vertex.y += max(cos(GameTime*12000. / 4) * 0.1, 0.);\n" +
" } else {\n" +
" if (vertexId == 3. || vertexId == 0.) vertex.y -= cos(GameTime * 12000. / 4) * 3;\n" +
" vertex.y -= max(cos(GameTime*12000. / 4) * 4, 0.);\n" +
" }\n" +
" vertexColor = ((.6 + .6 * cos(6. * (gl_Position.x + GameTime * 1000.) + vec4(0, 23, 21, 1))) + vec4(0., 0., 0., 1.)) * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else ";
public static final String Hide_ScoreBoard_Numbers =
"\n" +
" if (Position.z == 0.0\n" +
" && gl_Position.x >= 0.94\n" +
" && gl_Position.y >= -0.35\n" +
" && vertexColor.g == 84.0/255.0\n" +
" && vertexColor.g == 84.0/255.0\n" +
" && vertexColor.r == 252.0/255.0\n" +
" && gl_VertexID <= 7\n" +
" ) {\n" +
" gl_Position = ProjMat * ModelViewMat * vec4(ScreenSize + 100.0, 0.0, ScreenSize + 100.0);\n" +
" }";
public static final String Animated_Text_FSH =
"\n" +
" vec2 p1 = round(pos1 / (posID == 0 ? 1 - coord.x : 1 - coord.y));\n" +
" vec2 p2 = round(pos2 / (posID == 0 ? coord.y : coord.x));\n" +
" ivec2 resolution = ivec2(abs(p1 - p2));\n" +
" ivec2 corner = ivec2(min(p1, p2));\n" +
" vec4 pixel = texture(Sampler0, corner / 256.0) * 255;\n" +
" if (pixel.a == 1) {\n" +
" ivec2 frames = ivec2(resolution / pixel.gb);\n" +
" vec2 uv = (texCoord0 * 256 - corner) / frames.x;\n" +
" if (uv.x > pixel.y || uv.y > pixel.z)\n" +
" discard;\n" +
" int time = int(GameTime * pixel.r * 10 * pixel.x) % int(frames.x * frames.y);\n" +
" uv = corner + mod(uv, pixel.yz) + vec2(time % frames.x, time / frames.x % frames.y) * pixel.yz;\n" +
" color = texture(Sampler0, uv / 256.0) * vertexColor * ColorModulator;\n" +
" }";
public static final String Animated_Text_VSH =
"\n" +
" pos1 = pos2 = vec2(0);\n" +
" posID = gl_VertexID % 4;\n" +
" const vec2[4] corners = vec2[4](vec2(0), vec2(0, 1), vec2(1), vec2(1, 0));\n" +
" coord = corners[posID];\n" +
" if (posID == 0) pos1 = UV0 * 256;\n" +
" if (posID == 2) pos2 = UV0 * 256;";
public static final String Animated_Text_Out =
"\n" +
"out vec2 pos1;\n" +
"out vec2 pos2;\n" +
"out vec2 coord;\n" +
"flat out int posID;\n";
public static final String Animated_Text_In =
"\n" +
"in vec2 pos1;\n" +
"in vec2 pos2;\n" +
"in vec2 coord;\n" +
"flat in int posID;\n";
}
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.network.Tracker;
import net.momirealms.customnameplates.api.util.SelfIncreaseEntityID;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.function.Consumer;
public abstract class AbstractTag implements Tag {
protected CNPlayer owner;
protected TagRenderer renderer;
protected final UUID uuid = UUID.randomUUID();
protected final int entityID = SelfIncreaseEntityID.getAndIncrease();
protected boolean isShown = false;
protected final Vector<CNPlayer> viewers = new Vector<>();
protected CNPlayer[] viewerArray = new CNPlayer[0];
public AbstractTag(CNPlayer owner, TagRenderer renderer) {
this.owner = owner;
this.renderer = renderer;
}
protected abstract List<Object> spawnPacket(CNPlayer viewer);
@Override
public void tick() {
}
@Override
public void init() {
}
@Override
public boolean canShow() {
return true;
}
@Override
public boolean canShow(CNPlayer viewer) {
return true;
}
@Override
public void show() {
if (isShown) return;
this.isShown = true;
for (CNPlayer viewer : viewerArray) {
show(viewer);
}
}
@Override
public void show(CNPlayer viewer) {
if (!isShown()) throw new IllegalStateException("This tag is currently hidden");
viewers.add(viewer);
resetViewerArray();
owner.trackPassengers(viewer, entityID);
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, spawnPacket(viewer));
}
@Override
public void hide() {
if (!isShown()) return;
for (CNPlayer viewer : viewerArray) {
hide(viewer);
}
this.isShown = false;
}
@Override
public void hide(CNPlayer viewer) {
if (!isShown()) return;
viewers.remove(viewer);
resetViewerArray();
owner.untrackPassengers(viewer, entityID);
Object packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID);
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
}
@Override
public void respawn(CNPlayer viewer) {
ArrayList<Object> packets = new ArrayList<>();
packets.add(CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID));
packets.addAll(spawnPacket(viewer));
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packets);
}
@Override
public void respawn() {
for (CNPlayer viewer : viewerArray) {
respawn(viewer);
}
}
@Override
public void updateOpacity(byte opacity) {
for (CNPlayer viewer : viewerArray) {
updateOpacity(viewer, opacity);
}
}
@Override
public void updateOpacity(CNPlayer viewer, byte opacity) {
Consumer<List<Object>> modifiers = CustomNameplates.getInstance().getPlatform().createOpacityModifier(opacity);
Object packet = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifiers));
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
}
@Override
public void updateScale(double scale) {
for (CNPlayer viewer : viewerArray) {
updateScale(viewer, scale);
}
}
@Override
public void updateScale(CNPlayer viewer, double scale) {
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createScaleModifier(scale(viewer).multiply(scale));
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation(viewer).multiply(scale));
Object packet = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier1, modifier2));
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
}
@Override
public void updateTranslation() {
for (CNPlayer player : viewerArray) {
Tracker tracker = owner.getTracker(player);
if (tracker != null) {
Consumer<List<Object>> modifier = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation(player).multiply(tracker.getScale()));
Object packet = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier));
CustomNameplates.getInstance().getPacketSender().sendPacket(player, packet);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractTag that = (AbstractTag) o;
return owner == that.owner && uuid == that.uuid;
}
@Override
public int hashCode() {
return uuid.hashCode();
}
@Override
public int entityID() {
return entityID;
}
@Override
public UUID uuid() {
return uuid;
}
protected void resetViewerArray() {
this.viewerArray = viewers.toArray(new CNPlayer[0]);
}
@Override
public boolean isShown() {
return isShown;
}
@Override
public boolean isShown(CNPlayer another) {
if (!isShown) return false;
return viewers.contains(another);
}
@Override
public byte opacity() {
return -1;
}
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.feature.DynamicText;
import net.momirealms.customnameplates.api.feature.RelationalFeature;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import net.momirealms.customnameplates.api.network.Tracker;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import net.momirealms.customnameplates.api.util.Vector3;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class NameTag extends AbstractTag implements RelationalFeature {
private final NameTagConfig config;
private int order;
private int timeLeft;
private DynamicText currentText;
public NameTag(CNPlayer owner, NameTagConfig config, TagRenderer renderer) {
super(owner, renderer);
this.config = config;
}
@Override
public void init() {
order = config.carouselTexts().length - 1;
timeLeft = 0;
}
@Override
protected List<Object> spawnPacket(CNPlayer viewer) {
String newName = currentText.render(viewer);
Object component = AdventureHelper.miniMessageToMinecraftComponent(newName);
Tracker tracker = owner.getTracker(viewer);
return CustomNameplates.getInstance().getPlatform().createTextDisplayPacket(
entityID, uuid,
owner.position().add(0,(1.8 + (affectedByCrouching() && tracker.isCrouching() && !owner.isFlying() ? -0.3 : 0) + renderer.hatOffset()) * (affectedByScaling() ? tracker.getScale() : 1),0),
0f, 0f, 0d,
0, 0, 0,
component, config.backgroundColor(), config.opacity(), config.hasShadow(), config.isSeeThrough(), config.useDefaultBackgroundColor(),
config.alignment(), config.viewRange(), config.shadowRadius(), config.shadowStrength(),
(affectedByScaling() ? scale(viewer).multiply(tracker.getScale()) : scale(viewer)),
(affectedByScaling() ? translation(viewer).multiply(tracker.getScale()) : translation(viewer)),
config.lineWidth(),
(affectedByCrouching() && tracker.isCrouching())
);
}
@Override
public void tick() {
if (timeLeft > 0)
timeLeft--;
if (timeLeft == 0) {
int triedTimes = 0;
do {
if (triedTimes == config.carouselTexts().length) {
timeLeft = 20;
currentText = null;
CustomNameplates.getInstance().getPluginLogger().warn("No text in order is available for player " + owner.name() + ". Please check your tag's conditions.");
return;
}
order++;
if (order >= config.carouselTexts().length) {
order = 0;
}
triedTimes++;
} while (
!owner.isMet(config.carouselTexts()[order].requirements())
);
CarouselText carouselText = config.carouselTexts()[order];
timeLeft = carouselText.duration();
currentText = carouselText.preParsedDynamicText().fastCreate(owner);
if (carouselText.updateOnDisplay()) {
owner.forceUpdate(currentText.placeholders(), owner.nearbyPlayers());
}
refresh();
}
}
@Override
public void notifyPlaceholderUpdates(CNPlayer p1, CNPlayer p2, boolean force) {
refresh(p2);
}
@Override
public void notifyPlaceholderUpdates(CNPlayer p1, boolean force) {
refresh();
}
@Override
public boolean canShow() {
return owner.isMet(config.ownerRequirements());
}
@Override
public boolean canShow(CNPlayer viewer) {
return viewer.isMet(owner, config.viewerRequirements());
}
public void refresh() {
for (CNPlayer viewer : viewerArray) {
refresh(viewer);
}
}
public void refresh(CNPlayer viewer) {
String newName = currentText.render(viewer);
Object component = AdventureHelper.miniMessageToMinecraftComponent(newName);
Object packet = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(CustomNameplates.getInstance().getPlatform().createTextComponentModifier(component)));
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
}
@Override
public double getTextHeight(CNPlayer viewer) {
String current = currentText.render(viewer);
Tracker tracker = viewer.getTracker(owner);
int lines = CustomNameplates.getInstance().getAdvanceManager().getLines(current, config.lineWidth());
return ((lines * (9+1) + config.translation().y()) * config.scale().y() * (config.affectedByScaling() ? tracker.getScale() : 1)) / 40;
}
@Override
public void hide() {
if (!isShown()) return;
Object packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID);
for (CNPlayer viewer : viewerArray) {
owner.untrackPassengers(viewer, entityID);
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
}
this.isShown = false;
this.viewers.clear();
resetViewerArray();
}
@Override
public Vector3 scale(CNPlayer viewer) {
return config.scale();
}
@Override
public Vector3 translation(CNPlayer viewer) {
return config.translation().add(0, renderer.hatOffset(), 0);
}
@Override
public String name() {
return "UnlimitedTag";
}
@Override
public Set<Placeholder> activePlaceholders() {
if (currentText == null || !isShown()) return Collections.emptySet();
return currentText.placeholders();
}
@Override
public Set<Placeholder> allPlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
for (CarouselText text : config.carouselTexts()) {
placeholders.addAll(text.preParsedDynamicText().placeholders());
}
return placeholders;
}
@Override
public byte opacity() {
return config.opacity();
}
@Override
public String id() {
return "nametag";
}
@Override
public boolean affectedByCrouching() {
return config.affectedByScaling();
}
@Override
public boolean affectedByScaling() {
return affectedByCrouching();
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.Alignment;
import net.momirealms.customnameplates.api.util.Vector3;
public interface NameTagConfig {
String id();
Requirement[] ownerRequirements();
Requirement[] viewerRequirements();
CarouselText[] carouselTexts();
byte opacity();
int backgroundColor();
boolean hasShadow();
boolean isSeeThrough();
boolean useDefaultBackgroundColor();
boolean affectedByCrouching();
boolean affectedByScaling();
Alignment alignment();
float viewRange();
float shadowRadius();
float shadowStrength();
Vector3 scale();
Vector3 translation();
int lineWidth();
static Builder builder() {
return new NameTagConfigImpl.BuilderImpl();
}
interface Builder {
Builder id(String id);
Builder lineWidth(int lineWidth);
Builder ownerRequirement(Requirement[] requirements);
Builder viewerRequirement(Requirement[] requirements);
Builder carouselText(CarouselText[] carouselTexts);
Builder opacity(byte opacity);
Builder backgroundColor(int backgroundColor);
Builder hasShadow(boolean hasShadow);
Builder seeThrough(boolean seeThrough);
Builder useDefaultBackgroundColor(boolean useDefaultBackgroundColor);
Builder alignment(Alignment alignment);
Builder viewRange(float viewRange);
Builder shadowRadius(float shadowRadius);
Builder shadowStrength(float shadowStrength);
Builder scale(Vector3 scale);
Builder translation(Vector3 translation);
Builder affectedByCrouching(boolean affectedByCrouching);
Builder affectedByScaling(boolean affectedByScale);
NameTagConfig build();
}
}

View File

@@ -0,0 +1,290 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.Alignment;
import net.momirealms.customnameplates.api.util.Vector3;
public class NameTagConfigImpl implements NameTagConfig {
private final String id;
private final Requirement[] ownerRequirements;
private final Requirement[] viewerRequirements;
private final CarouselText[] carouselTexts;
private final int lineWidth;
private final byte opacity;
private final int backgroundColor;
private final boolean hasShadow;
private final boolean isSeeThrough;
private final boolean useDefaultBackgroundColor;
private final Alignment alignment;
private final float viewRange;
private final float shadowRadius;
private final float shadowStrength;
private final Vector3 scale;
private final Vector3 translation;
private final boolean affectedByCrouching;
private final boolean affectedByScale;
public NameTagConfigImpl(String id, Requirement[] ownerRequirements, Requirement[] viewerRequirements, CarouselText[] carouselTexts, int lineWidth, byte opacity, int backgroundColor, boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackgroundColor, Alignment alignment, float viewRange, float shadowRadius, float shadowStrength, Vector3 scale, Vector3 translation, boolean affectedByCrouching, boolean affectedByScale) {
this.id = id;
this.ownerRequirements = ownerRequirements;
this.viewerRequirements = viewerRequirements;
this.carouselTexts = carouselTexts;
this.opacity = opacity;
this.backgroundColor = backgroundColor;
this.hasShadow = hasShadow;
this.isSeeThrough = isSeeThrough;
this.useDefaultBackgroundColor = useDefaultBackgroundColor;
this.alignment = alignment;
this.viewRange = viewRange;
this.shadowRadius = shadowRadius;
this.shadowStrength = shadowStrength;
this.scale = scale;
this.translation = translation;
this.lineWidth = lineWidth;
this.affectedByCrouching = affectedByCrouching;
this.affectedByScale = affectedByScale;
}
@Override
public String id() {
return id;
}
@Override
public Requirement[] ownerRequirements() {
return ownerRequirements;
}
@Override
public Requirement[] viewerRequirements() {
return viewerRequirements;
}
@Override
public CarouselText[] carouselTexts() {
return carouselTexts;
}
@Override
public byte opacity() {
return opacity;
}
@Override
public int backgroundColor() {
return backgroundColor;
}
@Override
public boolean hasShadow() {
return hasShadow;
}
@Override
public boolean isSeeThrough() {
return isSeeThrough;
}
@Override
public boolean useDefaultBackgroundColor() {
return useDefaultBackgroundColor;
}
@Override
public boolean affectedByCrouching() {
return affectedByCrouching;
}
@Override
public boolean affectedByScaling() {
return affectedByScale;
}
@Override
public Alignment alignment() {
return alignment;
}
@Override
public float viewRange() {
return viewRange;
}
@Override
public float shadowRadius() {
return shadowRadius;
}
@Override
public float shadowStrength() {
return shadowStrength;
}
@Override
public Vector3 scale() {
return scale;
}
@Override
public Vector3 translation() {
return translation;
}
@Override
public int lineWidth() {
return lineWidth;
}
public static class BuilderImpl implements Builder {
private String id;
private Requirement[] ownerRequirements;
private Requirement[] viewerRequirements;
private CarouselText[] carouselTexts;
private int lineWidth;
private byte opacity;
private int backgroundColor;
private boolean hasShadow;
private boolean isSeeThrough;
private boolean useDefaultBackgroundColor;
private Alignment alignment;
private float viewRange;
private float shadowRadius;
private float shadowStrength;
private Vector3 scale;
private Vector3 translation;
private boolean affectedByCrouching;
private boolean affectedByScale;
@Override
public Builder id(String id) {
this.id = id;
return this;
}
@Override
public Builder lineWidth(int lineWidth) {
this.lineWidth = lineWidth;
return this;
}
@Override
public Builder ownerRequirement(Requirement[] requirements) {
this.ownerRequirements = requirements;
return this;
}
@Override
public Builder viewerRequirement(Requirement[] requirements) {
this.viewerRequirements = requirements;
return this;
}
@Override
public Builder carouselText(CarouselText[] carouselTexts) {
this.carouselTexts = carouselTexts;
return this;
}
@Override
public Builder opacity(byte opacity) {
this.opacity = opacity;
return this;
}
@Override
public Builder backgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
return this;
}
@Override
public Builder hasShadow(boolean hasShadow) {
this.hasShadow = hasShadow;
return this;
}
@Override
public Builder seeThrough(boolean seeThrough) {
this.isSeeThrough = seeThrough;
return this;
}
@Override
public Builder useDefaultBackgroundColor(boolean useDefaultBackgroundColor) {
this.useDefaultBackgroundColor = useDefaultBackgroundColor;
return this;
}
@Override
public Builder alignment(Alignment alignment) {
this.alignment = alignment;
return this;
}
@Override
public Builder viewRange(float viewRange) {
this.viewRange = viewRange;
return this;
}
@Override
public Builder shadowRadius(float shadowRadius) {
this.shadowRadius = shadowRadius;
return this;
}
@Override
public Builder shadowStrength(float shadowStrength) {
this.shadowStrength = shadowStrength;
return this;
}
@Override
public Builder scale(Vector3 scale) {
this.scale = scale;
return this;
}
@Override
public Builder translation(Vector3 translation) {
this.translation = translation;
return this;
}
@Override
public Builder affectedByCrouching(boolean affectedByCrouching) {
this.affectedByCrouching = affectedByCrouching;
return this;
}
@Override
public Builder affectedByScaling(boolean affectedByScale) {
this.affectedByScale = affectedByScale;
return this;
}
@Override
public NameTagConfig build() {
return new NameTagConfigImpl(id, ownerRequirements, viewerRequirements, carouselTexts, lineWidth, opacity, backgroundColor, hasShadow, isSeeThrough, useDefaultBackgroundColor, alignment, viewRange, shadowRadius, shadowStrength, scale, translation, affectedByCrouching, affectedByScale);
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.util.Vector3;
import java.util.UUID;
public interface Tag {
String id();
int entityID();
UUID uuid();
boolean affectedByCrouching();
boolean affectedByScaling();
void hide();
void hide(CNPlayer viewer);
void show();
void show(CNPlayer viewer);
void respawn();
void respawn(CNPlayer viewer);
byte opacity();
void updateOpacity(byte opacity);
void updateOpacity(CNPlayer viewer, byte opacity);
boolean canShow();
boolean canShow(CNPlayer viewer);
boolean isShown();
boolean isShown(CNPlayer viewer);
void tick();
void init();
double getTextHeight(CNPlayer viewer);
void updateScale(double scale);
void updateScale(CNPlayer viewer, double scale);
void updateTranslation();
Vector3 scale(CNPlayer viewer);
Vector3 translation(CNPlayer viewer);
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) <2022> <XiaoMoMi> * Copyright (C) <2024> <XiaoMoMi>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -15,20 +15,29 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package net.momirealms.customnameplates.api.manager; package net.momirealms.customnameplates.api.feature.tag;
import org.bukkit.entity.Player; import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
public interface ActionBarManager { public interface TagRenderer {
/** double hatOffset();
* Get the actionbar sent by other plugins in MiniMessage format
* Return "" if no other actionbar received void hatOffset(double hatOffset);
*
* @param player receiver void onTick();
* @return text
*/ void destroy();
@NotNull
String getOtherPluginActionBar(Player player); void addTag(Tag tag);
Tag[] tags();
int removeTagIf(Predicate<Tag> predicate);
int tagIndex(Tag tag);
void addTag(Tag tag, int index);
void removeTag(Tag tag);
} }

View File

@@ -0,0 +1,298 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.Feature;
import net.momirealms.customnameplates.api.network.Tracker;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
public class TagRendererImpl implements TagRenderer {
private final CNPlayer owner;
private final UnlimitedTagManager manager;
private Tag[] tags;
private double hatOffset;
public TagRendererImpl(UnlimitedTagManager manager, CNPlayer owner) {
this.owner = owner;
this.manager = manager;
List<NameTag> senderList = new ArrayList<>();
for (NameTagConfig config : manager.allConfigs()) {
NameTag sender = new NameTag(owner, config, this);
senderList.add(sender);
this.owner.addFeature(sender);
}
this.tags = senderList.toArray(new Tag[0]);
}
@Override
public double hatOffset() {
return hatOffset;
}
@Override
public void hatOffset(double hatOffset) {
if (hatOffset != this.hatOffset) {
this.hatOffset = hatOffset;
for (Tag tag : tags) {
tag.updateTranslation();
}
}
}
@Override
public void onTick() {
HashSet<CNPlayer> playersToUpdatePassengers = new HashSet<>();
for (Tag display : tags) {
boolean canShow = display.canShow();
// 能大众显示
if (canShow) {
// 当前大众显示
if (display.isShown()) {
for (CNPlayer nearby : owner.nearbyPlayers()) {
// 如果已经展示了
if (display.isShown(nearby)) {
// 不满足条件就撤掉
if (!display.canShow(nearby)) {
display.hide(nearby);
}
} else {
// 未展示,则检测条件,可以就上
if (display.canShow(nearby)) {
display.show(nearby);
playersToUpdatePassengers.add(nearby);
}
}
}
// 更新一下文字顺序放在后面是为了防止已经被hide的玩家多收一个包
display.tick();
} else {
// 之前隐藏,现在开始大众显示
// 需要重置文字顺序
display.init();
// 更新一下文字顺序
display.tick();
display.show();
for (CNPlayer nearby : owner.nearbyPlayers()) {
if (display.canShow(nearby) && !display.isShown(nearby)) {
display.show(nearby);
playersToUpdatePassengers.add(nearby);
}
}
}
} else {
// 不能展示的情况
// 如果已经展示了,就咔掉所有玩家
if (display.isShown()) {
display.hide();
}
}
}
// Update passengers
Set<Integer> realPassengers = owner.passengers();
for (CNPlayer nearby : playersToUpdatePassengers) {
updatePassengers(nearby, realPassengers);
}
}
@Override
public void destroy() {
for (Tag tag : this.tags) {
tag.hide();
if (tag instanceof Feature feature) {
this.owner.removeFeature(feature);
}
}
}
public void handlePlayerRemove(CNPlayer another) {
for (Tag display : this.tags) {
if (display.isShown()) {
if (display.isShown(another)) {
display.hide(another);
}
}
}
}
@Override
public void addTag(Tag tag) {
Tag[] newTags = new Tag[this.tags.length + 1];
System.arraycopy(this.tags, 0, newTags, 0, this.tags.length);
newTags[this.tags.length] = tag;
this.tags = newTags;
if (tag instanceof Feature feature) {
this.owner.addFeature(feature);
}
}
@Override
public Tag[] tags() {
return this.tags;
}
@Override
public int removeTagIf(Predicate<Tag> predicate) {
Set<Integer> removedIndexes = new HashSet<>();
for (int i = 0; i < this.tags.length; i++) {
if (predicate.test(this.tags[i])) {
removedIndexes.add(i);
this.tags[i].hide();
if (this.tags[i] instanceof Feature feature) {
this.owner.removeFeature(feature);
}
}
}
if (removedIndexes.isEmpty()) {
return 0;
}
Tag[] newTags = new Tag[this.tags.length - removedIndexes.size()];
int newIndex = 0;
for (int i = 0; i < this.tags.length; i++) {
if (!removedIndexes.contains(i)) {
newTags[newIndex++] = this.tags[i];
}
}
this.tags = newTags;
return removedIndexes.size();
}
@Override
public int tagIndex(Tag tag) {
for (int i = 0; i < this.tags.length; i++) {
if (this.tags[i].equals(tag)) {
return i;
}
}
return -1;
}
@Override
public void addTag(Tag tag, int index) {
if (index < 0 || index > this.tags.length) {
throw new IndexOutOfBoundsException("Index out of bounds: " + index);
}
Tag[] newTags = new Tag[this.tags.length + 1];
System.arraycopy(this.tags, 0, newTags, 0, index);
newTags[index] = tag;
System.arraycopy(this.tags, index, newTags, index + 1, this.tags.length - index);
this.tags = newTags;
tag.show();
if (tag instanceof Feature feature) {
this.owner.addFeature(feature);
}
}
@Override
public void removeTag(Tag tag) {
int i = 0;
boolean has = false;
for (Tag display : this.tags) {
if (display == tag) {
has = true;
break;
}
i++;
}
if (has) {
Tag[] newTags = new Tag[this.tags.length - 1];
System.arraycopy(this.tags, 0, newTags, 0, i);
System.arraycopy(this.tags, i + 1, newTags, i, (this.tags.length - i) - 1);
this.tags = newTags;
tag.hide();
if (tag instanceof Feature feature) {
this.owner.removeFeature(feature);
}
}
}
public void handlePlayerAdd(CNPlayer another) {
boolean updatePassengers = false;
for (Tag display : this.tags) {
if (display.isShown()) {
if (!display.isShown(another)) {
if (display.canShow(another)) {
display.show(another);
updatePassengers = true;
}
}
}
}
if (updatePassengers) {
Set<Integer> realPassengers = owner.passengers();
updatePassengers(another, realPassengers);
}
}
private void updatePassengers(CNPlayer another, Set<Integer> realPassengers) {
Set<Integer> fakePassengers = owner.getTrackedPassengerIds(another);
fakePassengers.addAll(realPassengers);
int[] passengers = new int[fakePassengers.size()];
int index = 0;
for (int passenger : fakePassengers) {
passengers[index++] = passenger;
}
Object packet = CustomNameplates.getInstance().getPlatform().setPassengersPacket(owner.entityID(), passengers);
CustomNameplates.getInstance().getPacketSender().sendPacket(another, packet);
}
public void handleEntityDataChange(CNPlayer another, boolean isCrouching) {
Tracker properties = owner.getTracker(another);
// should never be null
if (properties == null) return;
properties.setCrouching(isCrouching);
for (Tag display : this.tags) {
if (display.affectedByCrouching()) {
if (display.isShown()) {
if (display.isShown(another)) {
if (isCrouching) {
display.updateOpacity(another, (byte) 64);
} else {
display.updateOpacity(another, display.opacity());
}
}
}
}
}
}
public void handleAttributeChange(CNPlayer another, double scale) {
boolean updatePassengers = false;
Tracker properties = owner.getTracker(another);
// should never be null
if (properties == null) return;
properties.setScale(scale);
for (Tag display : this.tags) {
if (display.affectedByScaling()) {
if (display.isShown()) {
if (display.isShown(another)) {
display.updateScale(another, scale);
}
}
}
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.common.plugin.feature.Reloadable;
import org.jetbrains.annotations.ApiStatus;
public interface UnlimitedTagManager extends Reloadable {
void onTick();
boolean isAlwaysShow();
NameTagConfig getConfig(String name);
NameTagConfig[] allConfigs();
void setPreviewing(CNPlayer player, boolean preview);
int previewDuration();
TagRenderer getTagRender(CNPlayer owner);
@ApiStatus.Internal
void onAddPlayer(CNPlayer owner, CNPlayer added);
@ApiStatus.Internal
void onRemovePlayer(CNPlayer owner, CNPlayer removed);
@ApiStatus.Internal
void onPlayerDataSet(CNPlayer owner, CNPlayer viewer, boolean isCrouching);
@ApiStatus.Internal
void onPlayerAttributeSet(CNPlayer owner, CNPlayer viewer, double scale);
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import net.momirealms.customnameplates.api.AbstractCNPlayer;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
import net.momirealms.customnameplates.api.helper.VersionHelper;
import net.momirealms.customnameplates.api.network.Tracker;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.Alignment;
import net.momirealms.customnameplates.api.util.ConfigUtils;
import net.momirealms.customnameplates.api.util.Vector3;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitListener {
private final CustomNameplates plugin;
private final LinkedHashMap<String, NameTagConfig> configs = new LinkedHashMap<>();
private final ConcurrentHashMap<UUID, TagRendererImpl> tagRenderers = new ConcurrentHashMap<>();
private NameTagConfig[] configArray = new NameTagConfig[0];
private int previewDuration;
private boolean alwaysShow;
public UnlimitedTagManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
}
@Override
public void setPreviewing(CNPlayer player, boolean preview) {
boolean isPreviewing = player.isPreviewing();
if (isPreviewing) {
if (preview) return;
plugin.getUnlimitedTagManager().onRemovePlayer(player, player);
player.removePlayerFromTracker(player);
((AbstractCNPlayer) player).setPreviewing(false);
} else {
if (!preview) return;
Tracker tracker = player.addPlayerToTracker(player);
tracker.setScale(player.scale());
tracker.setCrouching(player.isCrouching());
plugin.getUnlimitedTagManager().onAddPlayer(player, player);
((AbstractCNPlayer) player).setPreviewing(true);
}
}
@Override
public int previewDuration() {
return previewDuration;
}
@Override
public void onPlayerJoin(CNPlayer player) {
TagRendererImpl sender = new TagRendererImpl(this, player);
sender.onTick();
TagRendererImpl previous = tagRenderers.put(player.uuid(), sender);
if (previous != null) {
previous.destroy();
}
setPreviewing(player, isAlwaysShow());
}
@Override
public void onPlayerQuit(CNPlayer player) {
TagRendererImpl sender = tagRenderers.remove(player.uuid());
if (sender != null) {
sender.destroy();
}
}
@Override
public void load() {
if (!ConfigManager.nameplateModule()) return;
this.loadConfig();
this.resetArray();
for (CNPlayer online : plugin.getOnlinePlayers()) {
onPlayerJoin(online);
}
}
@Override
public void unload() {
for (TagRendererImpl sender : tagRenderers.values()) {
sender.destroy();
}
this.tagRenderers.clear();
this.configs.clear();
this.resetArray();
}
@Override
public void onTick() {
for (TagRendererImpl sender : tagRenderers.values()) {
sender.onTick();
}
}
@Override
public boolean isAlwaysShow() {
return alwaysShow;
}
private void resetArray() {
configArray = configs.values().toArray(new NameTagConfig[0]);
}
@Override
public NameTagConfig getConfig(String name) {
return configs.get(name);
}
@Override
public NameTagConfig[] allConfigs() {
return configArray;
}
@Override
public void onAddPlayer(CNPlayer owner, CNPlayer added) {
TagRendererImpl controller = tagRenderers.get(owner.uuid());
if (controller != null) {
controller.handlePlayerAdd(added);
}
}
@Override
public TagRenderer getTagRender(CNPlayer owner) {
return tagRenderers.get(owner.uuid());
}
@Override
public void onRemovePlayer(CNPlayer owner, CNPlayer removed) {
TagRendererImpl controller = tagRenderers.get(owner.uuid());
if (controller != null) {
controller.handlePlayerRemove(removed);
}
}
@Override
public void onPlayerDataSet(CNPlayer owner, CNPlayer viewer, boolean isCrouching) {
TagRendererImpl controller = tagRenderers.get(owner.uuid());
if (controller != null) {
controller.handleEntityDataChange(viewer, isCrouching);
}
}
@Override
public void onPlayerAttributeSet(CNPlayer owner, CNPlayer viewer, double scale) {
TagRendererImpl controller = tagRenderers.get(owner.uuid());
if (controller != null) {
controller.handleAttributeChange(viewer, scale);
}
}
private void loadConfig() {
plugin.getConfigManager().saveResource("configs" + File.separator + "nameplate.yml");
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "nameplate.yml"));
previewDuration = document.getInt("preview-duration", 5);
alwaysShow = document.getBoolean("always-show", false);
Section unlimitedSection = document.getSection("unlimited");
if (unlimitedSection == null) return;
for (Map.Entry<String, Object> entry : unlimitedSection.getStringRouteMappedValues(false).entrySet()) {
if (!(entry.getValue() instanceof Section section))
return;
Vector3 translation = ConfigUtils.vector3(section.getString("translation", "0,0,0"));
this.configs.put(entry.getKey(),
NameTagConfig.builder()
.id(entry.getKey())
.ownerRequirement(plugin.getRequirementManager().parseRequirements(section.getSection("owner-conditions")))
.viewerRequirement(plugin.getRequirementManager().parseRequirements(section.getSection("viewer-conditions")))
.translation(VersionHelper.isVersionNewerThan1_20_2() ? translation : translation.add(0,0.5,0))
.scale(ConfigUtils.vector3(section.getString("scale", "1,1,1")))
.alignment(Alignment.valueOf(section.getString("alignment", "CENTER")))
.viewRange(section.getFloat("view-range", 1f))
.shadowRadius(section.getFloat("shadow-radius", 0f))
.shadowStrength(section.getFloat("shadow-strength", 1f))
.lineWidth(section.getInt("line-width", 200))
.hasShadow(section.getBoolean("has-shadow", false))
.seeThrough(section.getBoolean("is-see-through", false))
.opacity(section.getByte("opacity", (byte) -1))
.useDefaultBackgroundColor(section.getBoolean("use-default-background-color", false))
.backgroundColor(ConfigUtils.argb(section.getString("background-color", "64,0,0,0")))
.affectedByCrouching(section.getBoolean("affected-by-crouching", true))
.affectedByScaling(section.getBoolean("affected-by-scale-attribute", true))
.carouselText(
section.contains("text") ?
new CarouselText[]{new CarouselText(-1, new Requirement[0], section.getString("text"), false)} :
ConfigUtils.carouselTexts(section.getSection("text-display-order"))
)
.build()
);
}
}
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.helper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Helper class for handling Adventure components and related functionalities.
*/
public class AdventureHelper {
private final MiniMessage miniMessage;
private final MiniMessage miniMessageStrict;
private final GsonComponentSerializer gsonComponentSerializer;
public static boolean legacySupport = false;
private final Cache<String, Component> miniMessageToComponentCache;
private final Cache<String, Object> miniMessageToMinecraftComponentCache;
private final Cache<Object, String> minecraftComponentToMiniMessageCache;
private final Cache<String, String> jsonToMiniMessageCache;
private AdventureHelper() {
this.miniMessage = MiniMessage.builder().build();
this.miniMessageStrict = MiniMessage.builder().strict(true).build();
this.gsonComponentSerializer = GsonComponentSerializer.builder().build();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4, r -> {
Thread thread = Executors.defaultThreadFactory().newThread(r);
thread.setName("customnameplates-scheduler");
return thread;
});
executor.setRemoveOnCancelPolicy(true);
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
this.miniMessageToComponentCache =
Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.executor(executor)
.maximumSize(512)
.build();
this.miniMessageToMinecraftComponentCache =
Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.executor(executor)
.maximumSize(512)
.build();
this.minecraftComponentToMiniMessageCache =
Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.executor(executor)
.maximumSize(512)
.build();
this.jsonToMiniMessageCache =
Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.executor(executor)
.maximumSize(512)
.build();
}
private static class SingletonHolder {
private static final AdventureHelper INSTANCE = new AdventureHelper();
}
/**
* Retrieves the singleton instance of AdventureHelper.
*
* @return the singleton instance
*/
public static AdventureHelper getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* Converts a MiniMessage string to a Component.
*
* @param miniMessage the MiniMessage string
* @return the resulting Component
*/
public static Component miniMessage(String miniMessage) {
AdventureHelper instance = getInstance();
return instance.miniMessageToComponentCache.get(miniMessage, (text) -> {
if (legacySupport) {
return miniMessage().deserialize(legacyToMiniMessage(text));
} else {
return miniMessage().deserialize(text);
}
});
}
/**
* Converts a json string to a MiniMessage string.
*
* @param json the JSON string
* @return the MiniMessage string representation
*/
public static String jsonToMiniMessage(String json) {
AdventureHelper instance = getInstance();
return instance.jsonToMiniMessageCache.get(json, (rawJson) ->
instance.miniMessageStrict.serialize(instance.gsonComponentSerializer.deserialize(json))
);
}
public static Object miniMessageToMinecraftComponent(String miniMessage) {
AdventureHelper instance = getInstance();
return instance.miniMessageToMinecraftComponentCache.get(miniMessage, (text) -> {
String json = instance.gsonComponentSerializer.serialize(miniMessage(text));
return CustomNameplates.getInstance().getPlatform().jsonToMinecraftComponent(json);
});
}
public static Object miniMessageToMinecraftComponent(String miniMessage, String name, String objective) {
AdventureHelper instance = getInstance();
return instance.miniMessageToMinecraftComponentCache.get(miniMessage, (text) -> {
String json = instance.gsonComponentSerializer.serialize(Component.score().name(name).objective(objective).build().append(miniMessage(text)));
return CustomNameplates.getInstance().getPlatform().jsonToMinecraftComponent(json);
});
}
public static String minecraftComponentToMiniMessage(Object component) {
AdventureHelper instance = getInstance();
return instance.minecraftComponentToMiniMessageCache.get(component, (object) -> {
String json = CustomNameplates.getInstance().getPlatform().minecraftComponentToJson(object);
return instance.miniMessageStrict.serialize(instance.gsonComponentSerializer.deserialize(json));
});
}
/**
* Retrieves the MiniMessage instance.
*
* @return the MiniMessage instance
*/
public static MiniMessage miniMessage() {
return getInstance().miniMessage;
}
/**
* Retrieves the GsonComponentSerializer instance.
*
* @return the GsonComponentSerializer instance
*/
public static GsonComponentSerializer gson() {
return getInstance().gsonComponentSerializer;
}
public static String surroundWithNameplatesFont(String text) {
return surroundWithMiniMessageFont(text, ConfigManager.namespace() + ":" + ConfigManager.font());
}
public static String removeShadowTricky(String text) {
return "<#FFFEFD>" + text + "</#FFFEFD>";
}
/**
* Surrounds text with a MiniMessage font tag.
*
* @param text the text to surround
* @param font the font as a {@link Key}
* @return the text surrounded by the MiniMessage font tag
*/
public static String surroundWithMiniMessageFont(String text, Key font) {
return "<font:" + font.asString() + ">" + text + "</font>";
}
/**
* Surrounds text with a MiniMessage font tag.
*
* @param text the text to surround
* @param font the font as a {@link String}
* @return the text surrounded by the MiniMessage font tag
*/
public static String surroundWithMiniMessageFont(String text, String font) {
return "<font:" + font + ">" + text + "</font>";
}
/**
* Checks if a character is a legacy color code.
*
* @param c the character to check
* @return true if the character is a color code, false otherwise
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public static boolean isLegacyColorCode(char c) {
return c == '§' || c == '&';
}
/**
* Strips the minimessage tags
*
* @param text text
* @return the stripped texts
*/
public static String stripTags(String text) {
return getInstance().miniMessage.stripTags(text);
}
/**
* Converts a legacy color code string to a MiniMessage string.
*
* @param legacy the legacy color code string
* @return the MiniMessage string representation
*/
public static String legacyToMiniMessage(String legacy) {
StringBuilder stringBuilder = new StringBuilder();
char[] chars = legacy.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (!isLegacyColorCode(chars[i])) {
stringBuilder.append(chars[i]);
continue;
}
if (i + 1 >= chars.length) {
stringBuilder.append(chars[i]);
continue;
}
switch (chars[i+1]) {
case '0' -> stringBuilder.append("<black>");
case '1' -> stringBuilder.append("<dark_blue>");
case '2' -> stringBuilder.append("<dark_green>");
case '3' -> stringBuilder.append("<dark_aqua>");
case '4' -> stringBuilder.append("<dark_red>");
case '5' -> stringBuilder.append("<dark_purple>");
case '6' -> stringBuilder.append("<gold>");
case '7' -> stringBuilder.append("<gray>");
case '8' -> stringBuilder.append("<dark_gray>");
case '9' -> stringBuilder.append("<blue>");
case 'a' -> stringBuilder.append("<green>");
case 'b' -> stringBuilder.append("<aqua>");
case 'c' -> stringBuilder.append("<red>");
case 'd' -> stringBuilder.append("<light_purple>");
case 'e' -> stringBuilder.append("<yellow>");
case 'f' -> stringBuilder.append("<white>");
case 'r' -> stringBuilder.append("<r><!i>");
case 'l' -> stringBuilder.append("<b>");
case 'm' -> stringBuilder.append("<st>");
case 'o' -> stringBuilder.append("<i>");
case 'n' -> stringBuilder.append("<u>");
case 'k' -> stringBuilder.append("<obf>");
case 'x' -> {
if (i + 13 >= chars.length
|| !isLegacyColorCode(chars[i+2])
|| !isLegacyColorCode(chars[i+4])
|| !isLegacyColorCode(chars[i+6])
|| !isLegacyColorCode(chars[i+8])
|| !isLegacyColorCode(chars[i+10])
|| !isLegacyColorCode(chars[i+12])) {
stringBuilder.append(chars[i]);
continue;
}
stringBuilder
.append("<#")
.append(chars[i+3])
.append(chars[i+5])
.append(chars[i+7])
.append(chars[i+9])
.append(chars[i+11])
.append(chars[i+13])
.append(">");
i += 12;
}
default -> {
stringBuilder.append(chars[i]);
continue;
}
}
i++;
}
return stringBuilder.toString();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.helper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Helper class for managing Gson instances.
*/
public class GsonHelper {
private final Gson gson;
public GsonHelper() {
this.gson = new GsonBuilder()
.create();
}
/**
* Retrieves the Gson instance.
*
* @return the Gson instance
*/
public Gson getGson() {
return gson;
}
/**
* Retrieves the singleton Gson instance from GsonHelper.
*
* @return the singleton Gson instance
*/
public static Gson get() {
return SingletonHolder.INSTANCE.getGson();
}
/**
* Static inner class for holding the singleton instance of GsonHelper.
*/
private static class SingletonHolder {
private static final GsonHelper INSTANCE = new GsonHelper();
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.helper;
import net.momirealms.customnameplates.common.plugin.NameplatesPlugin;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
/**
* This class implements the VersionManager interface and is responsible for managing version-related information.
*/
public class VersionHelper {
// Method to asynchronously check for plugin updates
public static final Function<NameplatesPlugin, CompletableFuture<Boolean>> UPDATE_CHECKER = (plugin) -> {
CompletableFuture<Boolean> updateFuture = new CompletableFuture<>();
plugin.getScheduler().async().execute(() -> {
try {
URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2723&key=version");
URLConnection conn = url.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(60000);
InputStream inputStream = conn.getInputStream();
String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine();
String current = plugin.getPluginVersion();
inputStream.close();
if (!compareVer(newest, current)) {
updateFuture.complete(false);
return;
}
updateFuture.complete(true);
} catch (Exception exception) {
plugin.getPluginLogger().warn("Error occurred when checking update.");
updateFuture.completeExceptionally(exception);
}
});
return updateFuture;
};
private static float version;
private static boolean mojmap;
private static boolean folia;
public static void init(String serverVersion) {
String[] split = serverVersion.split("\\.");
version = Float.parseFloat(split[1] + "." + (split.length == 3 ? split[2] : "0"));
checkMojMap();
checkFolia();
}
public static float version() {
return version;
}
private static void checkMojMap() {
// Check if the server is Mojmap
try {
Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket");
mojmap = true;
} catch (ClassNotFoundException ignored) {
}
}
private static void checkFolia() {
try {
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
folia = true;
} catch (ClassNotFoundException ignored) {
}
}
public static boolean isVersionNewerThan1_20_5() {
return version >= 20.49;
}
public static boolean isVersionNewerThan1_20_4() {
return version >= 20.39;
}
public static boolean isVersionNewerThan1_19_4() {
return version >= 19.39;
}
public static boolean isVersionNewerThan1_20_2() {
return version >= 20.19;
}
public static boolean isVersionNewerThan1_20() {
return version >= 20;
}
public static boolean isFolia() {
return folia;
}
public static boolean isMojmap() {
return mojmap;
}
// Method to compare two version strings
private static boolean compareVer(String newV, String currentV) {
if (newV == null || currentV == null || newV.isEmpty() || currentV.isEmpty()) {
return false;
}
String[] newVS = newV.split("\\.");
String[] currentVS = currentV.split("\\.");
int maxL = Math.min(newVS.length, currentVS.length);
for (int i = 0; i < maxL; i++) {
try {
String[] newPart = newVS[i].split("-");
String[] currentPart = currentVS[i].split("-");
int newNum = Integer.parseInt(newPart[0]);
int currentNum = Integer.parseInt(currentPart[0]);
if (newNum > currentNum) {
return true;
} else if (newNum < currentNum) {
return false;
} else if (newPart.length > 1 && currentPart.length > 1) {
String[] newHotfix = newPart[1].split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
String[] currentHotfix = currentPart[1].split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
if (newHotfix.length == 2 && currentHotfix.length == 1) return true;
else if (newHotfix.length > 1 && currentHotfix.length > 1) {
int newHotfixNum = Integer.parseInt(newHotfix[1]);
int currentHotfixNum = Integer.parseInt(currentHotfix[1]);
if (newHotfixNum > currentHotfixNum) {
return true;
} else if (newHotfixNum < currentHotfixNum) {
return false;
} else {
return newHotfix[0].compareTo(currentHotfix[0]) > 0;
}
}
} else if (newPart.length > 1) {
return true;
} else if (currentPart.length > 1) {
return false;
}
}
catch (NumberFormatException ignored) {
return false;
}
}
return newVS.length > currentVS.length;
}
}

View File

@@ -1,191 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public interface AdventureManager {
/**
* Strip all the tags from text
*
* @param text text
* @return stripped
*/
String stripTags(String text);
/**
* Get component from text
*
* @param text text
* @return component
*/
Component getComponentFromMiniMessage(String text);
/**
* Send a message to a command sender
*
* @param sender sender
* @param msg message
*/
void sendMessage(CommandSender sender, String msg);
/**
* Send a message with prefix to a command sender
*
* @param sender sender
* @param msg message
*/
void sendMessageWithPrefix(CommandSender sender, String msg);
/**
* Send a message to console
*
* @param msg message
*/
void sendConsoleMessage(String msg);
/**
* Send a message to a player
*
* @param player player
* @param msg message
*/
void sendPlayerMessage(Player player, String msg);
/**
* Send a title
*
* @param player player
* @param title title
* @param subtitle subtitle
* @param in in (ms)
* @param duration duration (ms)
* @param out out (ms)
*/
void sendTitle(Player player, String title, String subtitle, int in, int duration, int out);
/**
* Send a title
*
* @param player player
* @param title title
* @param subtitle subtitle
* @param in in (ms)
* @param duration duration (ms)
* @param out out (ms)
*/
void sendTitle(Player player, Component title, Component subtitle, int in, int duration, int out);
/**
* Send an actionbar
*
* @param player player
* @param msg msg
*/
void sendActionbar(Player player, String msg);
/**
* Send an actionbar
*
* @param player player
* @param component component
*/
void sendActionbar(Player player, Component component);
/**
* Play a sound to a player
*
* @param player player
* @param source sound source
* @param key sound key
* @param volume volume
* @param pitch pitch
*/
void sendSound(Player player, Sound.Source source, Key key, float volume, float pitch);
/**
* Play a sound to a player
*
* @param player player
* @param sound sound
*/
void sendSound(Player player, Sound sound);
/**
* Replace legacy color codes to MiniMessage format
*
* @param legacy legacy text
* @return MiniMessage format text
*/
String legacyToMiniMessage(String legacy);
/**
* If a char is legacy color code
*
* @param c char
* @return is legacy color
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean isColorCode(char c);
/**
* Convert a color into decimal
*
* @param color color
* @return decimal
*/
int colorToDecimal(ChatColor color);
/**
* Get legacy format text
*
* @param component component
* @return legacy format text
*/
String componentToLegacy(Component component);
/**
* Get json by component
*
* @param component component
* @return json
*/
String componentToJson(Component component);
/**
* Get MiniMessage format text from component
*
* @param component component
* @return text
*/
String getMiniMessageFormat(Component component);
/**
* Get IChatComponent from Json
*
* @param json json
* @return component
*/
Object getIChatComponent(String json);
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.background.BackGround;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface BackGroundManager {
/**
* Get a background's config by key
*
* @param key key
* @return background
*/
@Nullable
BackGround getBackGround(@NotNull String key);
/**
* Get all the backgrounds
*
* @return backgrounds
*/
Collection<BackGround> getBackGrounds();
/**
* Register a background into the plugin
* This will fail if there already exists one with the same key
*
* @param key key
* @param backGround background
* @return success or not
*/
boolean registerBackGround(@NotNull String key, @NotNull BackGround backGround);
/**
* Unregister a background by key
* This will fail if the key doesn't exist
*
* @param key key
* @return success or not
*/
boolean unregisterBackGround(@NotNull String key);
}

View File

@@ -1,153 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.bubble.Bubble;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
public interface BubbleManager {
/**
* Set a custom chat provider
*
* @param provider provider
* @return success or not
*/
boolean setCustomChatProvider(AbstractChatProvider provider);
/**
* Remove a custom chat provider
*
* @return success or not
*/
boolean removeCustomChatProvider();
/**
* Register a bubble into map
*
* @param key key
* @param bubble bubble
* @return success or not
*/
boolean registerBubble(String key, Bubble bubble);
/**
* Unregister a bubble from map
*
* @param key key
* @return success or not
*/
boolean unregisterBubble(String key);
/**
* Get a bubble from map
*
* @param bubble key
* @return bubble
*/
@Nullable Bubble getBubble(String bubble);
/**
* If a player has a certain bubble
*
* @param player player
* @param bubble key
* @return
*/
boolean hasBubble(Player player, String bubble);
/**
* Get a list of the bubbles that a player has
*
* @param player player
* @return bubbles' keys
*/
List<String> getAvailableBubbles(Player player);
/**
* Get a list of the bubbles' display names that a player has
*
* @param player player
* @return bubbles' display names
*/
List<String> getAvailableBubblesDisplayNames(Player player);
/**
* Get blacklist chat channels
*
* @return channels
*/
String[] getBlacklistChannels();
/**
* Get all the bubbles
*
* @return bubbles
*/
Collection<Bubble> getBubbles();
/**
* Whether a bubble exists
*/
boolean containsBubble(String key);
/**
* Equip a bubble for a player
*
* @param player player
* @param bubble bubble
* @return success or not
*/
boolean equipBubble(Player player, String bubble);
/**
* Unequip a bubble for a player
*
* @param player player
*/
void unEquipBubble(Player player);
/**
* Get the default bubble key
*
* @return bubble key
*/
String getDefaultBubble();
void onChat(Player player, String text, String channel);
/**
* Get all the bubbles' keys
*
* @return keys
*/
Collection<String> getBubbleKeys();
/**
* Trigger chat
*
* @param player player
* @param text text
*/
void onChat(Player player, String text);
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface ImageManager {
/**
* Get an image by key
*
* @param key key
* @return image
*/
@Nullable
ConfiguredChar getImage(@NotNull String key);
/**
* Get all the images
*
* @return images
*/
Collection<ConfiguredChar> getImages();
/**
* Register am image into the plugin
* This will fail if there already exists one with the same key
*
* @param key key
* @param configuredChar image
* @return success or not
*/
boolean registerImage(@NotNull String key, @NotNull ConfiguredChar configuredChar);
/**
* Unregister an image by key
* This will fail if the key doesn't exist
*
* @param key key
* @return success or not
*/
boolean unregisterImage(@NotNull String key);
}

View File

@@ -1,316 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.nameplate.CachedNameplate;
import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate;
import net.momirealms.customnameplates.api.mechanic.nameplate.TagMode;
import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer;
import net.momirealms.customnameplates.common.team.TeamColor;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
public interface NameplateManager {
/**
* Get the default nameplate
*/
@NotNull
String getDefaultNameplate();
void handlePlayerJoin(Player player);
void handlePlayerQuit(Player player);
/**
* Put an entity's ID to map
* This map is used for quickly getting the entity instance
* Removal from the map is necessary when the entity is invalid
* Otherwise it would cause memory leak
*
* @param entityID entityID
* @param entity entity
*/
boolean putEntityIDToMap(int entityID, @NotNull Entity entity);
/**
* Remove the entity from map
*
* @param entityID entityID
* @return the removed entity
*/
@Nullable
Entity removeEntityIDFromMap(int entityID);
/**
* Nameplates are cached in memory so they would not be frequently updated
* The update rate is decided by "refresh-frequency" in nameplate.yml
*
* @param uuid player uuid
* @param nameplate cached nameplate
*/
boolean putCachedNameplateToMap(@NotNull UUID uuid, @NotNull CachedNameplate nameplate);
/**
* Remove CachedNameplate from map
*
* @param uuid player uuid
* @return removed CachedNameplate
*/
@Nullable
CachedNameplate removeCachedNameplateFromMap(@NotNull UUID uuid);
/**
* Get player by entityID from the cache
*
* @param id entityID
* @return player
*/
@Nullable
Player getPlayerByEntityID(int id);
/**
* Get entity by entityID from the cache
*
* @param id entityID
* @return entity
*/
@Nullable
Entity getEntityByEntityID(int id);
/**
* Update a player's cached nameplate
* The nameplate is affected by "prefix" and "suffix" option
*
* @param player player
* @return if the nameplate is updated
*/
boolean updateCachedNameplate(@NotNull Player player);
/**
* Update a player's cached nameplate
* The nameplate is affected by "prefix" and "suffix" option
*
* @param player player
* @return if the nameplate is updated
*/
boolean updateCachedNameplate(@NotNull Player player, @Nullable Nameplate nameplate);
/**
* This should not be null when player's data is loaded (async process)
*
* @param player player
* @return cached nameplate
*/
@Nullable
CachedNameplate getCacheNameplate(@NotNull Player player);
/**
* Create a name tag for a player, the tag type is decided by the mode in nameplate.yml
* If mode is DISABLE, this method would return null
* The tag would be put into map automatically and you can get it by getNameplatePlayer(uuid)
*
* @param player player
* @param isJoin
*/
@Nullable
NameplatePlayer createNameTag(@NotNull Player player, boolean isJoin);
/**
* Put a nameplater player to map
*
* @param player player
*/
void putNameplatePlayerToMap(@NotNull NameplatePlayer player);
/**
* Get a nameplate player from map
*
* @param uuid uuid
* @return nameplate player
*/
@Nullable
NameplatePlayer getNameplatePlayer(@NotNull UUID uuid);
/**
* Remove nameplate player from map
*
* @param uuid uuid
* @return removed nameplate player
*/
@Nullable
NameplatePlayer removeNameplatePlayerFromMap(@NotNull UUID uuid);
/**
* Get the nameplate's prefix with text tags
*
* @param player player
* @return prefix with text tags
*/
@NotNull
String getNameplatePrefix(@NotNull Player player);
/**
* Get the nameplate's suffix with text tags
*
* @param player player
* @return suffix with text tags
*/
@NotNull
String getNameplateSuffix(@NotNull Player player);
/**
* Get the full nameplate tag
*
* @param player player
* @return nameplate tag
*/
@NotNull
String getFullNameTag(@NotNull Player player);
/**
* Register a custom nameplate into map
*
* @param key key
* @param nameplate nameplate
* @return success or not
*/
boolean registerNameplate(@NotNull String key, @NotNull Nameplate nameplate);
/**
* Unregister a nameplate from map
*
* @param key key
* @return success or not
*/
boolean unregisterNameplate(@NotNull String key);
/**
* Equip a nameplate for a player
*
* @param player player
* @param nameplateKey key
* @param temp whether save to storage
* @return success or not
*/
boolean equipNameplate(@NotNull Player player, @NotNull String nameplateKey, boolean temp);
/**
* Remove a nameplate for a player
*
* @param player player
* @param temp whether save to storage
*/
void unEquipNameplate(Player player, boolean temp);
/**
* Is team managed on proxy side
*/
boolean isProxyMode();
/**
* Get preview duration
*/
int getPreviewDuration();
/**
* Get the tag mode
*/
@NotNull
TagMode getTagMode();
/**
* Get a nameplate by key
*
* @param key key
* @return nameplate
*/
@Nullable
Nameplate getNameplate(@NotNull String key);
/**
* Get all the nameplates
*
* @return nameplates
*/
@NotNull
Collection<Nameplate> getNameplates();
/**
* Get all the nameplates' keys
*
* @return keys
*/
@NotNull
Collection<String> getNameplateKeys();
/**
* Whether a nameplate key exists
*/
boolean containsNameplate(@NotNull String key);
/**
* Get all the nameplates that the player has
*
* @param player player
* @return nameplates' keys
*/
@NotNull
List<String> getAvailableNameplates(@NotNull Player player);
/**
* If player has permission for a certain nameplate
*/
boolean hasNameplate(@NotNull Player player, @NotNull String nameplate);
/**
* Get all the nameplates' display names that the player has
*
* @param player player
* @return nameplates' display names
*/
@NotNull
List<String> getAvailableNameplateDisplayNames(@NotNull Player player);
/**
* Get the nameplate's team color
*
* @param player player
* @return team color
*/
@NotNull
TeamColor getTeamColor(@NotNull Player player);
/**
* Get team tag manager
*/
@NotNull
TeamTagManager getTeamTagManager();
/**
* Get unlimited tag manager
*/
@NotNull
UnlimitedTagManager getUnlimitedTagManager();
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.placeholder.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
public interface PlaceholderManager {
/**
* Detect all the placeholders
*
* @param text text
* @return placeholder
*/
@NotNull
List<String> detectPlaceholders(String text);
/**
* Get a static text instance
*
* @param key key
* @return static text
*/
@Nullable
StaticText getStaticText(String key);
/**
* Get all the static texts
*
* @return static texts
*/
Collection<StaticText> getStaticTexts();
/**
* Get a switch text instance
*
* @param key key
* @return switch text
*/
@Nullable
SwitchText getSwitchText(String key);
/**
* Get all the switch texts
*
* @return switch texts
*/
Collection<SwitchText> getSwitchTexts();
/**
* Get a descent text instance
*
* @param key key
* @return descent text
*/
@Nullable
DescentText getDescentText(String key);
/**
* Get all the descent texts
*
* @return descent texts
*/
Collection<DescentText> getDescentTexts();
/**
* Get a conditional text
*
* @param key key
* @return conditional text
*/
@Nullable
ConditionalText getConditionalText(String key);
/**
* Get all the conditional texts
*
* @return conditional texts
*/
Collection<ConditionalText> getConditionalTexts();
/**
* Get a nameplate text
*
* @param key key
* @return nameplate text
*/
@Nullable
NameplateText getNameplateText(String key);
/**
* Get all the nameplate texts
*
* @return nameplate texts
*/
Collection<NameplateText> getNameplateTexts();
/**
* Get a background text
*
* @param key key
* @return background text
*/
@Nullable
BackGroundText getBackGroundText(String key);
/**
* Get all the background texts
*
* @return background texts
*/
Collection<BackGroundText> getBackGroundTexts();
/**
* Get a vanilla hud
*
* @param key key
* @return vanilla hud
*/
VanillaHud getVanillaHud(String key);
/**
* Get all the vanilla huds
*
* @return vanilla huds
*/
Collection<VanillaHud> getVanillaHuds();
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.requirement.Condition;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.requirement.RequirementFactory;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface RequirementManager {
/**
* Registers a custom requirement type with its corresponding factory.
*
* @param type The type identifier of the requirement.
* @param requirementFactory The factory responsible for creating instances of the requirement.
* @return True if registration was successful, false if the type is already registered.
*/
boolean registerRequirement(String type, RequirementFactory requirementFactory);
/**
* Unregisters a custom requirement type.
*
* @param type The type identifier of the requirement to unregister.
* @return True if unregistration was successful, false if the type is not registered.
*/
boolean unregisterRequirement(String type);
/**
* Retrieves an array of requirements based on a configuration section.
*
* @param section The configuration section containing requirement definitions.
* @return An array of Requirement objects based on the configuration section
*/
@Nullable Requirement[] getRequirements(ConfigurationSection section);
/**
* Retrieves a Requirement object based on a configuration section and advanced flag.
* <p>
* requirement_1: <- section
* type: xxx
* value: xxx
*
* @param section The configuration section containing requirement definitions.
* @return A Requirement object based on the configuration section, or an EmptyRequirement if the section is null or invalid.
*/
@NotNull Requirement getRequirement(ConfigurationSection section);
/**
* Gets a requirement based on the provided type and value.
* If a valid RequirementFactory is found for the type, it is used to create the requirement.
* If no factory is found, a warning is logged, and an empty requirement instance is returned.
* <p>
* world: <- type
* - world <- value
*
* @param type The type representing the requirement type.
* @param value The value associated with the requirement.
* @return A Requirement instance based on the type and value, or an EmptyRequirement if the type is invalid.
*/
@NotNull Requirement getRequirement(String type, Object value);
/**
* Retrieves a RequirementFactory based on the specified requirement type.
*
* @param type The requirement type for which to retrieve a factory.
* @return A RequirementFactory for the specified type, or null if no factory is found.
*/
@Nullable RequirementFactory getRequirementFactory(String type);
/**
* Checks if an array of requirements is met for a given condition.
*
* @param condition The Condition object to check against the requirements.
* @param requirements An array of Requirement instances to be evaluated.
* @return True if all requirements are met, false otherwise. Returns true if the requirements array is null.
*/
static boolean isRequirementMet(Condition condition, Requirement... requirements) {
if (requirements == null) return true;
for (Requirement requirement : requirements) {
if (!requirement.isConditionMet(condition)) {
return false;
}
}
return true;
}
}

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.data.DataStorageInterface;
import net.momirealms.customnameplates.api.data.OnlineUser;
import net.momirealms.customnameplates.api.data.PlayerData;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface StorageManager {
/**
* Get online users
*
* @return online users
*/
Collection<OnlineUser> getOnlineUsers();
/**
* Get a player's data by uuid
* The player can be an offline one
*
* @param uuid uuid
* @return player data
*/
CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid);
/**
* Save online players' data
*
* @param uuid uuid
* @return success or not
*/
CompletableFuture<Boolean> saveOnlinePlayerData(UUID uuid);
/**
* Save specified data
*
* @param uuid uuid
* @param playerData playerData
* @return success or not
*/
CompletableFuture<Boolean> savePlayerData(UUID uuid, PlayerData playerData);
/**
* Get an online user by uuid
*
* @param uuid uuid
* @return online user
*/
Optional<OnlineUser> getOnlineUser(UUID uuid);
/**
* Get player data from json
*
* @param json json
* @return data
*/
@NotNull
PlayerData fromJson(String json);
/**
* Get player data from bytes
*
* @param data data
* @return data
*/
PlayerData fromBytes(byte[] data);
/**
* Convert player data to bytes
*
* @param playerData playerData
* @return bytes
*/
byte[] toBytes(PlayerData playerData);
/**
* Convert player data to json
*
* @param playerData playerData
* @return json
*/
@NotNull
String toJson(@NotNull PlayerData playerData);
DataStorageInterface getDataSource();
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.common.team.TeamColor;
import net.momirealms.customnameplates.common.team.TeamTagVisibility;
import org.bukkit.entity.Player;
public interface TeamManager {
/**
* Create team for a player
*
* @param player player
*/
void createTeam(Player player);
/**
* Remove a team for a player
*
* @param player player
*/
void removeTeam(Player player);
/**
* Update a player's team for a viewer
*/
void updateTeam(Player owner, Player viewer, String prefix, String suffix, TeamColor color, TeamTagVisibility visibility);
/**
* Get the team player in
*
* @param player player
* @param viewer viewer
* @return team name
*/
String getTeamName(Player player, Player viewer);
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.tag.team.TeamTagPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public interface TeamTagManager {
/**
* Create team tag for a player
* If failed, the return value would be null
* This happens when there already exists a team tag for a player
*
* @return team tag
*/
@NotNull
TeamTagPlayer createTagForPlayer(Player player, String prefix, String suffix);
/**
* Remove a team tag from map by uuid
*
* @param uuid uuid
* @return team tag
*/
@Nullable
TeamTagPlayer removeTeamPlayerFromMap(UUID uuid);
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* 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 <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.tag.unlimited.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public interface UnlimitedTagManager {
/**
* Create a named entity (ArmorStand) for an entity
* To apply the changes, you should add it to the EntityTagEntity
*
* @param entity entity
* @param setting setting
* @return named entity
*/
@NotNull StaticTextEntity createNamedEntity(EntityTagEntity entity, StaticTextTagSetting setting);
/**
* Create a named entity (ArmorStand) for a player
* To apply the changes, you should add it the named player instance
*
* @param player player
* @param setting setting
* @return named entity
*/
@NotNull
DynamicTextEntity createNamedEntity(EntityTagPlayer player, DynamicTextTagSetting setting);
/**
* Create or get a tag instance
*
* @param entity entity
* @return entity tag instance
*/
EntityTagEntity createOrGetTagForEntity(Entity entity);
/**
* Create or get a tag instance
*
* @param player player
* @param isJoin
* @return entity tag instance
*/
EntityTagPlayer createOrGetTagForPlayer(Player player, boolean isJoin);
}

Some files were not shown because too many files have changed in this diff Show More