diff --git a/README.md b/README.md index d223206..20f6066 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,37 @@ -# This project is undergoing a major refactoring - # Custom-Nameplates ![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) Gitbook - -CustomNameplates is a Paper plugin that provides unlimited customization for nametags, bossbars, actionbars and chat bubbles. +[![Scc Count Badge](https://sloc.xyz/github/Xiao-MoMi/Custom-Nameplates/?category=codes)](https://github.com/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) ## How to build -### Windows - #### Command Line -Install JDK 17 and set the JDK installation path to JAVA_HOME as an environment variable.\ -Start powershell and change directory to the project folder.\ -Execute ".\gradlew build" and get the jar at /target/CustomNameplates-universe-version.jar. +Install JDK 17 & 21. \ +Start terminal and change directory to the project folder.\ +Execute ".\gradlew build" and get the artifact under /target folder #### IDE -Import the project and execute gradle build action. - -##### About Proxy -If you are using a proxy, configurate the proxy in gradle.properties. Otherwise comment the lines in gradle.properties. +Import the project and execute gradle build action. \ +Get the artifact under /target folder ## Support the developer Polymart: https://polymart.org/resource/customnameplates.2543 \ -Afdian: https://afdian.net/@xiaomomi +Afdian: https://afdian.com/@xiaomomi -## Use CustomNameplates API +## CustomNameplates API ### Maven -``` +```html jitpack @@ -46,37 +39,37 @@ Afdian: https://afdian.net/@xiaomomi ``` -``` +```html com.github.Xiao-MoMi Custom-Nameplates - {LATEST} + {VERSION} provided ``` ### Gradle (Groovy) -``` +```groovy repositories { maven { url 'https://jitpack.io' } } ``` -``` +```groovy dependencies { - compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}' + compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{VERSION}' } ``` ### Gradle (Kotlin) -``` +```kotlin repositories { maven("https://jitpack.io/") } ``` -``` +```kotlin dependencies { - compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}") + compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{VERSION}") } ``` \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts index ade419c..5eed195 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,8 +1,28 @@ +plugins { + id("io.github.goooler.shadow") version "8.1.8" +} + +repositories { +} + dependencies { - compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") - compileOnly("me.clip:placeholderapi:2.11.5") implementation(project(":common")) - implementation("net.kyori:adventure-api:4.16.0") + // Adventure + implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") + // YAML + implementation(files("libs/boosted-yaml-${rootProject.properties["boosted_yaml_version"]}.jar")) + // Cache + compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}") + // Netty + compileOnly("io.netty:netty-all:4.1.113.Final") + // FOP + compileOnly("org.apache.pdfbox:fontbox:${rootProject.properties["fontbox_version"]}") + // COMMONS IO + compileOnly("commons-io:commons-io:${rootProject.properties["commons_io_version"]}") + // GSON + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") } tasks { diff --git a/api/libs/boosted-yaml-1.3.7.jar b/api/libs/boosted-yaml-1.3.7.jar new file mode 100644 index 0000000..ea489cb Binary files /dev/null and b/api/libs/boosted-yaml-1.3.7.jar differ diff --git a/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java new file mode 100644 index 0000000..5d626c7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java @@ -0,0 +1,478 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api; + +import net.momirealms.customnameplates.api.feature.Feature; +import net.momirealms.customnameplates.api.feature.TickStampData; +import net.momirealms.customnameplates.api.network.Tracker; +import net.momirealms.customnameplates.api.placeholder.Placeholder; +import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder; +import net.momirealms.customnameplates.api.placeholder.RelationalPlaceholder; +import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder; +import net.momirealms.customnameplates.api.requirement.Requirement; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; + +public abstract class AbstractCNPlayer implements CNPlayer { + + protected final CustomNameplates plugin; + protected final Object player; + + private boolean isLoaded = false; + + private boolean isPreviewing = false; + + private String equippedNameplate; + private String equippedBubble; + + /** + * 此处缓存了所有解析过的变量,Relational则是缓存了与某个玩家的关系变量,仅更新当前处于active状态的变量。 + */ + private final Map> cachedValues = new ConcurrentHashMap<>(); + private final Map>> cachedRelationalValues = new ConcurrentHashMap<>(); + + /** + * 此处缓存了所有解析过的条件,Relational则是缓存了与某个玩家的关系条件 + */ + private final Map> cachedRequirements = new ConcurrentHashMap<>(); + private final Map>> cachedRelationalRequirements = new ConcurrentHashMap<>(); + + /* + * 这里维护了一个双向的Map以方便更新对应的Feature。 + * 插件会先获取当前处于活跃状态的变量(由Feature提供),根据变量的更新情况,判断是否需要反馈到对应的Feature以便只在必要的时刻进行更新 + */ + private final Set activeFeatures = new CopyOnWriteArraySet<>(); + private final Map> placeholder2Features = new ConcurrentHashMap<>(); + private final Map> feature2Placeholders = new ConcurrentHashMap<>(); + + private final Map trackers = Collections.synchronizedMap(new WeakHashMap<>()); + + protected AbstractCNPlayer(CustomNameplates plugin, Object player) { + this.plugin = plugin; + this.player = player; + } + + /** + * 将所有处于激活状态的变量统筹起来并返回一个更新任务 + * + * @return 更新任务 + */ + @Override + public List getRefreshValueTask() { + Placeholder[] activePlaceholders = activePlaceholders(); + List placeholderWithChildren = new ArrayList<>(); + for (Placeholder placeholder : activePlaceholders) { + childrenFirstList(placeholder, placeholderWithChildren); + } + List uniquePlaceholderWithChildren = placeholderWithChildren.stream().distinct().toList(); + List placeholdersToUpdate = new ArrayList<>(); + for (Placeholder placeholder : uniquePlaceholderWithChildren) { + int interval = placeholder.refreshInterval(); + if (interval > 0 && MainTask.getTicks() % interval == 0) { + placeholdersToUpdate.add(placeholder); + } + } + return placeholdersToUpdate; + } + + @Override + public void forceUpdate(Set placeholders, Set others) { + if (placeholders.isEmpty()) return; + List placeholderWithChildren = new ArrayList<>(); + for (Placeholder placeholder : placeholders) { + childrenFirstList(placeholder, placeholderWithChildren); + } + for (Placeholder placeholder : placeholderWithChildren) { + if (placeholder instanceof PlayerPlaceholder playerPlaceholder) { + TickStampData value = getValue(placeholder); + if (value == null) { + value = new TickStampData<>(playerPlaceholder.request(this), MainTask.getTicks(), true); + setValue(placeholder, value); + continue; + } + if (value.ticks() != MainTask.getTicks()) { + String newValue = playerPlaceholder.request(this); + value.updateTicks(!value.data().equals(newValue)); + value.data(newValue); + } + } else if (placeholder instanceof RelationalPlaceholder relational) { + for (CNPlayer player : others) { + TickStampData value = getRelationalValue(placeholder, player); + if (value == null) { + value = new TickStampData<>(relational.request(this, player), MainTask.getTicks(), true); + setRelationalValue(placeholder, player, value); + continue; + } + if (value.ticks() != MainTask.getTicks()) { + String newValue = relational.request(this, player); + value.updateTicks(!value.data().equals(newValue)); + value.data(newValue); + } + } + } else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) { + TickStampData value = getValue(placeholder); + if (value == null) { + String latest; + if (MainTask.hasRequested(sharedPlaceholder.countId())) { + latest = sharedPlaceholder.getLatestValue(); + } else { + latest = sharedPlaceholder.request(); + } + value = new TickStampData<>(latest, MainTask.getTicks(), true); + setValue(placeholder, value); + continue; + } + if (value.ticks() != MainTask.getTicks()) { + String latest; + if (MainTask.hasRequested(sharedPlaceholder.countId())) { + latest = sharedPlaceholder.getLatestValue(); + } else { + latest = sharedPlaceholder.request(); + } + value.updateTicks(!value.data().equals(latest)); + value.data(latest); + } + } + } + } + + private void childrenFirstList(Placeholder placeholder, List list) { + if (placeholder.children().isEmpty()) { + list.add(placeholder); + } else { + for (Placeholder child : placeholder.children()) { + childrenFirstList(child, list); + } + list.add(placeholder); + } + } + + public void reload() { + cachedValues.clear(); + cachedRelationalValues.clear(); + activeFeatures.clear(); + placeholder2Features.clear(); + feature2Placeholders.clear(); + } + + @Override + public Set getUsedFeatures(Placeholder placeholder) { + return placeholder2Features.getOrDefault(placeholder, Collections.emptySet()); + } + + @Override + public Object player() { + return player; + } + + public void setLoaded(boolean loaded) { + isLoaded = loaded; + } + + public void setPreviewing(boolean previewing) { + isPreviewing = previewing; + } + + @Override + public boolean isPreviewing() { + return isPreviewing; + } + + @Override + public boolean isLoaded() { + return isLoaded; + } + + @Override + public void addFeature(Feature feature) { + activeFeatures.add(feature); + Set allPlaceholdersUsedInFeature = feature.allPlaceholders(); + feature2Placeholders.put(feature, allPlaceholdersUsedInFeature); + for (Placeholder placeholder : allPlaceholdersUsedInFeature) { + Set featureSet = placeholder2Features.computeIfAbsent(placeholder, k -> { + forceUpdate(Set.of(placeholder), nearbyPlayers()); + return new HashSet<>(); + }); + featureSet.add(feature); + } + } + + @Override + public void removeFeature(Feature feature) { + activeFeatures.remove(feature); + Set placeholders = feature2Placeholders.remove(feature); + if (placeholders != null) { + for (Placeholder placeholder : placeholders) { + Set featureSet = placeholder2Features.get(placeholder); + featureSet.remove(feature); + if (featureSet.isEmpty()) placeholder2Features.remove(placeholder); + } + } + } + + @Override + public void setValue(Placeholder placeholder, TickStampData value) { + cachedValues.put(placeholder.countId(), value); + } + + @Override + public boolean setValue(Placeholder placeholder, String value) { + TickStampData previous = cachedValues.get(placeholder.countId()); + int currentTicks = MainTask.getTicks(); + boolean changed = false; + if (previous != null) { + if (previous.ticks() == currentTicks) { + return false; + } + String data = previous.data(); + if (!data.equals(value)) { + changed = true; + previous.data(value); + previous.updateTicks(true); + } + } else { + changed= true; + previous = new TickStampData<>(value, currentTicks, true); + cachedValues.put(placeholder.countId(), previous); + } + return changed; + } + + @Override + public void setRelationalValue(Placeholder placeholder, CNPlayer another, TickStampData value) { + WeakHashMap> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>()); + map.put(another, value); + } + + @Override + public boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value) { + WeakHashMap> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>()); + TickStampData previous = map.get(another); + int currentTicks = MainTask.getTicks(); + boolean changed = false; + if (previous != null) { + if (previous.ticks() == currentTicks) { + return false; + } + String data = previous.data(); + if (!data.equals(value)) { + changed = true; + previous.data(value); + previous.updateTicks(true); + } + } else { + changed= true; + previous = new TickStampData<>(value, currentTicks, true); + map.put(another, previous); + } + return changed; + } + + @Override + public String getData(Placeholder placeholder) { + return Optional.ofNullable(cachedValues.get(placeholder.countId())).map(TickStampData::data).orElse(placeholder.id()); + } + + @Override + public TickStampData getValue(Placeholder placeholder) { + return cachedValues.get(placeholder.countId()); + } + + @Override + public String getRelationalData(Placeholder placeholder, CNPlayer another) { + WeakHashMap> map = cachedRelationalValues.get(placeholder.countId()); + if (map == null) { + return null; + } + return Optional.ofNullable(map.get(another)).map(TickStampData::data).orElse(placeholder.id()); + } + + @Override + public TickStampData getRelationalValue(Placeholder placeholder, CNPlayer another) { + WeakHashMap> map = cachedRelationalValues.get(placeholder.countId()); + if (map == null) { + return null; + } + return map.get(another); + } + + @Override + public Placeholder[] activePlaceholders() { + HashSet placeholders = new HashSet<>(); + for (Feature feature : activeFeatures) { + placeholders.addAll(feature.activePlaceholders()); + } + return placeholders.toArray(new Placeholder[0]); + } + + @Override + public boolean isMet(Requirement[] requirements) { + int currentTicks = MainTask.getTicks(); + for (Requirement requirement : requirements) { + TickStampData data = cachedRequirements.get(requirement); + if (data != null) { + if (data.ticks() + requirement.refreshInterval() > currentTicks) { + if (!data.data()) { + return false; + } + } else { + boolean satisfied = requirement.isSatisfied(this, this); + data.updateTicks(false); + data.data(satisfied); + if (!satisfied) { + return false; + } + } + } else { + boolean satisfied = requirement.isSatisfied(this, this); + data = new TickStampData<>(satisfied, currentTicks, true); + cachedRequirements.put(requirement, data); + if (!satisfied) { + return false; + } + } + } + return true; + } + + @Override + public boolean isMet(CNPlayer another, Requirement[] requirements) { + int currentTicks = MainTask.getTicks(); + for (Requirement requirement : requirements) { + WeakHashMap> innerMap = cachedRelationalRequirements.computeIfAbsent(requirement, k -> new WeakHashMap<>()); + TickStampData data = innerMap.get(another); + if (data != null) { + if (data.ticks() + requirement.refreshInterval() > currentTicks) { + if (!data.data()) { + return false; + } + } else { + boolean satisfied = requirement.isSatisfied(this, another); + data.updateTicks(false); + data.data(satisfied); + if (!satisfied) { + return false; + } + } + } else { + boolean satisfied = requirement.isSatisfied(this, another); + data = new TickStampData<>(satisfied, currentTicks, true); + innerMap.put(another, data); + if (!satisfied) { + return false; + } + } + } + return true; + } + + @Override + public Tracker addPlayerToTracker(CNPlayer another) { + Tracker tracker = new Tracker(another); + trackers.put(another, tracker); + for (Placeholder placeholder : activePlaceholders()) { + if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) { + String value = relationalPlaceholder.request(this, another); + setRelationalValue(placeholder, another, value); + } + } + return tracker; + } + + @Override + public void removePlayerFromTracker(CNPlayer another) { + trackers.remove(another); + } + + @Override + public Set nearbyPlayers() { + return new HashSet<>(trackers.keySet()); + } + + @Override + public void trackPassengers(CNPlayer another, int... passengers) { + Tracker tracker = trackers.get(another); + if (tracker != null) { + for (int passenger : passengers) { + tracker.addPassengerID(passenger); + } + } + } + + @Override + public void untrackPassengers(CNPlayer another, int... passengers) { + Optional.ofNullable(trackers.get(another)).ifPresent(tracker -> { + for (int passenger : passengers) { + tracker.removePassengerID(passenger); + } + }); + } + + @Override + public Set getTrackedPassengerIds(CNPlayer another) { + return Optional.ofNullable(trackers.get(another)).map(tracker -> new HashSet<>(tracker.getPassengerIDs())).orElse(new HashSet<>()); + } + + @Override + public Tracker getTracker(CNPlayer another) { + return trackers.get(another); + } + + @Override + public String equippedBubble() { + if (equippedNameplate == null) return "none"; + return equippedBubble; + } + + @Override + public void equippedBubble(String equippedBubble) { + if (!equippedBubble.equals(this.equippedBubble)) { + this.equippedBubble = equippedBubble; + // TODO 更新变量 + } + } + + @Override + public String equippedNameplate() { + if (equippedNameplate == null) return "none"; + return equippedNameplate; + } + + @Override + public void equippedNameplate(String equippedNameplate) { + if (!equippedNameplate.equals(this.equippedNameplate)) { + this.equippedNameplate = equippedNameplate; + // TODO 更新变量 + } + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + AbstractCNPlayer that = (AbstractCNPlayer) object; + return entityID() == that.entityID() && uuid().equals(that.uuid()); + } + + @Override + public int hashCode() { + return entityID(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java new file mode 100644 index 0000000..3f74c7e --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api; + +import net.momirealms.customnameplates.api.feature.Feature; +import net.momirealms.customnameplates.api.feature.TickStampData; +import net.momirealms.customnameplates.api.network.Tracker; +import net.momirealms.customnameplates.api.placeholder.Placeholder; +import net.momirealms.customnameplates.api.requirement.Requirement; +import net.momirealms.customnameplates.api.util.Vector3; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public interface CNPlayer { + + String name(); + + UUID uuid(); + + int entityID(); + + Vector3 position(); + + String world(); + + void forceUpdate(Set placeholders, Set another); + + Set getUsedFeatures(Placeholder placeholder); + + Object player(); + + double scale(); + + boolean isCrouching(); + + boolean isLoaded(); + + boolean isPreviewing(); + + boolean hasPermission(String permission); + + long playerTime(); + + List getRefreshValueTask(); + + Placeholder[] activePlaceholders(); + + boolean isOnline(); + + String getData(Placeholder placeholder); + + TickStampData getValue(Placeholder placeholder); + + String getRelationalData(Placeholder placeholder, CNPlayer another); + + TickStampData getRelationalValue(Placeholder placeholder, CNPlayer another); + + void setValue(Placeholder placeholder, TickStampData value); + + boolean setValue(Placeholder placeholder, String value); + + void setRelationalValue(Placeholder placeholder, CNPlayer another, TickStampData value); + + boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value); + + void addFeature(Feature feature); + + void removeFeature(Feature feature); + + boolean isMet(Requirement[] requirements); + + boolean isMet(CNPlayer another, Requirement[] requirements); + + Tracker addPlayerToTracker(CNPlayer another); + + void removePlayerFromTracker(CNPlayer another); + + Set nearbyPlayers(); + + void trackPassengers(CNPlayer another, int... passengers); + + void untrackPassengers(CNPlayer another, int... passengers); + + Set getTrackedPassengerIds(CNPlayer another); + + Set passengers(); + + boolean isFlying(); + + Tracker getTracker(CNPlayer another); + + String equippedBubble(); + + void equippedBubble(String equippedBubble); + + String equippedNameplate(); + + void equippedNameplate(String equippedNameplate); + + int remainingAir(); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java b/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java new file mode 100644 index 0000000..58f88f8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle; +import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag; +import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; +import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; +import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; +import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; +import dev.dejvokep.boostedyaml.utils.format.NodeRole; +import net.momirealms.customnameplates.api.feature.bossbar.BossBar; +import net.momirealms.customnameplates.api.feature.pack.CharacterArranger; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import net.momirealms.customnameplates.common.plugin.CustomNameplatesProperties; +import net.momirealms.customnameplates.common.plugin.config.ConfigLoader; +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; + +public abstract class ConfigManager implements ConfigLoader, Reloadable { + + private static YamlDocument MAIN_CONFIG; + + public static YamlDocument getMainConfig() { + if (MAIN_CONFIG == null) { + throw new IllegalStateException("Main config not loaded"); + } + return MAIN_CONFIG; + } + + private static ConfigManager instance; + + protected final CustomNameplates plugin; + + protected boolean debug; + protected boolean checkUpdate; + protected boolean metrics; + protected boolean actionbarModule; + protected boolean imageModule; + protected boolean bossBarModule; + protected boolean bubbleModule; + protected boolean nameplateModule; + protected boolean backgroundModule; + protected int defaultRefreshInterval; + protected int delaySend; + protected boolean catchOtherActionBar; + + protected String namespace; + protected String font; + + protected boolean generateOnStart; + + protected char initialChar; + + protected String imagePath; + protected String nameplatePath; + protected String bubblePath; + protected String spaceSplitPath; + protected String backgroundPath; + + protected boolean enableShader; + protected boolean hideScoreBoardNumber; + protected boolean animatedText; + protected boolean itemsAdderEffect; + + protected BossBar.Color removedBarColor; + protected boolean bossBar1_20_2; + protected boolean bossBar1_17; + + protected boolean legacyUnicodes; + + protected boolean packItemsAdder; + protected boolean packItemsAdderLegacy; + protected boolean packOraxen; + + protected boolean chatUnsafe; + protected boolean chatTR; + protected boolean chatVenture; + protected boolean chatHusk; + protected boolean chatCarbon; + protected boolean chatAdvanced; + protected boolean chatEss; + + public ConfigManager(CustomNameplates plugin) { + this.plugin = plugin; + instance = this; + } + + @Override + public void load() { + String configVersion = CustomNameplatesProperties.getValue("config"); + try (InputStream inputStream = new FileInputStream(resolveConfig("config.yml").toFile())) { + MAIN_CONFIG = YamlDocument.create( + inputStream, + plugin.getResourceStream("config.yml"), + GeneralSettings.builder() + .setRouteSeparator('.') + .setUseDefaults(false) + .build(), + LoaderSettings + .builder() + .setAutoUpdate(true) + .build(), + DumperSettings.builder() + .setEscapeUnprintable(false) + .setScalarFormatter((tag, value, role, def) -> { + if (role == NodeRole.KEY) { + return ScalarStyle.PLAIN; + } else { + return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN; + } + }) + .build(), + UpdaterSettings + .builder() + .setVersioning(new BasicVersioning("config-version")) + .addIgnoredRoute(configVersion, "other-settings.placeholder-refresh-interval", '.') + .addIgnoredRoute(configVersion, "other-settings.font-templates", '.') + .addIgnoredRoute(configVersion, "other-settings.shift-fonts", '.') + .build() + ); + MAIN_CONFIG.save(resolveConfig("config.yml").toFile()); + loadSettings(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void loadSettings() { + YamlDocument config = MAIN_CONFIG; + AdventureHelper.legacySupport = config.getBoolean("other-settings.legacy-color-code-support", true); + TranslationManager.forceLocale(TranslationManager.parseLocale(config.getString("force-locale", ""))); + + // Basics + debug = config.getBoolean("debug", false); + metrics = config.getBoolean("metrics", false); + checkUpdate = config.getBoolean("update-checker", false); + + // Modules + actionbarModule = config.getBoolean("modules.actionbars", true); + imageModule = config.getBoolean("modules.images", true); + bossBarModule = config.getBoolean("modules.bossbars", true); + bubbleModule = config.getBoolean("modules.bubbles", true); + backgroundModule = config.getBoolean("modules.backgrounds", true); + nameplateModule = config.getBoolean("modules.nameplates", true); + + // Integrations + packItemsAdder = config.getBoolean("integrations.resource-pack.ItemsAdder", false); + packItemsAdderLegacy = config.getBoolean("integrations.resource-pack.ItemsAdder-old-method", false); + if (packItemsAdder) packItemsAdderLegacy = false; + packOraxen = config.getBoolean("integrations.resource-pack.Oraxen", false); + + chatUnsafe = config.getBoolean("other-settings.unsafe-chat-event", false); + chatEss = config.getBoolean("integrations.chat.Essentials", false); + chatCarbon = config.getBoolean("integrations.chat.CarbonChat", false); + chatHusk = config.getBoolean("integrations.chat.HuskChat", false); + chatAdvanced = config.getBoolean("integrations.chat.AdvancedChat", false); + chatTR = config.getBoolean("integrations.chat.TrChat", false); + chatVenture = config.getBoolean("integrations.chat.VentureChat", false); + + // Packs + generateOnStart = !config.getBoolean("resource-pack.disable-generation-on-start", false); + namespace = config.getString("resource-pack.namespace", "nameplates"); + font = config.getString("resource-pack.font", "default"); + initialChar = config.getChar("resource-pack.initial-char", '뀁'); + CharacterArranger.reset(initialChar); + + nameplatePath = config.getString("resource-pack.image-path.nameplates", "font/nameplates/"); + backgroundPath = config.getString("resource-pack.image-path.backgrounds", "font/backgrounds/"); + imagePath = config.getString("resource-pack.image-path.images", "font/images/"); + bubblePath = config.getString("resource-pack.image-path.bubbles", "font/bubbles/"); + spaceSplitPath = config.getString("resource-pack.image-path.space-split", "font/base/"); + + enableShader = config.getBoolean("resource-pack.shader.enable", true); + hideScoreBoardNumber = config.getBoolean("resource-pack.shader.hide-scoreboard-number", false); + animatedText = config.getBoolean("resource-pack.shader.animated-text", false); + itemsAdderEffect = config.getBoolean("resource-pack.shader.ItemsAdder-text-effects", false); + + removedBarColor = BossBar.Color.valueOf(config.getString("resource-pack.transparent-bossbar.color", "YELLOW").toUpperCase(Locale.ENGLISH)); + bossBar1_20_2 = config.getBoolean("resource-pack.transparent-bossbar.1_20_2+", true); + bossBar1_17 = config.getBoolean("resource-pack.transparent-bossbar.1_17-1_20_1", true); + + legacyUnicodes = config.getBoolean("resource-pack.legacy-unicodes", true); + + // Other settings + delaySend = config.getInt("other-settings.send-delay", 0); + defaultRefreshInterval = config.getInt("other-settings.default-placeholder-refresh-interval", 10); + catchOtherActionBar = config.getBoolean("other-settings.catch-other-plugin-actionbar", true); + } + + @Override + public void unload() { + Reloadable.super.unload(); + } + + public static int delaySend() { + return instance.delaySend; + } + + public static String spaceSplitPath() { + return instance.spaceSplitPath; + } + + public static String bubblePath() { + return instance.bubblePath; + } + + public static String imagePath() { + return instance.imagePath; + } + + public static String backgroundPath() { + return instance.backgroundPath; + } + + public static String nameplatePath() { + return instance.nameplatePath; + } + + public static boolean generateOnStart() { + return instance.generateOnStart; + } + + public static String namespace() { + return instance.namespace; + } + + public static String font() { + return instance.font; + } + + public static char initialChar() { + return instance.initialChar; + } + + public static boolean actionbarModule() { + return instance.actionbarModule; + } + + public static boolean catchOtherActionBar() { + return instance.catchOtherActionBar; + } + + public static boolean packItemsAdder() { + return instance.packItemsAdder; + } + + public static boolean packItemsAdderLegacy() { + return instance.packItemsAdderLegacy; + } + + public static boolean packOraxen() { + return instance.packOraxen; + } + + public static boolean hideScoreBoardNumber() { + return instance.hideScoreBoardNumber; + } + + public static boolean animatedText() { + return instance.animatedText; + } + + public static boolean itemsAdderEffect() { + return instance.itemsAdderEffect; + } + + public static BossBar.Color removedBarColor() { + return instance.removedBarColor; + } + + public static boolean imageModule() { + return instance.imageModule; + } + + public static boolean bossbarModule() { + return instance.bossBarModule; + } + + public static boolean bubbleModule() { + return instance.bubbleModule; + } + + public static boolean backgroundModule() { + return instance.backgroundModule; + } + + public static boolean nameplateModule() { + return instance.nameplateModule; + } + + public static boolean debug() { + return instance.debug; + } + + public static boolean checkUpdate() { + return instance.checkUpdate; + } + + public static boolean metrics() { + return instance.metrics; + } + + public static int defaultRefreshInterval() { + return instance.defaultRefreshInterval; + } + + public static boolean enableShader() { + return instance.enableShader; + } + + public static boolean legacyUnicodes() { + return instance.legacyUnicodes; + } + + public static boolean bossBar1_20_2() { + return instance.bossBar1_20_2; + } + + public static boolean bossBar1_17() { + return instance.bossBar1_17; + } + + public static boolean chatUnsafe() { + return instance.chatUnsafe; + } + + public static boolean chatTrChat() { + return instance.chatTR; + } + + public static boolean chatVenture() { + return instance.chatVenture; + } + + public static boolean chatHusk() { + return instance.chatHusk; + } + + public static boolean chatEss() { + return instance.chatEss; + } + + public static boolean chatCarbon() { + return instance.chatCarbon; + } + + public static boolean chatAdvanced() { + return instance.chatAdvanced; + } + + @Override + public YamlDocument loadConfig(String filePath) { + return loadConfig(filePath, '.'); + } + + @Override + public YamlDocument loadConfig(String filePath, char routeSeparator) { + try (InputStream inputStream = new FileInputStream(resolveConfig(filePath).toFile())) { + return YamlDocument.create( + inputStream, + plugin.getResourceStream(filePath), + GeneralSettings.builder().setRouteSeparator(routeSeparator).build(), + LoaderSettings + .builder() + .setAutoUpdate(true) + .build(), + DumperSettings.builder() + .setScalarFormatter((tag, value, role, def) -> { + if (role == NodeRole.KEY) { + return ScalarStyle.PLAIN; + } else { + return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN; + } + }) + .build(), + UpdaterSettings + .builder() + .setVersioning(new BasicVersioning("config-version")) + .build() + ); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + filePath, e); + throw new RuntimeException(e); + } + } + + @Override + public YamlDocument loadData(File file) { + try (InputStream inputStream = new FileInputStream(file)) { + return YamlDocument.create(inputStream); + } catch (IOException e) { + plugin.getPluginLogger().severe("Failed to load config " + file, e); + throw new RuntimeException(e); + } + } + + protected Path resolveConfig(String filePath) { + if (filePath == null || filePath.isEmpty()) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + filePath = filePath.replace('\\', '/'); + Path configFile = plugin.getConfigDirectory().resolve(filePath); + // if the config doesn't exist, create it based on the template in the resources dir + if (!Files.exists(configFile)) { + try { + Files.createDirectories(configFile.getParent()); + } catch (IOException e) { + // ignore + } + try (InputStream is = plugin.getResourceStream(filePath)) { + if (is == null) { + throw new IllegalArgumentException("The embedded resource '" + filePath + "' cannot be found"); + } + Files.copy(is, configFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return configFile; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplates.java b/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplates.java new file mode 100644 index 0000000..0ce3f33 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplates.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api; + +import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManager; +import net.momirealms.customnameplates.api.feature.advance.AdvanceManager; +import net.momirealms.customnameplates.api.feature.background.BackgroundManager; +import net.momirealms.customnameplates.api.feature.bossbar.BossBarManager; +import net.momirealms.customnameplates.api.feature.bubble.BubbleManager; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; +import net.momirealms.customnameplates.api.feature.image.ImageManager; +import net.momirealms.customnameplates.api.feature.nameplate.NameplateManager; +import net.momirealms.customnameplates.api.feature.pack.ResourcePackManager; +import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManager; +import net.momirealms.customnameplates.api.network.PacketSender; +import net.momirealms.customnameplates.api.network.PipelineInjector; +import net.momirealms.customnameplates.api.placeholder.PlaceholderManager; +import net.momirealms.customnameplates.api.requirement.RequirementManager; +import net.momirealms.customnameplates.api.storage.StorageManager; +import net.momirealms.customnameplates.common.dependency.DependencyManager; +import net.momirealms.customnameplates.common.event.EventManager; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public abstract class CustomNameplates implements NameplatesPlugin { + + private static CustomNameplates instance; + + protected DependencyManager dependencyManager; + protected TranslationManager translationManager; + protected Consumer> debugger = (s) -> getPluginLogger().info("[DEBUG] " + s.get()); + protected ConfigManager configManager; + protected PacketSender packetSender; + protected PipelineInjector pipelineInjector; + protected PlaceholderManager placeholderManager; + protected RequirementManager requirementManager; + protected ActionBarManager actionBarManager; + protected BossBarManager bossBarManager; + protected UnlimitedTagManager unlimitedTagManager; + protected Platform platform; + protected MainTask mainTask = new MainTask(this); + protected SchedulerTask scheduledMainTask; + protected ConcurrentHashMap onlinePlayerMap = new ConcurrentHashMap<>(); + protected HashMap entityIDFastLookup = new HashMap<>(); + protected AdvanceManager advanceManager; + protected BackgroundManager backgroundManager; + protected EventManager eventManager; + protected StorageManager storageManager; + protected BubbleManager bubbleManager; + protected ChatManager chatManager; + protected ImageManager imageManager; + protected NameplateManager nameplateManager; + protected ResourcePackManager resourcePackManager; + + protected CustomNameplates() { + instance = this; + } + + @Override + public void reload() { + if (scheduledMainTask != null) + scheduledMainTask.cancel(); + scheduledMainTask = getScheduler().asyncRepeating(mainTask, 50, 50, TimeUnit.MILLISECONDS); + } + + @Override + public void disable() { + if (this.scheduledMainTask != null) this.scheduledMainTask.cancel(); + } + + @Override + public DependencyManager getDependencyManager() { + return dependencyManager; + } + + @Override + public TranslationManager getTranslationManager() { + return translationManager; + } + + @Override + public ConfigManager getConfigManager() { + return configManager; + } + + @Override + public void debug(Supplier supplier) { + this.debugger.accept(supplier); + } + + public PlaceholderManager getPlaceholderManager() { + return placeholderManager; + } + + public PacketSender getPacketSender() { + return packetSender; + } + + public RequirementManager getRequirementManager() { + return requirementManager; + } + + public ActionBarManager getActionBarManager() { + return actionBarManager; + } + + public UnlimitedTagManager getUnlimitedTagManager() { + return unlimitedTagManager; + } + + public AdvanceManager getAdvanceManager() { + return advanceManager; + } + + public BackgroundManager getBackgroundManager() { + return backgroundManager; + } + + public EventManager getEventManager() { + return eventManager; + } + + public StorageManager getStorageManager() { + return storageManager; + } + + public NameplateManager getNameplateManager() { + return nameplateManager; + } + + public ImageManager getImageManager() { + return imageManager; + } + + public BubbleManager getBubbleManager() { + return bubbleManager; + } + + public ChatManager getChatManager() { + return chatManager; + } + + public ResourcePackManager getResourcePackManager() { + return resourcePackManager; + } + + public Platform getPlatform() { + return platform; + } + + public Collection getOnlinePlayers() { + return new HashSet<>(onlinePlayerMap.values()); + } + + public CNPlayer getPlayer(UUID uuid) { + return onlinePlayerMap.get(uuid); + } + + public CNPlayer getPlayer(int entityID) { + return entityIDFastLookup.get(entityID); + } + + public static CustomNameplates getInstance() { + return instance; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplatesPlugin.java b/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplatesPlugin.java deleted file mode 100644 index 9212b3c..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/CustomNameplatesPlugin.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api; - -import net.momirealms.customnameplates.api.manager.*; -import net.momirealms.customnameplates.api.scheduler.Scheduler; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; - -public abstract class CustomNameplatesPlugin extends JavaPlugin { - - protected static CustomNameplatesPlugin instance; - protected Scheduler scheduler; - protected StorageManager storageManager; - protected VersionManager versionManager; - protected AdventureManager adventureManager; - protected RequirementManager requirementManager; - protected BossBarManager bossBarManager; - protected ImageManager imageManager; - protected PlaceholderManager placeholderManager; - protected ResourcePackManager resourcePackManager; - protected BackGroundManager backGroundManager; - protected TeamManager teamManager; - protected NameplateManager nameplateManager; - protected ActionBarManager actionBarManager; - protected WidthManager widthManager; - protected BubbleManager bubbleManager; - - protected CustomNameplatesPlugin() { - instance = this; - } - - /* Get plugin instance */ - public static CustomNameplatesPlugin getInstance() { - return instance; - } - - /* Get plugin instance */ - public static CustomNameplatesPlugin get() { - return instance; - } - - /* reload the plugin */ - public abstract void reload(); - - /* Get the scheduler */ - public Scheduler getScheduler() { - return scheduler; - } - - /* Get the storage manager */ - public StorageManager getStorageManager() { - return storageManager; - } - - /* Get the requirement manager */ - public RequirementManager getRequirementManager() { - return requirementManager; - } - - /* Get the image manager */ - public ImageManager getImageManager() { - return imageManager; - } - - /* Get the background manager */ - public BackGroundManager getBackGroundManager() { - return backGroundManager; - } - - /* Get the resource pack manager */ - public ResourcePackManager getResourcePackManager() { - return resourcePackManager; - } - - /* Get the adventure manager */ - public AdventureManager getAdventure() { - return adventureManager; - } - - /* Get the version manager */ - public VersionManager getVersionManager() { - return versionManager; - } - - /* Get the bossbar manager */ - public BossBarManager getBossBarManager() { - return bossBarManager; - } - - /* Get the placeholder manager */ - public PlaceholderManager getPlaceholderManager() { - return placeholderManager; - } - - /* Get the team manager */ - public TeamManager getTeamManager() { - return teamManager; - } - - /* Get the actionbar manager */ - public ActionBarManager getActionBarManager() { - return actionBarManager; - } - - /* Get the nameplate manager */ - public NameplateManager getNameplateManager() { - return nameplateManager; - } - - /* Get the width manager */ - public WidthManager getWidthManager() { - return widthManager; - } - - /* Get the bubble manager */ - public BubbleManager getBubbleManager() { - return bubbleManager; - } - - /* debug get config by file name */ - public abstract YamlConfiguration getConfig(String file); - - /* debug message */ - public abstract void debug(String s); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/MainTask.java b/api/src/main/java/net/momirealms/customnameplates/api/MainTask.java new file mode 100644 index 0000000..12de0f7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/MainTask.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class MainTask implements Runnable { + + // The system would break if a server hasn't stopped for 3 years + private static int TICKS = 0; + + private static final Map TIME_1 = new ConcurrentHashMap<>(2048, 1F); + private static final Map TIME_2 = new ConcurrentHashMap<>(2048, 1F); + private static final Set requestedSharedPlaceholders = Collections.synchronizedSet(new HashSet<>()); + private int timer; + + private final CustomNameplates plugin; + + public MainTask(CustomNameplates plugin) { + this.plugin = plugin; + } + + public static int getTicks() { + return TICKS; + } + + public static boolean hasRequested(int countId) { + boolean result = requestedSharedPlaceholders.contains(countId); + if (!result) requestedSharedPlaceholders.add(countId); + return result; + } + + @Override + public void run() { + TICKS++; + requestedSharedPlaceholders.clear(); + long time1 = System.nanoTime(); + plugin.actionBarManager.refreshConditions(); + plugin.bossBarManager.onTick(); + plugin.unlimitedTagManager.onTick(); + long time2 = System.nanoTime(); + plugin.placeholderManager.refreshPlaceholders(); + long time3 = System.nanoTime(); + plugin.actionBarManager.checkHeartBeats(); + int diff1 = (int) (time2 - time1); + TIME_1.put(timer, diff1); + int diff2 = (int) (time3 - time2); + TIME_2.put(timer, diff2); + timer++; + if (timer >= 1200) timer = 0; + } + + public static HealthyProfile getHealthyProfile() { + long total1 = 0; + long total2 = 0; + for (int value : TIME_1.values()) { + total1 += value; + } + for (int value : TIME_2.values()) { + total2 += value; + } + long total = total1 + total2; + double size = TIME_1.size(); + double load = total / size / 50_000_000; + return new HealthyProfile( + load, + (long) (total / size), + (int) (total1 / size), + (int) (total2 / size) + ); + } + + public static class HealthyProfile { + + private final double load; + private final long totalTimeNS; + + private final int actionBarConditionNS; + private final int refreshPlaceholderNS; + + public HealthyProfile( + double load, + long totalTimeNS, + int actionBarConditionNS, + int refreshPlaceholderNS + ) { + this.actionBarConditionNS = actionBarConditionNS; + this.refreshPlaceholderNS = refreshPlaceholderNS; + this.load = load; + this.totalTimeNS = totalTimeNS; + } + + public int getActionBarConditionNS() { + return actionBarConditionNS; + } + + public int getRefreshPlaceholderNS() { + return refreshPlaceholderNS; + } + + public double getLoad() { + return load; + } + + public long getTotalTimeNS() { + return totalTimeNS; + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/Platform.java b/api/src/main/java/net/momirealms/customnameplates/api/Platform.java new file mode 100644 index 0000000..4e4d088 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/Platform.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api; + +import net.momirealms.customnameplates.api.feature.bossbar.BossBar; +import net.momirealms.customnameplates.api.network.PacketEvent; +import net.momirealms.customnameplates.api.placeholder.Placeholder; +import net.momirealms.customnameplates.api.util.Alignment; +import net.momirealms.customnameplates.api.util.Vector3; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public interface Platform { + + Object jsonToMinecraftComponent(String json); + + String minecraftComponentToJson(Object component); + + Object vec3(double x, double y, double z); + + Placeholder registerPlatformPlaceholder(String id); + + void onPacketSend(CNPlayer player, PacketEvent event); + + Object setActionBarTextPacket(Object component); + + Object createBossBarPacket(UUID uuid, Object component, float progress, BossBar.Overlay overlay, BossBar.Color color); + + Object removeBossBarPacket(UUID uuid); + + Object updateBossBarNamePacket(UUID uuid, Object component); + + List 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> createInterpolationDelayModifier(int delay); + + Consumer> createTransformationInterpolationDurationModifier(int duration); + + Consumer> createTextComponentModifier(Object component); + + Consumer> createScaleModifier(Vector3 scale); + + Consumer> createTranslationModifier(Vector3 translation); + + Consumer> createOpacityModifier(byte opacity); + + Object updateTextDisplayPacket(int entityID, List>> modifiers); + + Object setPassengersPacket(int vehicle, int[] passengers); + + Object removeEntityPacket(int... entityID); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/data/DataStorageInterface.java b/api/src/main/java/net/momirealms/customnameplates/api/data/DataStorageInterface.java deleted file mode 100644 index 437ecc7..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/data/DataStorageInterface.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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> getPlayerData(UUID uuid); - - CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData); - - CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData); - - Set getUniqueUsers(boolean legacy); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/data/LegacyDataStorageInterface.java b/api/src/main/java/net/momirealms/customnameplates/api/data/LegacyDataStorageInterface.java deleted file mode 100644 index 60e5c4b..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/data/LegacyDataStorageInterface.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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> getLegacyPlayerData(UUID uuid); -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customnameplates/api/data/OnlineUser.java b/api/src/main/java/net/momirealms/customnameplates/api/data/OnlineUser.java deleted file mode 100644 index 3978458..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/data/OnlineUser.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/data/PlayerData.java b/api/src/main/java/net/momirealms/customnameplates/api/data/PlayerData.java deleted file mode 100644 index bf44f13..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/data/PlayerData.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/event/BubblesSpawnEvent.java b/api/src/main/java/net/momirealms/customnameplates/api/event/BubblesSpawnEvent.java deleted file mode 100644 index 1fd1552..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/event/BubblesSpawnEvent.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/event/CustomNameplatesReloadEvent.java b/api/src/main/java/net/momirealms/customnameplates/api/event/CustomNameplatesReloadEvent.java deleted file mode 100644 index fc24819..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/event/CustomNameplatesReloadEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/NameplatePlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/event/DataLoadEvent.java similarity index 69% rename from api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/NameplatePlayer.java rename to api/src/main/java/net/momirealms/customnameplates/api/event/DataLoadEvent.java index 9800628..c14908e 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/NameplatePlayer.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/event/DataLoadEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,17 +15,13 @@ * along with this program. If not, see . */ -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); - - boolean isPreviewing(); - - Player getPlayer(); - - void updateText(); + @Param(0) + PlayerData data(); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/event/NameplateDataLoadEvent.java b/api/src/main/java/net/momirealms/customnameplates/api/event/NameplateDataLoadEvent.java deleted file mode 100644 index da804cf..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/event/NameplateDataLoadEvent.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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(); - } -} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/message/MessageType.java b/api/src/main/java/net/momirealms/customnameplates/api/event/NameplatesReloadEvent.java similarity index 68% rename from common/src/main/java/net/momirealms/customnameplates/common/message/MessageType.java rename to api/src/main/java/net/momirealms/customnameplates/api/event/NameplatesReloadEvent.java index b31bc98..1c233f2 100644 --- a/common/src/main/java/net/momirealms/customnameplates/common/message/MessageType.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/event/NameplatesReloadEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,10 @@ * along with this program. If not, see . */ -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 static final String REMOVE = "1"; - public static final String UPDATE = "2"; +public interface NameplatesReloadEvent extends NameplatesEvent, Cancellable { } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/AdaptiveImage.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/AdaptiveImage.java new file mode 100644 index 0000000..4c0eae5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/AdaptiveImage.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/CarouselText.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/CarouselText.java new file mode 100644 index 0000000..79e69c4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/CarouselText.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/ChatListener.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/ChatListener.java new file mode 100644 index 0000000..6e6e31e --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/ChatListener.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature; + +import net.momirealms.customnameplates.api.CNPlayer; + +public interface ChatListener { + + void onPlayerChat(CNPlayer player, String message, String channel); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/ConfiguredCharacter.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/ConfiguredCharacter.java new file mode 100644 index 0000000..3f18eaf --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/ConfiguredCharacter.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/DynamicText.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/DynamicText.java new file mode 100644 index 0000000..4715d22 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/DynamicText.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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> texts; + private final Set placeholders; + + public DynamicText(String text, List> texts, Set placeholders) { + this.text = text; + this.texts = texts; + this.placeholders = placeholders; + } + + public Set placeholders() { + return placeholders; + } + + public String render(CNPlayer viewer) { + StringBuilder builder = new StringBuilder(); + for (Function function : texts) { + builder.append(function.apply(viewer)); + } + return builder.toString(); + } + + public String text() { + return text; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/Feature.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/Feature.java new file mode 100644 index 0000000..1e5751c --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/Feature.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.placeholder.Placeholder; + +import java.util.Set; + +public interface Feature { + + String name(); + + Set activePlaceholders(); + + Set allPlaceholders(); + + void notifyPlaceholderUpdates(CNPlayer p1, boolean force); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/JoinQuitListener.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/JoinQuitListener.java new file mode 100644 index 0000000..bde9df2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/JoinQuitListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature; + +import net.momirealms.customnameplates.api.CNPlayer; + +public interface JoinQuitListener { + + void onPlayerJoin(CNPlayer player); + + void onPlayerQuit(CNPlayer player); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/OffsetFont.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/OffsetFont.java new file mode 100644 index 0000000..9f629c2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/OffsetFont.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 negFastLookup = new HashMap<>(); + private static final HashMap 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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/PreParsedDynamicText.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/PreParsedDynamicText.java new file mode 100644 index 0000000..7b0ec17 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/PreParsedDynamicText.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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>> textFunctions = new ArrayList<>(); + private final Set 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 detectedPlaceholders = manager.detectPlaceholders(text); + List>> convertor = new ArrayList<>(detectedPlaceholders.size()); + List 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> functions = new ArrayList<>(); + for (Function> textFunction : textFunctions) { + functions.add(textFunction.apply(player)); + } + return new DynamicText(text, functions, set); + } + + public Set 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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/RelationalFeature.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/RelationalFeature.java new file mode 100644 index 0000000..6364327 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/RelationalFeature.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature; + +import net.momirealms.customnameplates.api.CNPlayer; + +public interface RelationalFeature extends Feature { + + void notifyPlaceholderUpdates(CNPlayer p1, CNPlayer p2, boolean force); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/TickStampData.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/TickStampData.java new file mode 100644 index 0000000..d05b9e0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/TickStampData.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature; + +import net.momirealms.customnameplates.api.MainTask; + +public class TickStampData { + + 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; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarConfig.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarConfig.java new file mode 100644 index 0000000..d1d1319 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarConfig.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarConfigImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarConfigImpl.java new file mode 100644 index 0000000..d0d6778 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarConfigImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarManager.java new file mode 100644 index 0000000..49b051c --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarManagerImpl.java new file mode 100644 index 0000000..5a5a632 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarManagerImpl.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 configs = new LinkedHashMap<>(); + private final ConcurrentHashMap 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 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() + ); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarSender.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarSender.java new file mode 100644 index 0000000..d51b167 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/actionbar/ActionBarSender.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 activePlaceholders() { + if (currentActionBar == null) return Collections.emptySet(); + return currentActionBar.placeholders(); + } + + @Override + public Set allPlaceholders() { + HashSet 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; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/AdvanceManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/AdvanceManager.java new file mode 100644 index 0000000..6e832e7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/AdvanceManager.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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> miniMessageToIterable(String text); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/AdvanceManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/AdvanceManagerImpl.java new file mode 100644 index 0000000..f6fc2a7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/AdvanceManagerImpl.java @@ -0,0 +1,1178 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature.advance; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.internal.parser.node.ElementNode; +import net.kyori.adventure.text.minimessage.internal.parser.node.TagNode; +import net.kyori.adventure.text.minimessage.internal.parser.node.ValueNode; +import net.kyori.adventure.text.minimessage.tag.Inserting; +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.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.AdventureHelper; +import net.momirealms.customnameplates.api.placeholder.Placeholder; +import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder; +import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder; +import net.momirealms.customnameplates.api.util.CharacterUtils; +import net.momirealms.customnameplates.common.util.Tuple; +import org.apache.commons.io.FileUtils; +import org.apache.fontbox.ttf.CmapSubtable; +import org.apache.fontbox.ttf.TTFParser; +import org.apache.fontbox.ttf.TrueTypeFont; +import org.jetbrains.annotations.Nullable; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import static java.util.Objects.requireNonNull; + +public class AdvanceManagerImpl implements AdvanceManager { + + private static final Key MINECRAFT_DEFAULT_FONT = Key.key("minecraft", "default"); + private static final Map CODE_POINTS = new HashMap<>(); + private static final Map CODE_POINT_GRIDS = new HashMap<>(); + private static final Set DEFAULT_BITMAP_IMAGES = new HashSet<>( + List.of("ascii.png", "ascii_sga.png", "asciillager.png", "nonlatin_european.png", "accented.png") + ); + private static final Set DEFAULT_UNIHEX = new HashSet<>(List.of("unifont.zip", "unifont_jp.zip")); + + static { + CODE_POINTS.put("ascii", new String[] { + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0020\\u0021\\u0022\\u0023\\u0024\\u0025\\u0026\\u0027\\u0028\\u0029\\u002a\\u002b\\u002c\\u002d\\u002e\\u002f", + "\\u0030\\u0031\\u0032\\u0033\\u0034\\u0035\\u0036\\u0037\\u0038\\u0039\\u003a\\u003b\\u003c\\u003d\\u003e\\u003f", + "\\u0040\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\\u0047\\u0048\\u0049\\u004a\\u004b\\u004c\\u004d\\u004e\\u004f", + "\\u0050\\u0051\\u0052\\u0053\\u0054\\u0055\\u0056\\u0057\\u0058\\u0059\\u005a\\u005b\\u005c\\u005d\\u005e\\u005f", + "\\u0060\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\\u0067\\u0068\\u0069\\u006a\\u006b\\u006c\\u006d\\u006e\\u006f", + "\\u0070\\u0071\\u0072\\u0073\\u0074\\u0075\\u0076\\u0077\\u0078\\u0079\\u007a\\u007b\\u007c\\u007d\\u007e\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u00a3\\u0000\\u0000\\u0192", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u00aa\\u00ba\\u0000\\u0000\\u00ac\\u0000\\u0000\\u0000\\u00ab\\u00bb", + "\\u2591\\u2592\\u2593\\u2502\\u2524\\u2561\\u2562\\u2556\\u2555\\u2563\\u2551\\u2557\\u255d\\u255c\\u255b\\u2510", + "\\u2514\\u2534\\u252c\\u251c\\u2500\\u253c\\u255e\\u255f\\u255a\\u2554\\u2569\\u2566\\u2560\\u2550\\u256c\\u2567", + "\\u2568\\u2564\\u2565\\u2559\\u2558\\u2552\\u2553\\u256b\\u256a\\u2518\\u250c\\u2588\\u2584\\u258c\\u2590\\u2580", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u2205\\u2208\\u0000", + "\\u2261\\u00b1\\u2265\\u2264\\u2320\\u2321\\u00f7\\u2248\\u00b0\\u2219\\u0000\\u221a\\u207f\\u00b2\\u25a0\\u0000" + }); + CODE_POINTS.put("ascii_sga", new String[] { + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\\u0047\\u0048\\u0049\\u004A\\u004B\\u004C\\u004D\\u004E\\u004F", + "\\u0050\\u0051\\u0052\\u0053\\u0054\\u0055\\u0056\\u0057\\u0058\\u0059\\u005A\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\\u0067\\u0068\\u0069\\u006A\\u006B\\u006C\\u006D\\u006E\\u006F", + "\\u0070\\u0071\\u0072\\u0073\\u0074\\u0075\\u0076\\u0077\\u0078\\u0079\\u007A\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", + "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" + }); + CODE_POINTS.put("asciillager", new String[] { + "\\u0021\\u002C\\u002D\\u002E\\u0030\\u0031\\u0032\\u0033\\u0034\\u0035\\u0036\\u0037\\u0038\\u0039\\u003F\\u0061", + "\\u0062\\u0063\\u0064\\u0065\\u0066\\u0067\\u0068\\u0069\\u006A\\u006B\\u006C\\u006D\\u006E\\u006F\\u0070\\u0071", + "\\u0072\\u0073\\u0074\\u0075\\u0076\\u0077\\u0078\\u0079\\u007A\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\\u0047", + "\\u0048\\u0049\\u004A\\u004B\\u004C\\u004D\\u004E\\u004F\\u0050\\u0051\\u0052\\u0053\\u0054\\u0055\\u0056\\u0057", + "\\u0058\\u0059\\u005A\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" + }); + CODE_POINTS.put("accented", new String[] { + "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf", + "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00e0\\u00e1\\u00e2\\u00e3", + "\\u00e4\\u00e5\\u00e6\\u00e7\\u00ec\\u00ed\\u00ee\\u00ef\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f9\\u00fa", + "\\u00fb\\u00fc\\u00fd\\u00ff\\u0100\\u0101\\u0102\\u0103\\u0104\\u0105\\u0106\\u0107\\u0108\\u0109\\u010a\\u010b", + "\\u010c\\u010d\\u010e\\u010f\\u0110\\u0111\\u0112\\u0113\\u0114\\u0115\\u0116\\u0117\\u0118\\u0119\\u011a\\u011b", + "\\u011c\\u011d\\u1e20\\u1e21\\u011e\\u011f\\u0120\\u0121\\u0122\\u0123\\u0124\\u0125\\u0126\\u0127\\u0128\\u0129", + "\\u012a\\u012b\\u012c\\u012d\\u012e\\u012f\\u0130\\u0131\\u0134\\u0135\\u0136\\u0137\\u0139\\u013a\\u013b\\u013c", + "\\u013d\\u013e\\u013f\\u0140\\u0141\\u0142\\u0143\\u0144\\u0145\\u0146\\u0147\\u0148\\u014a\\u014b\\u014c\\u014d", + "\\u014e\\u014f\\u0150\\u0151\\u0152\\u0153\\u0154\\u0155\\u0156\\u0157\\u0158\\u0159\\u015a\\u015b\\u015c\\u015d", + "\\u015e\\u015f\\u0160\\u0161\\u0162\\u0163\\u0164\\u0165\\u0166\\u0167\\u0168\\u0169\\u016a\\u016b\\u016c\\u016d", + "\\u016e\\u016f\\u0170\\u0171\\u0172\\u0173\\u0174\\u0175\\u0176\\u0177\\u0178\\u0179\\u017a\\u017b\\u017c\\u017d", + "\\u017e\\u01fc\\u01fd\\u01fe\\u01ff\\u0218\\u0219\\u021a\\u021b\\u0386\\u0388\\u0389\\u038a\\u038c\\u038e\\u038f", + "\\u0390\\u03aa\\u03ab\\u03ac\\u03ad\\u03ae\\u03af\\u03b0\\u03ca\\u03cb\\u03cc\\u03cd\\u03ce\\u0400\\u0401\\u0403", + "\\u0407\\u040c\\u040d\\u040e\\u0419\\u0439\\u0450\\u0451\\u0452\\u0453\\u0457\\u045b\\u045c\\u045d\\u045e\\u045f", + "\\u0490\\u0491\\u1e02\\u1e03\\u1e0a\\u1e0b\\u1e1e\\u1e1f\\u1e22\\u1e23\\u1e30\\u1e31\\u1e40\\u1e41\\u1e56\\u1e57", + "\\u1e60\\u1e61\\u1e6a\\u1e6b\\u1e80\\u1e81\\u1e82\\u1e83\\u1e84\\u1e85\\u1ef2\\u1ef3\\u00e8\\u00e9\\u00ea\\u00eb", + "\\u0149\\u01e7\\u01eb\\u040f\\u1e0d\\u1e25\\u1e5b\\u1e6d\\u1e92\\u1eca\\u1ecb\\u1ecc\\u1ecd\\u1ee4\\u1ee5\\u2116", + "\\u0207\\u0194\\u0263\\u0283\\u2047\\u01f1\\u01f2\\u01f3\\u01c4\\u01c5\\u01c6\\u01c7\\u01c8\\u01ca\\u01cb\\u01cc", + "\\u2139\\u1d6b\\ua732\\ua733\\ua734\\ua735\\ua736\\ua737\\ua738\\ua73a\\ua73c\\ua73d\\ua74e\\ua74f\\ua760\\ua761", + "\\ufb04\\ufb06\\u16a1\\u16b5\\u01a0\\u01a1\\u01af\\u01b0\\u1eae\\u1eaf\\u1ea4\\u1ea5\\u1ebe\\u1ebf\\u1ed1\\u1eda", + "\\u1edb\\u1ee8\\u1ee9\\u1eb0\\u1eb1\\u1ea6\\u1ea7\\u1ec0\\u1ec1\\u1ed3\\u1edc\\u1edd\\u1eea\\u1eeb\\u1ea2\\u1ea3", + "\\u1eb2\\u1eb3\\u1ea8\\u1ea9\\u1eba\\u1ebb\\u1ed5\\u1ede\\u1ec2\\u1ec3\\u1ec8\\u1ec9\\u1ece\\u1ecf\\u1ed4\\u1edf", + "\\u1ee6\\u1ee7\\u1eec\\u1eed\\u1ef6\\u1ef7\\u1ea0\\u1ea1\\u1eb6\\u1eb7\\u1eac\\u1ead\\u1eb8\\u1eb9\\u1ec6\\u1ec7", + "\\u1ed8\\u1ed9\\u1ee2\\u1ee3\\u1ef0\\u1ef1\\u1ef4\\u1ef5\\u1ed0\\u0195\\u1eaa\\u1eab\\u1ed6\\u1ed7\\u1eef\\u261e", + "\\u261c\\u262e\\u1eb4\\u1eb5\\u1ebc\\u1ebd\\u1ec4\\u1ec5\\u1ed2\\u1ee0\\u1ee1\\u1eee\\u1ef8\\u1ef9\\u0498\\u0499", + "\\u04a0\\u04a1\\u04aa\\u04ab\\u01f6\\u26a0\\u24ea\\u2460\\u2461\\u2462\\u2463\\u2464\\u2465\\u2466\\u2467\\u2468", + "\\u2469\\u246a\\u246b\\u246c\\u246d\\u246e\\u246f\\u2470\\u2471\\u2472\\u2473\\u24b6\\u24b7\\u24b8\\u24b9\\u24ba", + "\\u24bb\\u24bc\\u24bd\\u24be\\u24bf\\u24c0\\u24c1\\u24c2\\u24c3\\u24c4\\u24c5\\u24c6\\u24c7\\u24c8\\u24c9\\u24ca", + "\\u24cb\\u24cc\\u24cd\\u24ce\\u24cf\\u24d0\\u24d1\\u24d2\\u24d3\\u24d4\\u24d5\\u24d6\\u24d7\\u24d8\\u24d9\\u24da", + "\\u24db\\u24dc\\u24dd\\u24de\\u24df\\u24e0\\u24e1\\u24e2\\u24e3\\u24e4\\u24e5\\u24e6\\u24e7\\u24e8\\u24e9\\u0327", + "\\u0282\\u0290\\u0276\\u01cd\\u01ce\\u01de\\u01df\\u01fa\\u01fb\\u0202\\u0203\\u0226\\u0227\\u01e0\\u01e1\\u1e00", + "\\u1e01\\u0200\\u0201\\u1e06\\u1e07\\u1e04\\u1e05\\u1d6c\\u1e08\\u1e09\\u1e10\\u1e11\\u1e12\\u1e13\\u1e0e\\u1e0f", + "\\u1e0c\\u1d6d\\u1e14\\u1e15\\u1e16\\u1e17\\u1e18\\u1e19\\u1e1c\\u1e1d\\u0228\\u0229\\u1e1a\\u1e1b\\u0204\\u0205", + "\\u0206\\u1d6e\\u01f4\\u01f5\\u01e6\\u1e26\\u1e27\\u1e28\\u1e29\\u1e2a\\u1e2b\\u021e\\u021f\\u1e24\\u1e96\\u1e2e", + "\\u1e2f\\u020a\\u020b\\u01cf\\u01d0\\u0208\\u0209\\u1e2c\\u1e2d\\u01f0\\u0237\\u01e8\\u01e9\\u1e32\\u1e33\\u1e34", + "\\u1e35\\u1e3a\\u1e3b\\u1e3c\\u1e3d\\u1e36\\u1e37\\u1e38\\u1e39\\u2c62\\u1e3e\\u1e3f\\u1e42\\u1e43\\u1d6f\\u1e44", + "\\u1e45\\u1e46\\u1e47\\u1e4a\\u1e4b\\u01f8\\u01f9\\u1e48\\u1e49\\u1d70\\u01ec\\u01ed\\u022c\\u022d\\u1e4c\\u1e4d", + "\\u1e4e\\u1e4f\\u1e50\\u1e51\\u1e52\\u1e53\\u020e\\u020f\\u022a\\u022b\\u01d1\\u01d2\\u022e\\u022f\\u0230\\u0231", + "\\u020c\\u020d\\u01ea\\u1e54\\u1e55\\u1d71\\u0212\\u0213\\u1e58\\u1e59\\u1e5c\\u1e5d\\u1e5e\\u1e5f\\u0210\\u0211", + "\\u1e5a\\u1d73\\u1d72\\u1e64\\u1e65\\u1e66\\u1e67\\u1e62\\u1e63\\u1e68\\u1e69\\u1d74\\u1e70\\u1e71\\u1e6e\\u1e6f", + "\\u1e6c\\u1e97\\u1d75\\u1e72\\u1e73\\u1e76\\u1e77\\u1e78\\u1e79\\u1e7a\\u1e7b\\u01d3\\u01d4\\u01d5\\u01d6\\u01d7", + "\\u01d8\\u01d9\\u01da\\u01db\\u01dc\\u1e74\\u1e75\\u0214\\u0215\\u0216\\u1e7e\\u1e7f\\u1e7c\\u1e7d\\u1e86\\u1e87", + "\\u1e88\\u1e89\\u1e98\\u1e8c\\u1e8d\\u1e8a\\u1e8b\\u0232\\u0233\\u1e8e\\u1e8f\\u1e99\\u1e94\\u1e95\\u1e90\\u1e91", + "\\u1e93\\u1d76\\u01ee\\u01ef\\u1e9b\\ua73e\\ua73f\\u01e2\\u01e3\\u1d7a\\u1efb\\u1d02\\u1d14\\uab63\\u0238\\u02a3", + "\\u02a5\\u02a4\\u02a9\\u02aa\\u02ab\\u0239\\u02a8\\u02a6\\u02a7\\uab50\\uab51\\u20a7\\u1efa\\ufb2e\\ufb2f\\u0180", + "\\u0182\\u0183\\u0187\\u0188\\u018a\\u018b\\u018c\\u0193\\u01e4\\u01e5\\u0197\\u0196\\u0269\\u0198\\u0199\\u019d", + "\\u01a4\\u01a5\\u027d\\u01a6\\u01ac\\u01ad\\u01ab\\u01ae\\u0217\\u01b1\\u019c\\u01b3\\u01b4\\u01b5\\u01b6\\u01a2", + "\\u01a3\\u0222\\u0223\\u02ad\\u02ae\\u02af\\ufb14\\ufb15\\ufb17\\ufb16\\ufb13\\u04d0\\u04d1\\u04d2\\u04d3\\u04f6", + "\\u04f7\\u0494\\u0495\\u04d6\\u04d7\\u04bc\\u04bd\\u04be\\u04bf\\u04da\\u04db\\u04dc\\u04dd\\u04c1\\u04c2\\u04de", + "\\u04df\\u04e2\\u04e3\\u04e4\\u04e5\\u04e6\\u04e7\\u04ea\\u04eb\\u04f0\\u04f1\\u04ee\\u04ef\\u04f2\\u04f3\\u04f4", + "\\u04f5\\u04f8\\u04f9\\u04ec\\u04ed\\u0476\\u0477\\u04d4\\u04fa\\u0502\\ua682\\ua680\\ua688\\u052a\\u052c\\ua684", + "\\u0504\\u0510\\u04e0\\u0506\\u048a\\u04c3\\u049e\\u049c\\u051e\\u051a\\u04c5\\u052e\\u0512\\u0520\\u0508\\u0514", + "\\u04cd\\u04c9\\u0528\\u04c7\\u04a4\\u0522\\u050a\\u04a8\\u0524\\u04a6\\u048e\\u0516\\u050c\\ua690\\u04ac\\ua68a", + "\\ua68c\\u050e\\u04b2\\u04fc\\u04fe\\u0526\\ua694\\u04b4\\ua68e\\u04b6\\u04cb\\u04b8\\ua692\\ua696\\ua686\\u048c", + "\\u0518\\u051c\\u04d5\\u04fb\\u0503\\ua683\\ua681\\ua689\\u052b\\u052d\\ua685\\u0505\\u0511\\u04e1\\u0507\\u048b", + "\\u04c4\\u049f\\u049d\\u051f\\u051b\\u04c6\\u052f\\u0513\\u0521\\u0509\\u0515\\u04ce\\u04ca\\u0529\\u04c8\\u04a5", + "\\u0523\\u050b\\u04a9\\u0525\\u04a7\\u048f\\u0517\\u050d\\ua691\\u04ad\\ua68b\\ua68d\\u050f\\u04b3\\u04fd\\u04ff", + "\\u0527\\ua695\\u04b5\\ua68f\\u04b7\\u04cc\\u04b9\\ua693\\ua697\\ua687\\u048d\\u0519\\u051d\\u1f08\\u1f00\\u1f09", + "\\u1f01\\u1f0a\\u1f02\\u1f0b\\u1f03\\u1f0c\\u1f04\\u1f0d\\u1f05\\u1f0e\\u1f06\\u1f0f\\u1f07\\u1fba\\u1f70\\u1fb8", + "\\u1fb0\\u1fb9\\u1fb1\\u1fbb\\u1f71\\u1f88\\u1f80\\u1f89\\u1f81\\u1f8a\\u1f82\\u1f8b\\u1f83\\u1f8c\\u1f84\\u1f8d", + "\\u1f85\\u1f8e\\u1f86\\u1f8f\\u1f87\\u1fbc\\u1fb4\\u1fb6\\u1fb7\\u1fb2\\u1fb3\\u1f18\\u1f10\\u1f19\\u1f11\\u1f1a", + "\\u1f12\\u1f1b\\u1f13\\u1f1c\\u1f14\\u1f1d\\u1f15\\u1fc8\\u1fc9\\u1f72\\u1f73\\u1f28\\u1f20\\u1fca\\u1f74\\u1f29", + "\\u1f21\\u1f2a\\u1f22\\u1f2b\\u1f23\\u1f2c\\u1f24\\u1f2d\\u1f25\\u1f2e\\u1f26\\u1f2f\\u1f27\\u1f98\\u1f90\\u1f99", + "\\u1f91\\u1f9a\\u1f92\\u1f9b\\u1f93\\u1f9c\\u1f94\\u1f9d\\u1f95\\u1f9e\\u1f96\\u1f9f\\u1f97\\u1fcb\\u1f75\\u1fcc", + "\\u1fc3\\u1fc2\\u1fc4\\u1fc6\\u1fc7\\u1fda\\u1f76\\u1fdb\\u1f77\\u1f38\\u1f30\\u1f39\\u1f31\\u1f3a\\u1f32\\u1f3b", + "\\u1f33\\u1f3c\\u1f34\\u1f3d\\u1f35\\u1f3e\\u1f36\\u1f3f\\u1f37\\u1fd8\\u1fd0\\u1fd9\\u1fd1\\u1fd2\\u1fd3\\u1fd6", + "\\u1fd7\\u1ff8\\u1f78\\u1ff9\\u1f79\\u1f48\\u1f40\\u1f49\\u1f41\\u1f4a\\u1f42\\u1f4b\\u1f43\\u1f4c\\u1f44\\u1f4d", + "\\u1f45\\u1fec\\u1fe4\\u1fe5\\u1fea\\u1f7a\\u1feb\\u1f7b\\u1f59\\u1f51\\u1f5b\\u1f53\\u1f5d\\u1f55\\u1f5f\\u1f57", + "\\u1fe8\\u1fe0\\u1fe9\\u1fe1\\u03d3\\u03d4\\u1fe2\\u1fe3\\u1fe7\\u1f50\\u1f52\\u1f54\\u1fe6\\u1f56\\u1ffa\\u1f7c", + "\\u1ffb\\u1f7d\\u1f68\\u1f60\\u1f69\\u1f61\\u1f6a\\u1f62\\u1f6b\\u1f63\\u1f6c\\u1f64\\u1f6d\\u1f65\\u1f6e\\u1f66", + "\\u1f6f\\u1f67\\u1fa8\\u1fa0\\u1fa9\\u1fa1\\u1faa\\u1fa2\\u1fab\\u1fa3\\u1fac\\u1fa4\\u1fad\\u1fa5\\u1fae\\u1fa6", + "\\u1faf\\u1fa7\\u1ffc\\u1ff3\\u1ff2\\u1ff4\\u1ff6\\u1ff7\\u262f\\u2610\\u2611\\u2612\\u018d\\u01ba\\u2c7e\\u023f", + "\\u2c7f\\u0240\\u1d80\\ua7c4\\ua794\\u1d81\\u1d82\\u1d83\\ua795\\u1d84\\u1d85\\u1d86\\u1d87\\u1d88\\u1d89\\u1d8a", + "\\u1d8b\\u1d8c\\u1d8d\\ua7c6\\u1d8e\\u1d8f\\u1d90\\u1d92\\u1d93\\u1d94\\u1d95\\u1d96\\u1d97\\u1d98\\u1d99\\u1d9a", + "\\u1e9a\\u2152\\u2158\\u20a8\\u20af\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" + }); + CODE_POINTS.put("nonlatin_european", new String[] { + "\\u00a1\\u2030\\u00ad\\u00b7\\u20b4\\u2260\\u00bf\\u00d7\\u00d8\\u00de\\u04bb\\u00f0\\u00f8\\u00fe\\u0391\\u0392", + "\\u0393\\u0394\\u0395\\u0396\\u0397\\u0398\\u0399\\u039a\\u039b\\u039c\\u039d\\u039e\\u039f\\u03a0\\u03a1\\u03a3", + "\\u03a4\\u03a5\\u03a6\\u03a7\\u03a8\\u03a9\\u03b1\\u03b2\\u03b3\\u03b4\\u03b5\\u03b6\\u03b7\\u03b8\\u03b9\\u03ba", + "\\u03bb\\u03bc\\u03bd\\u03be\\u03bf\\u03c0\\u03c1\\u03c2\\u03c3\\u03c4\\u03c5\\u03c6\\u03c7\\u03c8\\u03c9\\u0402", + "\\u0405\\u0406\\u0408\\u0409\\u040a\\u040b\\u0410\\u0411\\u0412\\u0413\\u0414\\u0415\\u0416\\u0417\\u0418\\u041a", + "\\u041b\\u041c\\u041d\\u041e\\u041f\\u0420\\u0421\\u0422\\u0423\\u0424\\u0425\\u0426\\u0427\\u0428\\u0429\\u042a", + "\\u042b\\u042c\\u042d\\u042e\\u042f\\u0430\\u0431\\u0432\\u0433\\u0434\\u0435\\u0436\\u0437\\u0438\\u043a\\u043b", + "\\u043c\\u043d\\u043e\\u043f\\u0440\\u0441\\u0442\\u0443\\u0444\\u0445\\u0446\\u0447\\u0448\\u0449\\u044a\\u044b", + "\\u044c\\u044d\\u044e\\u044f\\u0454\\u0455\\u0456\\u0458\\u0459\\u045a\\u2013\\u2014\\u2018\\u2019\\u201c\\u201d", + "\\u201e\\u2026\\u204a\\u2190\\u2191\\u2192\\u2193\\u21c4\\uff0b\\u018f\\u0259\\u025b\\u026a\\u04ae\\u04af\\u04e8", + "\\u04e9\\u02bb\\u02cc\\u037e\\u0138\\u1e9e\\u00df\\u20bd\\u20ac\\u0462\\u0463\\u0474\\u0475\\u04c0\\u0472\\u0473", + "\\u2070\\u00b9\\u00b3\\u2074\\u2075\\u2076\\u2077\\u2078\\u2079\\u207a\\u207b\\u207c\\u207d\\u207e\\u2071\\u2122", + "\\u0294\\u0295\\u29c8\\u2694\\u2620\\u049a\\u049b\\u0492\\u0493\\u04b0\\u04b1\\u04d8\\u04d9\\u0496\\u0497\\u04a2", + "\\u04a3\\u04ba\\u05d0\\u05d1\\u05d2\\u05d3\\u05d4\\u05d5\\u05d6\\u05d7\\u05d8\\u05d9\\u05db\\u05dc\\u05de\\u05dd", + "\\u05e0\\u05df\\u05e1\\u05e2\\u05e4\\u05e3\\u05e6\\u05e5\\u05e7\\u05e8\\u00a2\\u00a4\\u00a5\\u00a9\\u00ae\\u00b5", + "\\u00b6\\u00bc\\u00bd\\u00be\\u0387\\u2010\\u201a\\u2020\\u2021\\u2022\\u2031\\u2032\\u2033\\u2034\\u2035\\u2036", + "\\u2037\\u2039\\u203a\\u203b\\u203c\\u203d\\u2042\\u2048\\u2049\\u204b\\u204e\\u204f\\u2051\\u2052\\u2057\\u2117", + "\\u2212\\u2213\\u221e\\u2600\\u2601\\u2608\\u0404\\u2632\\u2635\\u263d\\u2640\\u2642\\u26a5\\u2660\\u2663\\u2665", + "\\u2666\\u2669\\u266a\\u266b\\u266c\\u266d\\u266e\\u266f\\u2680\\u2681\\u2682\\u2683\\u2684\\u2685\\u02ac\\u26a1", + "\\u26cf\\u2714\\u2744\\u274c\\u2764\\u2b50\\u2e18\\u2e2e\\u2e35\\u2e38\\u2e41\\u2e4b\\u295d\\u1614\\u0190\\u07c8", + "\\u03db\\u3125\\u2c6f\\u15fa\\u0186\\u15e1\\u018e\\u2132\\u2141\\ua7b0\\ua780\\u0500\\ua779\\u1d1a\\u27d8\\u2229", + "\\u0245\\u2144\\u0250\\u0254\\u01dd\\u025f\\u1d77\\u0265\\u1d09\\u027e\\u029e\\ua781\\u026f\\u0279\\u0287\\u028c", + "\\u028d\\u028e\\u0531\\u0532\\u0533\\u0534\\u0536\\u0537\\u0539\\u053a\\u053b\\u053c\\u053d\\u053e\\u053f\\u0540", + "\\u0541\\u0542\\u0543\\u0544\\u0545\\u0546\\u0547\\u0548\\u0549\\u054b\\u054c\\u054d\\u054e\\u054f\\u0550\\u0551", + "\\u0552\\u0553\\u0554\\u0555\\u0556\\u0559\\u0561\\u0562\\u0563\\u0564\\u0565\\u0566\\u0567\\u0568\\u0569\\u056a", + "\\u056b\\u056c\\u056d\\u056e\\u056f\\u0570\\u0571\\u0572\\u0573\\u0574\\u0575\\u0576\\u0577\\u0578\\u0579\\u057a", + "\\u057b\\u057c\\u057d\\u057e\\u057f\\u0580\\u0581\\u0582\\u0583\\u0584\\u0585\\u0586\\u0587\\u05e9\\u05ea\\u0538", + "\\u055a\\u055b\\u055c\\u055d\\u055e\\u055f\\u0560\\u0588\\u058f\\u00af\\u017f\\u01b7\\u0292\\u01f7\\u01bf\\u021c", + "\\u021d\\u0224\\u0225\\u02d9\\ua75a\\ua75b\\u2011\\u214b\\u23cf\\u23e9\\u23ea\\u23ed\\u23ee\\u23ef\\u23f4\\u23f5", + "\\u23f6\\u23f7\\u23f8\\u23f9\\u23fa\\u23fb\\u23fc\\u23fd\\u2b58\\u25b2\\u25b6\\u25bc\\u25c0\\u25cf\\u25e6\\u25d8", + "\\u2693\\u26e8\\u0132\\u0133\\u01c9\\ua728\\ua729\\ua739\\ua73b\\ufb00\\ufb01\\ufb02\\ufb03\\ufb05\\ufffd\\u0535", + "\\u054a\\u16a0\\u16a2\\u16a3\\u16a4\\u16a5\\u16a6\\u16a7\\u16a8\\u16a9\\u16aa\\u16ab\\u16ac\\u16ad\\u16ae\\u16af", + "\\u16b0\\u16b1\\u16b2\\u16b3\\u16b4\\u16b6\\u16b7\\u16b8\\u16b9\\u16ba\\u16bb\\u16bc\\u16bd\\u16be\\u16bf\\u16c0", + "\\u16c1\\u16c2\\u16c3\\u16c4\\u16c5\\u16c6\\u16c7\\u16c8\\u16c9\\u16ca\\u16cb\\u16cc\\u16cd\\u16ce\\u16cf\\u16d0", + "\\u16d1\\u16d2\\u16d3\\u16d4\\u16d5\\u16d6\\u16d7\\u16d8\\u16d9\\u16da\\u16db\\u16dc\\u16dd\\u16de\\u16df\\u16e0", + "\\u16e1\\u16e2\\u16e3\\u16e4\\u16e5\\u16e6\\u16e7\\u16e8\\u16e9\\u16ea\\u16eb\\u16ec\\u16ed\\u16ee\\u16ef\\u16f0", + "\\u16f1\\u16f2\\u16f3\\u16f4\\u16f5\\u16f6\\u16f7\\u16f8\\u263a\\u263b\\u00a6\\u2639\\u05da\\u05f3\\u05f4\\u05f0", + "\\u05f1\\u05f2\\u05be\\u05c3\\u05c6\\u00b4\\u00a8\\u1d00\\u0299\\u1d04\\u1d05\\u1d07\\ua730\\u0262\\u029c\\u1d0a", + "\\u1d0b\\u029f\\u1d0d\\u0274\\u1d0f\\u1d18\\ua7af\\u0280\\ua731\\u1d1b\\u1d1c\\u1d20\\u1d21\\u028f\\u1d22\\u00a7", + "\\u0271\\u0273\\u0272\\u0288\\u0256\\u0261\\u02a1\\u0255\\u0291\\u0278\\u029d\\u02a2\\u027b\\u0281\\u0266\\u028b", + "\\u0270\\u026c\\u026e\\u0298\\u01c0\\u01c3\\u01c2\\u01c1\\u0253\\u0257\\u1d91\\u0284\\u0260\\u029b\\u0267\\u026b", + "\\u0268\\u0289\\u028a\\u0258\\u0275\\u0264\\u025c\\u025e\\u0251\\u0252\\u025a\\u025d\\u0181\\u0189\\u0191\\u01a9", + "\\u01b2\\u10a0\\u10a1\\u10a2\\u10a3\\u10a4\\u10a5\\u10a6\\u10a7\\u10a8\\u10a9\\u10aa\\u10ab\\u10ac\\u10ad\\u10ae", + "\\u10af\\u10b0\\u10b1\\u10b2\\u10b3\\u10b4\\u10b5\\u10b6\\u10b7\\u10b8\\u10b9\\u10ba\\u10bb\\u10bc\\u10bd\\u10be", + "\\u10bf\\u10c0\\u10c1\\u10c2\\u10c3\\u10c4\\u10c5\\u10c7\\u10cd\\u10d0\\u10d1\\u10d2\\u10d3\\u10d4\\u10d5\\u10d6", + "\\u10d7\\u10d8\\u10d9\\u10da\\u10db\\u10dc\\u10dd\\u10de\\u10df\\u10e0\\u10e1\\u10e2\\u10e3\\u10e4\\u10e5\\u10e6", + "\\u10e7\\u10e8\\u10e9\\u10ea\\u10eb\\u10ec\\u10ed\\u10ee\\u10ef\\u10f0\\u10f1\\u10f2\\u10f3\\u10f4\\u10f5\\u10f6", + "\\u10f7\\u10f8\\u10f9\\u10fa\\u10fb\\u10fc\\u10fd\\u10fe\\u10ff\\ufb4a\\ufb2b\\ufb4e\\ufb44\\ufb3b\\ufb1f\\ufb1d", + "\\ufb4b\\ufb35\\ufb4c\\ufb31\\ua727\\ua726\\u027a\\u2c71\\u02a0\\u0297\\u0296\\u026d\\u0277\\u027f\\u0285\\u0286", + "\\u0293\\u029a\\u20aa\\u20be\\u058a\\u2d00\\u2d01\\u2d02\\u2d03\\u2d04\\u2d05\\u2d06\\u2d21\\u2d07\\u2d08\\u2d09", + "\\u2d0a\\u2d0b\\u2d0c\\u2d22\\u2d0d\\u2d0e\\u2d0f\\u2d10\\u2d11\\u2d12\\u2d23\\u2d13\\u2d14\\u2d15\\u2d16\\u2d17", + "\\u2d18\\u2d19\\u2d1a\\u2d1b\\u2d1c\\u2d1d\\u2d1e\\u2d24\\u2d1f\\u2d20\\u2d25\\u215b\\u215c\\u215d\\u215e\\u2153", + "\\u2154\\u2709\\u2602\\u2614\\u2604\\u26c4\\u2603\\u231b\\u231a\\u2690\\u270e\\u2763\\u2664\\u2667\\u2661\\u2662", + "\\u26c8\\u2630\\u2631\\u2633\\u2634\\u2636\\u2637\\u2194\\u21d2\\u21cf\\u21d4\\u21f5\\u2200\\u2203\\u2204\\u2209", + "\\u220b\\u220c\\u2282\\u2283\\u2284\\u2285\\u2227\\u2228\\u22bb\\u22bc\\u22bd\\u2225\\u2262\\u22c6\\u2211\\u22a4", + "\\u22a5\\u22a2\\u22a8\\u2254\\u2201\\u2234\\u2235\\u221b\\u221c\\u2202\\u22c3\\u2286\\u2287\\u25a1\\u25b3\\u25b7", + "\\u25bd\\u25c1\\u25c6\\u25c7\\u25cb\\u25ce\\u2606\\u2605\\u2718\\u2080\\u2081\\u2082\\u2083\\u2084\\u2085\\u2086", + "\\u2087\\u2088\\u2089\\u208a\\u208b\\u208c\\u208d\\u208e\\u222b\\u222e\\u221d\\u2300\\u2302\\u2318\\u3012\\u027c", + "\\u0184\\u0185\\u1e9f\\u023d\\u019a\\u019b\\u0220\\u019e\\u019f\\u01a7\\u01a8\\u01aa\\u01b8\\u01b9\\u01bb\\u01bc", + "\\u01bd\\u01be\\u0221\\u0234\\u0235\\u0236\\u023a\\u2c65\\u023b\\u023c\\u0246\\u0247\\u023e\\u2c66\\u0241\\u0242", + "\\u0243\\u0244\\u0248\\u0249\\u024a\\u024b\\u024c\\u024d\\u024e\\u024f\\u1e9c\\u1e9d\\u1efc\\u1efd\\u1efe\\u1eff", + "\\ua7a8\\ua7a9\\ud800\\udf30\\ud800\\udf31\\ud800\\udf32\\ud800\\udf33\\ud800\\udf34\\ud800\\udf35\\ud800\\udf36\\ud800\\udf37\\ud800\\udf38\\ud800\\udf39\\ud800\\udf3a\\ud800\\udf3b\\ud800\\udf3c\\ud800\\udf3d", + "\\ud800\\udf3e\\ud800\\udf3f\\ud800\\udf40\\ud800\\udf41\\ud800\\udf42\\ud800\\udf43\\ud800\\udf44\\ud800\\udf45\\ud800\\udf46\\ud800\\udf47\\ud800\\udf48\\ud800\\udf49\\ud800\\udf4a\\ud83c\\udf27\\ud83d\\udd25\\ud83c\\udf0a", + "\\u2150\\u2151\\u2155\\u2156\\u2157\\u2159\\u215a\\u215f\\u2189\\ud83d\\udde1\\ud83c\\udff9\\ud83e\\ude93\\ud83d\\udd31\\ud83c\\udfa3\\ud83e\\uddea\\u2697", + "\\u2bea\\u2beb\\u2c6d\\ud83d\\udee1\\u2702\\ud83c\\udf56\\ud83e\\udea3\\ud83d\\udd14\\u23f3\\u2691\\u20a0\\u20a1\\u20a2\\u20a3\\u20a4\\u20a5", + "\\u20a6\\u20a9\\u20ab\\u20ad\\u20ae\\u20b0\\u20b1\\u20b2\\u20b3\\u20b5\\u20b6\\u20b7\\u20b8\\u20b9\\u20ba\\u20bb", + "\\u20bc\\u20bf\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" + }); + + for (Map.Entry entry : CODE_POINTS.entrySet()) { + String id = entry.getKey(); + String[] value = entry.getValue(); + int[][] codePointGrid = new int[value.length][]; + int i = 0; + for (String line : value) { + char[] chars = CharacterUtils.unicodeToChars(line); + codePointGrid[i++] = CharacterUtils.toCodePoints(chars); + } + CODE_POINT_GRIDS.put(id, codePointGrid); + } + } + + private final HashMap> templateConfigConsumersMap = new HashMap<>(); + + private final CustomNameplates plugin; + private final HashMap charFontWidthDataMap = new HashMap<>(); + private final HashMap configFontWidthDataMap = new HashMap<>(); + + private final Cache textWidthCache; + + private boolean templatesLoaded = false; + + public AdvanceManagerImpl(CustomNameplates plugin) { + this.plugin = plugin; + this.textWidthCache = + Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .build(); + this.init(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void init() { + this.templateConfigConsumersMap.put("space", (id, section) -> { + HashMap data = new HashMap<>(); + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", "space"); + JsonObject advancesObject = new JsonObject(); + + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String character = entry.getKey(); + int codePoint; + if (character.length() == 1) { + char[] chars = character.toCharArray(); + codePoint = CharacterUtils.toCodePoint(chars); + } else if (character.startsWith("\\u")) { + char[] chars = CharacterUtils.unicodeToChars(character); + codePoint = CharacterUtils.toCodePoint(chars); + } else { + plugin.getPluginLogger().warn(character + " is not a valid character"); + continue; + } + if (entry.getValue() instanceof Number number) { + data.put(codePoint, number.floatValue()); + advancesObject.addProperty(entry.getKey(), number); + } + } + + jsonObject.add("advances", advancesObject); + this.charFontWidthDataMap.put(id, CharacterFontAdvanceData.builder() + .id(id) + .advance(data) + .fontProviderFunction((__ignore__) -> List.of(jsonObject)) + .build()); + }); + + this.templateConfigConsumersMap.put("unihex", (id, section) -> { + String path = requireNonNull(section.getString("file")); + boolean copy = section.getBoolean("generate", false); + List list = new ArrayList<>(); + List> overrideSection = section.getMapList("size_overrides"); + for (Map map : overrideSection) { + String start = (String) map.get("from"); + String to = (String) map.get("to"); + int left = (int) map.get("left"); + int right = (int) map.get("right"); + SizeOverrides sizeOverrides = new SizeOverrides(CharacterUtils.toCodePoint(start.toCharArray()), CharacterUtils.toCodePoint(to.toCharArray()), left, right); + list.add(sizeOverrides); + } + + Map filters = new HashMap<>(); + Section filterSection = section.getSection("filter"); + if (filterSection != null) { + for (Map.Entry entry : filterSection.getStringRouteMappedValues(false).entrySet()) { + String key = entry.getKey(); + if (entry.getValue() instanceof Boolean b) { + filters.put(key, b); + } + } + } + + File unifontCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp"); + if (!unifontCache.exists()) { + File unihex = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path); + if (DEFAULT_UNIHEX.contains(path)) { + plugin.getConfigManager().saveResource("font" + File.separator + path); + } + if (!unihex.exists()) { + plugin.getPluginLogger().warn(unihex.getAbsolutePath() + " not found"); + return; + } + if (!unihex.getName().endsWith(".zip")) { + plugin.getPluginLogger().warn(unihex.getAbsolutePath() + " is not a .zip"); + return; + } + try (ZipFile zipFile = new ZipFile(unihex)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (entry.getName().endsWith(".hex") && !entry.isDirectory()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(zipFile.getInputStream(entry)))) { + unifontCache.getParentFile().mkdirs(); + unifontCache.createNewFile(); + YamlDocument yml = plugin.getConfigManager().loadData(unifontCache); + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split(":"); + if (parts.length > 1) { + int codePoint = Integer.parseInt(parts[0], 16); + char[] surrogatePair = Character.toChars(codePoint); + StringBuilder s = new StringBuilder(); + for (char c : surrogatePair) { + s.append(CharacterUtils.char2Unicode(c)); + } + String unicode = s.toString(); + + // width in pixels + String hexData = parts[1]; + int width = hexData.length() / 4; + int high = 4; + int low = 4; + int splitInterval = width / 4; + + int x; + int n; + outer: + { + for (x = 0; x < splitInterval; x++) { + inner: + for (int y = 0; y < 16; y++) { + int pos = y * splitInterval + x; + char selectedHex = hexData.charAt(pos); + int decimal = Character.digit(selectedHex, 16); + for (int k = 0; k < low; k++) { + if ((decimal & (1 << (3 - k))) != 0) { + low = k; + if (low == 0) { + break outer; + } else { + continue inner; + } + } + } + } + if (low != 4) { + break outer; + } + } + yml.set(unicode, -1); + continue; + } + + outer: + for (n = splitInterval - 1; n >= x; n--) { + inner: + for (int y = 0; y < 16; y++) { + int pos = y * splitInterval + n; + char selectedHex = hexData.charAt(pos); + int decimal = Character.digit(selectedHex, 16); + for (int k = 0; k < high; k++) { + if ((decimal & (1 << k)) != 0) { + high = k; + if (high == 0) { + break outer; + } else { + continue inner; + } + } + } + } + if (high != 4) { + break; + } + } + yml.set(unicode, ((n - x + 1) * 4 - high - low) / 2 + 1); + } + } + for (SizeOverrides sizeOverride : list) { + int advance = (sizeOverride.right() - sizeOverride.left() + 1) / 2 + 1; + for (int i = sizeOverride.startCodePoint(); i <= sizeOverride.endCodePoint(); i++) { + char c = (char) i; + yml.set(CharacterUtils.char2Unicode(c), advance); + } + } + yml.save(unifontCache); + } + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + registerCharacterFontData(id, unifontCache, (__ignore__) -> { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", "unihex"); + jsonObject.addProperty("hex_file", "minecraft:font/" + path); + JsonArray jsonArray = new JsonArray(); + jsonObject.add("size_overrides", jsonArray); + for (SizeOverrides sizeOverride : list) { + JsonObject override = new JsonObject(); + override.addProperty("left", sizeOverride.left()); + override.addProperty("right", sizeOverride.right()); + override.addProperty("from", CharacterUtils.char2Unicode((char) sizeOverride.startCodePoint())); + override.addProperty("to", CharacterUtils.char2Unicode((char) sizeOverride.endCodePoint())); + jsonArray.add(override); + } + if (!filters.isEmpty()) { + JsonObject filter = new JsonObject(); + for (Map.Entry entry : filters.entrySet()) { + filter.addProperty(entry.getKey(), entry.getValue()); + } + jsonObject.add("filter", filter); + } + if (copy) { + File unihexFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path); + File destination = new File(plugin.getDataDirectory().toFile(), "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "font" + File.separator + path); + try { + FileUtils.copyFile(unihexFile, destination); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return List.of(jsonObject); + }); + }); + + this.templateConfigConsumersMap.put("bitmap", (id, section) -> { + String file = requireNonNull(section.getString("file"), "File should be NonNull"); + String codePoints = requireNonNull(section.getString("codepoints", ""), "codepoints should be NonNull"); + int height = section.getInt("height", 8); + boolean custom = section.getBoolean("custom", false); + + File bitmapCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp"); + if (!bitmapCache.exists()) { + File png = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + file); + if (DEFAULT_BITMAP_IMAGES.contains(file)) { + plugin.getConfigManager().saveResource("font" + File.separator + file); + } + if (!png.exists()) { + plugin.getPluginLogger().warn(png.getAbsolutePath() + " not found"); + return; + } + if (!png.getName().endsWith(".png")) { + plugin.getPluginLogger().warn(png.getAbsolutePath() + " is not a .png"); + return; + } + try { + bitmapCache.getParentFile().mkdirs(); + bitmapCache.createNewFile(); + BufferedImage bufferedImage = ImageIO.read(png); + YamlDocument yml = plugin.getConfigManager().loadData(bitmapCache); + + int[][] codePointGrid = CODE_POINT_GRIDS.get(codePoints); + + int imageWidth = bufferedImage.getWidth(); + int imageHeight = bufferedImage.getHeight(); + int characterWidth = imageWidth / codePointGrid[0].length; + int characterHeight = imageHeight / codePointGrid.length; + float scale = (float) height / (float) characterHeight; + + int posY = 0; + while (posY < codePointGrid.length) { + int posX = 0; + int[] line = codePointGrid[posY]; + for (int codePoint : line) { + int characterPosX = posX++; + if (codePoint != 0) { + int pixels = 0; + outer: + for (int u = characterWidth - 1; u >= 0; --u) { + int pixelX = characterPosX * characterWidth + u; + for (int k = 0; k < characterHeight; ++k) { + int pixelY = posY * characterHeight + k; + int rgb = bufferedImage.getRGB(pixelX, pixelY); + int alpha = (rgb >> 24) & 0xff; + if (alpha != 0) { + pixels = u + 1; + break outer; + } + } + } + int width = (int) (0.5 + (double) ((float) pixels * scale)) + 1; + char[] chars = Character.toChars(codePoint); + yml.set(CharacterUtils.char2Unicode(chars), width); + } + } + posY++; + } + yml.save(bitmapCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + registerCharacterFontData(id, bitmapCache, (properties) -> { + int ascent = (int) properties.get("shift_y"); + String filePath = custom ? ConfigManager.namespace() + ":font/" + file : "minecraft:font/" + codePoints + ".png"; + plugin.getConfigManager().saveResource("tmp/" + codePoints + ".json"); + StringBuilder jsonContent = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + codePoints + ".json")))) { + String line; + while ((line = reader.readLine()) != null) { + jsonContent.append(line); + } + JsonElement jsonElement = JsonParser.parseString(String.valueOf(jsonContent).replace("\\", "\\\\")); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + JsonArray providers = jsonObject.getAsJsonArray("providers"); + ArrayList modified = new ArrayList<>(); + for (JsonElement element : providers) { + JsonObject provider = element.getAsJsonObject(); + provider.addProperty("ascent", ascent); + provider.addProperty("height", height); + provider.addProperty("file", filePath); + modified.add(provider); + } + return modified; + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + }); + + this.templateConfigConsumersMap.put("legacy_unicode", (id, section) -> { + String path = section.getString("file"); + int height = section.getInt("height"); + boolean custom = section.getBoolean("custom", false); + File unicodeCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp"); + if (!unicodeCache.exists()) { + if (path.equals("unicode_page_%02x.png")) { + for (int a = 0; a < 256; a++) { + plugin.getConfigManager().saveResource("font" + File.separator + "unicode_page_" + String.format("%02x", a) + ".png"); + } + } + try { + unicodeCache.getParentFile().mkdirs(); + unicodeCache.createNewFile(); + YamlDocument yml = plugin.getConfigManager().loadData(unicodeCache); + for (int a = 0; a < 256; a++) { + File png = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + String.format(path, a)); + if (!png.exists()) { + continue; + } + String unicodeStr = String.format("%02x", a); + BufferedImage bufferedImage = ImageIO.read(png); + + int imageWidth = bufferedImage.getWidth(); + int imageHeight = bufferedImage.getHeight(); + int characterWidth = imageWidth / 16; + int characterHeight = imageHeight / 16; + float scale = (float) height / (float) characterHeight; + + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + int x_upper = i * characterWidth; + int x_lower = i * characterWidth; + outer: + for (int x = (i+1) * characterWidth - 1; x >= i * characterWidth; x--) { + for (int y = j * characterWidth; y < (j+1) * characterWidth; y++) { + int rgb = bufferedImage.getRGB(x, y); + int alpha = (rgb >> 24) & 0xff; + if (alpha != 0) { + x_upper = x; + break outer; + } + } + } + int pixels = x_upper - x_lower + 1; + int width = (int) (0.5 + (double) ((float) pixels * scale)) + 1; + String unicode = "\\u" + unicodeStr + String.format("%02x", i + j * 16); + yml.set(unicode, width); + } + } + } + yml.save(unicodeCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + registerCharacterFontData(id, unicodeCache, (properties) -> { + int ascent = (int) properties.get("shift_y"); + plugin.getConfigManager().saveResource("tmp/unicode.json"); + StringBuilder jsonContent = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + "unicode.json")))) { + String line; + while ((line = reader.readLine()) != null) { + jsonContent.append(line); + } + JsonElement jsonElement = JsonParser.parseString(String.valueOf(jsonContent).replace("\\", "\\\\")); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + JsonArray providers = jsonObject.getAsJsonArray("providers"); + ArrayList modified = new ArrayList<>(); + String replacedFormat = path.replace("%02x", "%s"); + for (JsonElement element : providers) { + JsonObject provider = element.getAsJsonObject(); + provider.addProperty("ascent", ascent); + provider.addProperty("height", height); + String index = provider.getAsJsonPrimitive("file").getAsString(); + provider.addProperty("file", custom ? + String.format(ConfigManager.namespace() + ":font/" + replacedFormat, index) + : String.format("minecraft:font/unicode_page_%s.png", index) + ); + modified.add(provider); + } + return modified; + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + }); + + this.templateConfigConsumersMap.put("unicode", (id, section) -> { + String path = section.getString("file"); + String sizesBin = section.getString("sizes"); + File unicodeCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp"); + if (!unicodeCache.exists()) { + if (path.equals("unicode_page_%02x.png")) { + for (int a = 0; a < 256; a++) { + plugin.getConfigManager().saveResource("font" + File.separator + "unicode_page_" + String.format("%02x", a) + ".png"); + } + plugin.getConfigManager().saveResource("font" + File.separator + "glyph_sizes.bin"); + } + try { + File sizeFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + sizesBin); + if (!sizeFile.exists()) { + plugin.getPluginLogger().warn(sizeFile.getAbsolutePath() + " not found"); + return; + } + + unicodeCache.getParentFile().mkdirs(); + unicodeCache.createNewFile(); + YamlDocument yml = plugin.getConfigManager().loadData(unicodeCache); + + try (InputStream inputStream = new FileInputStream(sizeFile)) { + byte[] sizes = inputStream.readNBytes(65536); + for (int i = 0; i < 256; ++i) { + int j = i * 256; + String identifier = String.format("%02x", i); + File png = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + String.format(path, i)); + if (!png.exists()) { + continue; + } + BufferedImage bufferedImage = ImageIO.read(png); + if (bufferedImage.getWidth() != 256 || bufferedImage.getHeight() != 256) { + break; + } + for (int k = 0; k < 256; ++k) { + byte b = sizes[j + k]; + if (b != 0 && ((b >> 4 & 15) > (b & 15) + 1)) { + b = 0; + } + int advance = (((b & 15) + 1) - (b >> 4 & 15)) / 2 + 1; + String unicode = "\\u" + identifier + String.format("%02x", k); + yml.set(unicode, advance); + } + Arrays.fill(sizes, j, j + 256, (byte)0); + } + } + + yml.save(unicodeCache); + } catch (IOException e) { + plugin.getPluginLogger().warn("Failed to load legacy unicode font", e); + } + } + + registerCharacterFontData(id, unicodeCache, (__ignore__) -> { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", "legacy_unicode"); + jsonObject.addProperty("sizes", "minecraft:font/" + sizesBin); + jsonObject.addProperty("template", "minecraft:font/" + path.replace("%02x", "%s")); + return List.of(jsonObject); + }); + }); + + this.templateConfigConsumersMap.put("ttf", (id, section) -> { + String path = section.getString("file"); + float size = section.getFloat("size"); + float oversample = section.getFloat("oversample"); + boolean copy = section.getBoolean("generate", false); + List skip = section.getStringList("skip"); + StringBuilder skipped = new StringBuilder(); + for (String s : skip) { + skipped.append(s); + } + String skippedStrings = skipped.toString(); + char[] chars = skippedStrings.toCharArray(); + int[] skippCodePoints = CharacterUtils.toCodePoints(chars); + + File ttfCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp"); + if (!ttfCache.exists()) { + File ttfFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path); + if (!ttfFile.exists()) { + plugin.getPluginLogger().warn(ttfFile.getAbsolutePath() + " not found"); + return; + } + if (!ttfFile.getName().endsWith(".ttf")) { + plugin.getPluginLogger().warn(ttfFile.getAbsolutePath() + " is not a .ttf"); + return; + } + try (InputStream inputStream = new FileInputStream(ttfFile)) { + ttfCache.getParentFile().mkdirs(); + ttfCache.createNewFile(); + YamlDocument yml = plugin.getConfigManager().loadData(ttfCache); + TTFParser parser = new TTFParser(); + TrueTypeFont ttf = parser.parseEmbedded(inputStream); + CmapSubtable[] cMaps = ttf.getCmap().getCmaps(); + Set codePoints = new HashSet<>(); + for (CmapSubtable cMap : cMaps) { + for (int codepoint = Character.MIN_CODE_POINT; codepoint <= Character.MAX_CODE_POINT; codepoint++) { + int glyphId = cMap.getGlyphId(codepoint); + if (glyphId != 0) { + codePoints.add(codepoint); + } + } + } + for (int skippedCodePoint : skippCodePoints) { + codePoints.remove(skippedCodePoint); + } + for (int codePoint : codePoints) { + float advanceWidth = ttf.getWidth(Character.toString(codePoint)); + char[] text = Character.toChars(codePoint); + yml.set(CharacterUtils.char2Unicode(text), ((advanceWidth) / ttf.getUnitsPerEm()) * size); + } + ttf.close(); + yml.save(ttfCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + registerCharacterFontData(id, ttfCache, (properties) -> { + int y = (int) properties.get("shift_y"); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", "ttf"); + JsonArray ja = new JsonArray(); + ja.add(0); + ja.add(y); + jsonObject.add("shift", ja); + jsonObject.addProperty("size", size); + jsonObject.addProperty("file", path); + jsonObject.addProperty("oversample", oversample); + if (copy) { + File ttfFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path); + File destination = new File(plugin.getDataDirectory().toFile(), "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "font" + File.separator + path); + try { + FileUtils.copyFile(ttfFile, destination); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return List.of(jsonObject); + }); + }); + } + + private void registerCharacterFontData(String id, File asciiCache, Function, List> function) { + YamlDocument data = plugin.getConfigManager().loadData(asciiCache); + HashMap dataMap = new HashMap<>(); + for (Map.Entry entry : data.getStringRouteMappedValues(false).entrySet()) { + char[] chars = CharacterUtils.unicodeToChars(entry.getKey()); + int codePoint = chars.length == 2 ? Character.toCodePoint(chars[0], chars[1]) : chars[0]; + if (entry.getValue() instanceof Number number) { + dataMap.put(codePoint, number.floatValue()); + } + } + + CharacterFontAdvanceData fontWidthData = CharacterFontAdvanceData.builder() + .id(id) + .advance(dataMap) + .fontProviderFunction(function) + .build(); + charFontWidthDataMap.put(id, fontWidthData); + } + + @Override + public void load() { + if (!this.templatesLoaded) { + this.loadTemplates(); + this.templatesLoaded = true; + } + this.loadInternalFont(); + this.loadShiftFont(); + this.loadFontData(); + } + + private void loadShiftFont() { + 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) { + ConfigurableFontAdvanceData.Builder builder = ConfigurableFontAdvanceData.builder() + .defaultAdvance(8) + .id(ConfigManager.namespace() + ":" + font); + List order = section.getStringList(font); + List parents = new ArrayList<>(); + for (String f : order) { + String[] split = f.split(":", 2); + String template = split[0]; + CharacterFontAdvanceData advanceData = charFontWidthDataMap.get(template); + if (advanceData != null) { + parents.add(advanceData); + } else { + // ResourcePackManager would warn + // plugin.getPluginLogger() + } + } + Collections.reverse(parents); + builder.parentFont(parents); + configFontWidthDataMap.put(ConfigManager.namespace() + ":" + font, builder.build()); + } + } + } + } + + private void loadInternalFont() { + ArrayList chars = new ArrayList<>(); + for (Nameplate nameplate : plugin.getNameplateManager().getNameplates()) { + chars.add(nameplate.left()); + chars.add(nameplate.middle()); + chars.add(nameplate.right()); + } + for (Bubble bubble : plugin.getBubbleManager().getBubbles()) { + chars.add(bubble.left()); + chars.add(bubble.middle()); + chars.add(bubble.tail()); + chars.add(bubble.right()); + } + for (Image image : plugin.getImageManager().getImages()) { + chars.add(image.character()); + } + for (Background background : plugin.getBackgroundManager().getBackgrounds()) { + chars.add(background.left()); + chars.add(background.width_1()); + chars.add(background.width_2()); + chars.add(background.width_4()); + chars.add(background.width_8()); + chars.add(background.width_16()); + chars.add(background.width_32()); + chars.add(background.width_64()); + chars.add(background.width_128()); + chars.add(background.right()); + } + + ConfigurableFontAdvanceData.Builder builder = ConfigurableFontAdvanceData.builder() + .defaultAdvance(0) + .id(ConfigManager.namespace() + ":" + ConfigManager.font()); + + for (ConfiguredCharacter character : chars) { + builder.advance(character.character(), character.advance()); + } + for (OffsetFont offsetFont : OffsetFont.values()) { + builder.advance(offsetFont.character(), offsetFont.advance()); + } + configFontWidthDataMap.put(ConfigManager.namespace() + ":" + ConfigManager.font(), builder.build()); + } + + @Override + public void unload() { + this.configFontWidthDataMap.clear(); + } + + @Override + public void disable() { + this.unload(); + for (CharacterFontAdvanceData data : charFontWidthDataMap.values()) { + data.close(); + } + this.charFontWidthDataMap.clear(); + } + + @Override + public void loadTemplates() { + YamlDocument mainConfig = ConfigManager.getMainConfig(); + Section section = mainConfig.getSection("other-settings.font-templates"); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section inner) { + String template = entry.getKey(); + BiConsumer consumer = templateConfigConsumersMap.get(template); + if (consumer != null) { + for (Map.Entry innerEntry : inner.getStringRouteMappedValues(false).entrySet()) { + if (innerEntry.getValue() instanceof Section s) { + consumer.accept(innerEntry.getKey(), s); + } + } + } else { + plugin.getPluginLogger().warn("Unsupported template: " + template); + } + } + } + } + } + + private void loadFontData() { + plugin.getConfigManager().saveResource("configs" + File.separator + "advance-data.yml"); + YamlDocument fontConfig = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "advance-data.yml")); + for (Map.Entry entry : fontConfig.getStringRouteMappedValues(false).entrySet()) { + if (entry.getValue() instanceof Section section) { + float defaultWidth = section.getFloat("default", 0f); + + ArrayList widthDataArrayList = new ArrayList<>(); + for (String template : section.getStringList("template-loading-sequence")) { + CharacterFontAdvanceData fontWidthData = charFontWidthDataMap.get(template); + if (fontWidthData != null) { + widthDataArrayList.add(fontWidthData); + } else { + plugin.getPluginLogger().warn("Unknown template: " + template); + } + } + + ConfigurableFontAdvanceData.Builder builder = ConfigurableFontAdvanceData.builder() + .id(entry.getKey()) + .defaultAdvance(defaultWidth) + .parentFont(widthDataArrayList); + Section valuesSection = section.getSection("values"); + if (valuesSection != null) { + for (Map.Entry innerEntry : valuesSection.getStringRouteMappedValues(false).entrySet()) { + if (innerEntry.getValue() instanceof Number number) { + String character = innerEntry.getKey(); + if (character.startsWith("%") && character.endsWith("%")) { + Placeholder placeholder = plugin.getPlatform().registerPlatformPlaceholder(character); + String value = null; + if (placeholder instanceof SharedPlaceholder sharedPlaceholder) { + value = sharedPlaceholder.request(); + } else if (placeholder instanceof PlayerPlaceholder playerPlaceholder) { + value = playerPlaceholder.request(null); + } + if (value != null) { + String finalText = AdventureHelper.stripTags(AdventureHelper.legacyToMiniMessage(value)); + if (finalText.length() != 1) { + plugin.getPluginLogger().warn(character + " is not a supported placeholder"); + } else { + builder.advance(finalText.charAt(0), number.floatValue()); + } + } + } else if (character.length() == 1) { + builder.advance(character.charAt(0), number.floatValue()); + } else { + plugin.getPluginLogger().warn(character + " is not a supported character"); + } + } + } + } + configFontWidthDataMap.put(entry.getKey(), builder.build()); + } + } + } + + @Override + @Nullable + public ConfigurableFontAdvanceData getCustomFontData(String id) { + return configFontWidthDataMap.get(id); + } + + @Override + public CharacterFontAdvanceData getCharacterFontData(String id) { + return charFontWidthDataMap.get(id); + } + + @Override + public float getLineAdvance(String text) { + requireNonNull(text); + return textWidthCache.get(text, this::calculateTextAdvance); + } + + @Override + public float getCharAdvance(char[] chars, Key font, boolean bold) { + int codePoint = chars.length == 2 ? Character.toCodePoint(chars[0], chars[1]) : chars[0]; + ConfigurableFontAdvanceData data = getCustomFontData(font.asString()); + if (data == null) { + plugin.getPluginLogger().warn("Detected unknown font: " + font + ". Please register it in advance-data.yml"); + return 0f; + } + float advance = data.getAdvance(codePoint); + return bold ? advance + 1 : advance; + } + + @Override + public int getLines(String text, int width) { + List> list = miniMessageToIterable(text); + + int lines = 0; + float totalAdvance = 0; + boolean firstChar = true; + boolean startANewLine = false; + + for (Tuple tuple : list) { + char[] chars = tuple.left().toCharArray(); + int i; + for (i = 0; i < chars.length; i++) { + float advance; + if (Character.isHighSurrogate(chars[i])) { + advance = getCharAdvance(new char[]{chars[i], chars[++i]}, tuple.mid(), tuple.right()); + } else { + if (chars[i] == '\n') { + firstChar = true; + totalAdvance = 0; + lines++; + startANewLine = true; + continue; + } else { + advance = getCharAdvance(new char[]{chars[i]}, tuple.mid(), tuple.right()); + } + } + + if (totalAdvance + advance > width) { + lines++; + if (firstChar) { + totalAdvance = 0; + } else { + if (advance > width) { + totalAdvance = 0; + lines++; + firstChar = true; + } else { + totalAdvance = advance; + } + } + } else { + totalAdvance += advance; + firstChar = false; + } + + startANewLine = false; + } + } + + if (totalAdvance > 0 || startANewLine) lines++; + + return lines; + } + + private float calculateTextAdvance(String text) { + List> iterableTexts = miniMessageToIterable(text); + float totalAdvance = 0; + for (Tuple element : iterableTexts) { + ConfigurableFontAdvanceData data = getCustomFontData(element.mid().asString()); + if (data == null) { + plugin.getPluginLogger().warn("Detected unknown font: " + element.mid() + ". Please register it in advance-data.yml"); + continue; + } + char[] chars = element.left().toCharArray(); + for (int j = 0; j < chars.length; j++) { + float advance; + if (Character.isHighSurrogate(chars[j])) { + advance = data.getAdvance(Character.toCodePoint(chars[j], chars[++j])); + } else { + advance = data.getAdvance(chars[j]); + } + totalAdvance += advance; + if (element.right()) { + totalAdvance += 1; + } + } + } + return totalAdvance; + } + + @Override + public List> miniMessageToIterable(String text) { + if (AdventureHelper.legacySupport) text = AdventureHelper.legacyToMiniMessage(text); + ElementNode node = (ElementNode) AdventureHelper.miniMessage().deserializeToTree(text); + ArrayList> iterableTexts = new ArrayList<>(); + nodeToIterableTexts(node, iterableTexts, MINECRAFT_DEFAULT_FONT, false); + return iterableTexts; + } + + private void nodeToIterableTexts(ElementNode node, List> list, Key font, boolean bold) { + if (node instanceof ValueNode valueNode) { + String text = valueNode.value(); + if (!text.isEmpty()) + list.add(Tuple.of(text, font, bold)); + } else if (node instanceof TagNode tagNode) { + if (tagNode.tag() instanceof Inserting inserting) { + Component component = inserting.value(); + switch (component.decoration(TextDecoration.BOLD)) { + case TRUE -> bold = true; + case FALSE -> bold = false; + case NOT_SET -> {} + } + Key key = component.font(); + if (key != null) + font = key; + if (component instanceof TextComponent textComponent) { + String text = textComponent.content(); + if (!text.isEmpty()) + list.add(Tuple.of(text, font, bold)); + } + } + } + if (!node.unsafeChildren().isEmpty()) { + for (ElementNode child : node.unsafeChildren()) { + this.nodeToIterableTexts(child, list, font, bold); + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/CharacterFontAdvanceData.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/CharacterFontAdvanceData.java new file mode 100644 index 0000000..1b42cb3 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/CharacterFontAdvanceData.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 data(); + + String id(); + + List fontProvider(Map properties); + + static Builder builder() { + return new CharacterFontAdvanceDataImpl.BuilderImpl(); + } + + void close(); + + interface Builder { + + Builder advance(Map data); + + Builder id(String id); + + Builder fontProviderFunction(Function, List> function); + + CharacterFontAdvanceData build(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/CharacterFontAdvanceDataImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/CharacterFontAdvanceDataImpl.java new file mode 100644 index 0000000..c71e4fc --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/CharacterFontAdvanceDataImpl.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 data; + private final String id; + private final Function, List> fontProviderFunction; + + public CharacterFontAdvanceDataImpl(String id, HashMap data, Function, List> 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 data() { + return data; + } + + @Override + public String id() { + return id; + } + + @Override + public List fontProvider(Map properties) { + return fontProviderFunction.apply(properties); + } + + @Override + public void close() { + data.clear(); + } + + public static class BuilderImpl implements Builder { + + private final HashMap data = new HashMap<>(); + private String id; + private Function, List> fontProviderFunction = (stringObjectMap -> null); + + @Override + public Builder advance(Map data) { + this.data.putAll(data); + return this; + } + + @Override + public Builder id(String id) { + this.id = id; + return this; + } + + @Override + public Builder fontProviderFunction(Function, List> function) { + this.fontProviderFunction = function; + return this; + } + + @Override + public CharacterFontAdvanceData build() { + return new CharacterFontAdvanceDataImpl(id, data, fontProviderFunction); + } + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/ExpressionOr.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/ConfigurableFontAdvanceData.java similarity index 53% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/ExpressionOr.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/advance/ConfigurableFontAdvanceData.java index 624ec8f..63b2dae 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/ExpressionOr.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/ConfigurableFontAdvanceData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,19 +15,32 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import org.bukkit.OfflinePlayer; +package net.momirealms.customnameplates.api.feature.advance; import java.util.List; -public record ExpressionOr(List requirements) implements PapiRequirement{ +public interface ConfigurableFontAdvanceData { - @Override - public boolean isMet(OfflinePlayer player) { - for (PapiRequirement requirement : requirements) { - if (requirement.isMet(player)) return true; - } - return false; + float defaultAdvance(); + + float getAdvance(int codePoint); + + String id(); + + static Builder builder() { + return new ConfigurableFontAdvanceDataImpl.BuilderImpl(); } -} \ No newline at end of file + + interface Builder { + + Builder id(String id); + + Builder defaultAdvance(float width); + + Builder advance(int codePoint, float width); + + Builder parentFont(List font); + + ConfigurableFontAdvanceData build(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/ConfigurableFontAdvanceDataImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/ConfigurableFontAdvanceDataImpl.java new file mode 100644 index 0000000..d54501d --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/ConfigurableFontAdvanceDataImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 data = new HashMap<>(); + private final List parents = new ArrayList<>(); + private final String id; + + public ConfigurableFontAdvanceDataImpl(String id, float defaultAdvance, HashMap customData, List 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 customData = new HashMap<>(); + private final List 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 font) { + this.parents.addAll(font); + return this; + } + + @Override + public ConfigurableFontAdvanceData build() { + return new ConfigurableFontAdvanceDataImpl(id, defaultAdvance, customData, parents); + } + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/ImageParser.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/SizeOverrides.java similarity index 75% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/ImageParser.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/advance/SizeOverrides.java index 2315b2e..3eee277 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/ImageParser.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/advance/SizeOverrides.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,8 @@ * along with this program. If not, see . */ -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); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/background/Background.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/Background.java new file mode 100644 index 0000000..5bb6fbe --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/Background.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundImpl.java new file mode 100644 index 0000000..85cfab0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundImpl.java @@ -0,0 +1,324 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundManager.java new file mode 100644 index 0000000..764aa0f --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundManager.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 getBackgrounds(); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundManagerImpl.java new file mode 100644 index 0000000..e9e8be2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/background/BackgroundManagerImpl.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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 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 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"); + } + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/MySQLImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBar.java similarity index 58% rename from paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/MySQLImpl.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBar.java index 7c8827a..c17b06c 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/MySQLImpl.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,19 +15,35 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.storage.method.database.sql; +package net.momirealms.customnameplates.api.feature.bossbar; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.StorageType; +import java.util.UUID; -public class MySQLImpl extends AbstractHikariDatabase { +public interface BossBar { - public MySQLImpl(CustomNameplatesPlugin plugin) { - super(plugin); + UUID uuid(); + + float progress(); + + Color color(); + + Overlay overlay(); + + enum Overlay { + PROGRESS, + NOTCHED_6, + NOTCHED_10, + NOTCHED_12, + NOTCHED_20 } - @Override - public StorageType getStorageType() { - return StorageType.MySQL; + enum Color { + BLUE, + GREEN, + PINK, + PURPLE, + RED, + WHITE, + YELLOW } } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarConfig.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarConfig.java new file mode 100644 index 0000000..9754f64 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarConfig.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarConfigImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarConfigImpl.java new file mode 100644 index 0000000..bd27802 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarConfigImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarDisplayController.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarDisplayController.java new file mode 100644 index 0000000..132c283 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarDisplayController.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarManager.java new file mode 100644 index 0000000..ebe71ca --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarManager.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature.bossbar; + +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; + +public interface BossBarManager extends Reloadable { + + void onTick(); + + BossBarConfig getConfig(String name); + + BossBarConfig[] allConfigs(); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarManagerImpl.java new file mode 100644 index 0000000..538943c --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarManagerImpl.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 configs = new LinkedHashMap<>(); + private final ConcurrentHashMap 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 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() + ); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarSender.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarSender.java new file mode 100644 index 0000000..02cc98f --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bossbar/BossBarSender.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 activePlaceholders() { + if (currentBossBar == null || !isShown()) return Collections.emptySet(); + return currentBossBar.placeholders(); + } + + @Override + public Set allPlaceholders() { + HashSet 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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/Bubble.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/Bubble.java new file mode 100644 index 0000000..0417303 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/Bubble.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleConfig.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleConfig.java new file mode 100644 index 0000000..b428373 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleConfig.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleConfigImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleConfigImpl.java new file mode 100644 index 0000000..2da4135 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleConfigImpl.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleImpl.java new file mode 100644 index 0000000..db8276b --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleImpl.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleManager.java new file mode 100644 index 0000000..2a59a84 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleManager.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 getBubbleConfigs(); + + @Nullable + Bubble getBubble(String id); + + @Nullable BubbleConfig getBubbleConfig(String id); + + Collection getBubbles(); + + boolean hasBubble(CNPlayer player, String id); + + Collection availableBubbles(CNPlayer player); + + Set blacklistChannels(); + + String defaultBubbleId(); + + Requirement[] sendBubbleRequirements(); + + Requirement[] viewBubbleRequirements(); + + String omittedText(); + + double verticalOffset(); + + int stayDuration(); + + int appearDuration(); + + int disappearDuration(); + + float viewRange(); + + ChannelMode channelMode(); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleManagerImpl.java new file mode 100644 index 0000000..7d84dec --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleManagerImpl.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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 blacklistChannels; + private ChannelMode channelMode; + private final HashMap 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 getBubbles() { + return new HashSet<>(bubbles.values()); + } + + @Override + public Collection 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 availableBubbles(CNPlayer player) { + ArrayList available = new ArrayList<>(); + for (BubbleConfig bubble : bubbleConfigs.values()) { + if (player.hasPermission("bubbles.equip." + bubble.id())) { + available.add(bubble); + } + } + return available; + } + + @Override + public Set 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 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 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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleTag.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleTag.java new file mode 100644 index 0000000..67cda50 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/BubbleTag.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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 spawnPacket(CNPlayer viewer) { + Tracker tracker = owner.getTracker(viewer); + Vector3 translation = translation(viewer); + List 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> modifier0 = CustomNameplates.getInstance().getPlatform().createInterpolationDelayModifier(-1); + Consumer> modifier1 = CustomNameplates.getInstance().getPlatform().createTransformationInterpolationDurationModifier(manager.appearDuration()); + Consumer> modifier2 = CustomNameplates.getInstance().getPlatform().createScaleModifier(bubbleConfig.scale()); + Consumer> 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 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 createDisappearPacket() { + Consumer> modifier0 = CustomNameplates.getInstance().getPlatform().createInterpolationDelayModifier(-1); + Consumer> modifier1 = CustomNameplates.getInstance().getPlatform().createTransformationInterpolationDurationModifier(manager.disappearDuration()); + Consumer> 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; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/ChannelMode.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/ChannelMode.java similarity index 92% rename from api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/ChannelMode.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/ChannelMode.java index 098d7bc..0a16057 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/ChannelMode.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/ChannelMode.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.mechanic.bubble; +package net.momirealms.customnameplates.api.feature.bubble; public enum ChannelMode { diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/AbstractChatManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/AbstractChatManager.java new file mode 100644 index 0000000..1dde029 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/AbstractChatManager.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 emojiProviders = new ArrayList<>(); + protected final List 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); + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/team/TeamColor.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/AbstractChatMessageProvider.java similarity index 55% rename from common/src/main/java/net/momirealms/customnameplates/common/team/TeamColor.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/AbstractChatMessageProvider.java index f7057ee..d341a34 100644 --- a/common/src/main/java/net/momirealms/customnameplates/common/team/TeamColor.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/AbstractChatMessageProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,32 +15,23 @@ * along with this program. If not, see . */ -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, - DARK_BLUE, - DARK_GREEN, - DARK_AQUA, - DARK_RED, - DARK_PURPLE, - GOLD, - GRAY, - DARK_GRAY, - BLUE, - GREEN, - AQUA, - RED, - LIGHT_PURPLE, - YELLOW, - WHITE, - NONE, - CUSTOM; + protected CustomNameplates plugin; + protected ChatManager manager; - public TeamColor getById(String id) throws IllegalArgumentException { - return valueOf(id.toUpperCase(Locale.ENGLISH)); + public AbstractChatMessageProvider(CustomNameplates plugin, ChatManager manager) { + this.plugin = plugin; + this.manager = manager; + } + + public void register() { + } + + public void unregister() { } } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/ChatManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/ChatManager.java new file mode 100644 index 0000000..a479a15 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/ChatManager.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/ChatMessageProvider.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/ChatMessageProvider.java new file mode 100644 index 0000000..06b3aca --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/chat/ChatMessageProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/emoji/EmojiProvider.java similarity index 73% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiRequirement.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/emoji/EmojiProvider.java index 496ed54..657c9c3 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiRequirement.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/bubble/emoji/EmojiProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -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); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/EntityTagEntity.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/Image.java similarity index 52% rename from api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/EntityTagEntity.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/image/Image.java index 8bf6c50..ba138c4 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/EntityTagEntity.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/Image.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,28 +15,34 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; +package net.momirealms.customnameplates.api.feature.image; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; +import net.momirealms.customnameplates.api.feature.ConfiguredCharacter; -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 getStaticTags(); + static Builder builder() { + return new ImageImpl.BuilderImpl(); + } - void forceAddNearbyPlayer(Player player); + interface Builder { - void forceRemoveNearbyPlayer(Player player); + Builder id(String id); - Entity getEntity(); + Builder hasShadow(boolean has); - void destroy(); + Builder opacity(int opacity); + + Builder character(ConfiguredCharacter character); + + Image build(); + } } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageImpl.java new file mode 100644 index 0000000..d534430 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageManager.java new file mode 100644 index 0000000..28db5d9 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageManager.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 getImages(); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageManagerImpl.java new file mode 100644 index 0000000..62904e8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/image/ImageManagerImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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 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 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); + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/Nameplate.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/Nameplate.java new file mode 100644 index 0000000..8e2affb --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/Nameplate.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateImpl.java new file mode 100644 index 0000000..63971ee --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateImpl.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/velocity/src/main/java/net/momirealms/customnameplates/velocity/team/VelocityTeamManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateManager.java similarity index 55% rename from velocity/src/main/java/net/momirealms/customnameplates/velocity/team/VelocityTeamManager.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateManager.java index d2fadb1..2a033ed 100644 --- a/velocity/src/main/java/net/momirealms/customnameplates/velocity/team/VelocityTeamManager.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,18 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.velocity.team; +package net.momirealms.customnameplates.api.feature.nameplate; -import com.velocitypowered.api.proxy.Player; -import net.kyori.adventure.text.Component; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.common.team.TeamTagVisibility; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; import org.jetbrains.annotations.Nullable; -public interface VelocityTeamManager { +import java.util.Collection; + +public interface NameplateManager extends Reloadable { @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 getNameplates(); + + boolean hasNameplate(CNPlayer player, String id); + + Collection availableNameplates(CNPlayer player); + + String defaultNameplateId(); + + String playerNameTag(); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateManagerImpl.java new file mode 100644 index 0000000..ef7732c --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/nameplate/NameplateManagerImpl.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 nameplates = new HashMap<>(); + private final Map 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 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 availableNameplates(CNPlayer player) { + ArrayList 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 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); + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/character/CharacterArranger.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/CharacterArranger.java similarity index 95% rename from api/src/main/java/net/momirealms/customnameplates/api/mechanic/character/CharacterArranger.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/pack/CharacterArranger.java index 34bd094..1c41204 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/character/CharacterArranger.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/CharacterArranger.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.mechanic.character; +package net.momirealms.customnameplates.api.feature.pack; public class CharacterArranger { diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/ResourcePackManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/ResourcePackManager.java new file mode 100644 index 0000000..9767119 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/ResourcePackManager.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.feature.pack; + +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; + +public interface ResourcePackManager extends Reloadable { + + void generate(); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/ResourcePackManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/ResourcePackManagerImpl.java new file mode 100644 index 0000000..562bdab --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/pack/ResourcePackManagerImpl.java @@ -0,0 +1,615 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 order = section.getStringList(font); + for (String f : order) { + String[] split = f.split(":", 2); + Map 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 = 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 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 getBubbles(File texturesFolder) { + ArrayList 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 getBackgrounds(File texturesFolder) { + ArrayList 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 getImages(File texturesFolder) { + ArrayList 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 getNameplates(File texturesFolder) { + ArrayList 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 getOffsets(File texturesFolder) { + this.saveSplit(texturesFolder); + ArrayList 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"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/AbstractTag.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/AbstractTag.java new file mode 100644 index 0000000..b294faa --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/AbstractTag.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 viewers = new Vector<>(); + protected CNPlayer[] viewerArray = new CNPlayer[0]; + + public AbstractTag(CNPlayer owner, TagRenderer renderer) { + this.owner = owner; + this.renderer = renderer; + } + + protected abstract List 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 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> 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> modifier1 = CustomNameplates.getInstance().getPlatform().createScaleModifier(scale(viewer).multiply(scale)); + Consumer> 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> 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; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTag.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTag.java new file mode 100644 index 0000000..32688db --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTag.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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 activePlaceholders() { + if (currentText == null || !isShown()) return Collections.emptySet(); + return currentText.placeholders(); + } + + @Override + public Set allPlaceholders() { + HashSet 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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTagConfig.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTagConfig.java new file mode 100644 index 0000000..aa89983 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTagConfig.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTagConfigImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTagConfigImpl.java new file mode 100644 index 0000000..d59a14d --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/NameTagConfigImpl.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/Tag.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/Tag.java new file mode 100644 index 0000000..b35ec4b --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/Tag.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/ActionBarManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/TagRenderer.java similarity index 59% rename from api/src/main/java/net/momirealms/customnameplates/api/manager/ActionBarManager.java rename to api/src/main/java/net/momirealms/customnameplates/api/feature/tag/TagRenderer.java index f53f5b3..f1d6ea0 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/ActionBarManager.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/TagRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,20 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.manager; +package net.momirealms.customnameplates.api.feature.tag; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; +import java.util.function.Predicate; -public interface ActionBarManager { +public interface TagRenderer { - /** - * Get the actionbar sent by other plugins in MiniMessage format - * Return "" if no other actionbar received - * - * @param player receiver - * @return text - */ - @NotNull - String getOtherPluginActionBar(Player player); + double hatOffset(); + + void hatOffset(double hatOffset); + + void onTick(); + + void destroy(); + + void addTag(Tag tag); + + Tag[] tags(); + + int removeTagIf(Predicate predicate); + + int tagIndex(Tag tag); + + void addTag(Tag tag, int index); + + void removeTag(Tag tag); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/TagRendererImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/TagRendererImpl.java new file mode 100644 index 0000000..d24c28a --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/TagRendererImpl.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 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 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 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 predicate) { + Set 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 realPassengers = owner.passengers(); + updatePassengers(another, realPassengers); + } + } + + private void updatePassengers(CNPlayer another, Set realPassengers) { + Set 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); + } + } + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/UnlimitedTagManager.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/UnlimitedTagManager.java new file mode 100644 index 0000000..06ffa0f --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/UnlimitedTagManager.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/UnlimitedTagManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/UnlimitedTagManagerImpl.java new file mode 100644 index 0000000..e88974a --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/tag/UnlimitedTagManagerImpl.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 configs = new LinkedHashMap<>(); + private final ConcurrentHashMap 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 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() + ); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/helper/AdventureHelper.java b/api/src/main/java/net/momirealms/customnameplates/api/helper/AdventureHelper.java new file mode 100644 index 0000000..1d0f5db --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/helper/AdventureHelper.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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 miniMessageToComponentCache; + private final Cache miniMessageToMinecraftComponentCache; + private final Cache minecraftComponentToMiniMessageCache; + private final Cache 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 + ""; + } + + /** + * 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 "" + text + ""; + } + + /** + * 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 "" + text + ""; + } + + /** + * 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(""); + case '1' -> stringBuilder.append(""); + case '2' -> stringBuilder.append(""); + case '3' -> stringBuilder.append(""); + case '4' -> stringBuilder.append(""); + case '5' -> stringBuilder.append(""); + case '6' -> stringBuilder.append(""); + case '7' -> stringBuilder.append(""); + case '8' -> stringBuilder.append(""); + case '9' -> stringBuilder.append(""); + case 'a' -> stringBuilder.append(""); + case 'b' -> stringBuilder.append(""); + case 'c' -> stringBuilder.append(""); + case 'd' -> stringBuilder.append(""); + case 'e' -> stringBuilder.append(""); + case 'f' -> stringBuilder.append(""); + case 'r' -> stringBuilder.append(""); + case 'l' -> stringBuilder.append(""); + case 'm' -> stringBuilder.append(""); + case 'o' -> stringBuilder.append(""); + case 'n' -> stringBuilder.append(""); + case 'k' -> stringBuilder.append(""); + 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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/helper/GsonHelper.java b/api/src/main/java/net/momirealms/customnameplates/api/helper/GsonHelper.java new file mode 100644 index 0000000..85445e6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/helper/GsonHelper.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/helper/VersionHelper.java b/api/src/main/java/net/momirealms/customnameplates/api/helper/VersionHelper.java new file mode 100644 index 0000000..37eae69 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/helper/VersionHelper.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.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> UPDATE_CHECKER = (plugin) -> { + CompletableFuture 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; + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/AdventureManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/AdventureManager.java deleted file mode 100644 index cd1c77c..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/AdventureManager.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/BackGroundManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/BackGroundManager.java deleted file mode 100644 index 4e4f720..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/BackGroundManager.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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 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); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/BubbleManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/BubbleManager.java deleted file mode 100644 index 95405e0..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/BubbleManager.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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 getAvailableBubbles(Player player); - - /** - * Get a list of the bubbles' display names that a player has - * - * @param player player - * @return bubbles' display names - */ - List getAvailableBubblesDisplayNames(Player player); - - /** - * Get blacklist chat channels - * - * @return channels - */ - String[] getBlacklistChannels(); - - /** - * Get all the bubbles - * - * @return bubbles - */ - Collection 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 getBubbleKeys(); - - /** - * Trigger chat - * - * @param player player - * @param text text - */ - void onChat(Player player, String text); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/ImageManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/ImageManager.java deleted file mode 100644 index 4396a46..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/ImageManager.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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 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); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/NameplateManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/NameplateManager.java deleted file mode 100644 index 64afb27..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/NameplateManager.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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 getNameplates(); - - /** - * Get all the nameplates' keys - * - * @return keys - */ - @NotNull - Collection 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 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 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(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/PlaceholderManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/PlaceholderManager.java deleted file mode 100644 index 5c2d4d6..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/PlaceholderManager.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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 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 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 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 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 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 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 getBackGroundTexts(); - - /** - * Get a vanilla hud - * - * @param key key - * @return vanilla hud - */ - VanillaHud getVanillaHud(String key); - - /** - * Get all the vanilla huds - * - * @return vanilla huds - */ - Collection getVanillaHuds(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/RequirementManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/RequirementManager.java deleted file mode 100644 index 6c9a77f..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/RequirementManager.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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. - *

- * 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. - *

- * 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; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/StorageManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/StorageManager.java deleted file mode 100644 index 81d8f43..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/StorageManager.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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 getOnlineUsers(); - - /** - * Get a player's data by uuid - * The player can be an offline one - * - * @param uuid uuid - * @return player data - */ - CompletableFuture> getPlayerData(UUID uuid); - - /** - * Save online players' data - * - * @param uuid uuid - * @return success or not - */ - CompletableFuture saveOnlinePlayerData(UUID uuid); - - /** - * Save specified data - * - * @param uuid uuid - * @param playerData playerData - * @return success or not - */ - CompletableFuture savePlayerData(UUID uuid, PlayerData playerData); - - /** - * Get an online user by uuid - * - * @param uuid uuid - * @return online user - */ - Optional 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(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/TeamManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/TeamManager.java deleted file mode 100644 index 76ca7bc..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/TeamManager.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/TeamTagManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/TeamTagManager.java deleted file mode 100644 index c28e0c9..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/TeamTagManager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/UnlimitedTagManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/UnlimitedTagManager.java deleted file mode 100644 index cd9dce7..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/UnlimitedTagManager.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.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); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/VersionManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/VersionManager.java deleted file mode 100644 index 9a6eb78..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/VersionManager.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.manager; - -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.CompletionStage; - -public interface VersionManager { - - /** - * Is folia scheduler implemented - */ - boolean isFolia(); - - /** - * Check update - * - * @return return true if plugin needs update - */ - CompletionStage checkUpdate(); - - boolean isVersionNewerThan1_20_5(); - - /** - * Get plugin version - * - * @return version - */ - @NotNull - String getPluginVersion(); - - /** - * Is the plugin the latest version - * - * @return latest or not - */ - boolean isLatest(); - - /** - * Get pack format - * - * @return pack format - */ - int getPackFormat(); - - boolean isVersionNewerThan1_19(); - - boolean isMojmap(); - - boolean isVersionNewerThan1_19_R2(); - - boolean isVersionNewerThan1_19_R3(); - - boolean isVersionNewerThan1_20(); - - boolean isVersionNewerThan1_20_R2(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/WidthManager.java b/api/src/main/java/net/momirealms/customnameplates/api/manager/WidthManager.java deleted file mode 100644 index 3191062..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/WidthManager.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.manager; - -import net.momirealms.customnameplates.api.mechanic.font.FontData; -import net.momirealms.customnameplates.common.Key; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface WidthManager { - - /** - * Register a font data into the plugin - * If there already exists one, it would fail - * - * @param key key - * @param fontData data - * @return success or not - */ - boolean registerFontData(@NotNull Key key, @NotNull FontData fontData); - - /** - * Unregister a font data from map - * - * @param key key - * @return success or not - */ - boolean unregisterFontData(@NotNull Key key); - - /** - * Get font data by key - * - * @param key key - * @return font data - */ - @Nullable - FontData getFontData(@NotNull Key key); - - /** - * Get text's width (MiniMessage format text) - * - * @param textWithTags text - * @return width - */ - int getTextWidth(@NotNull String textWithTags); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/background/BackGround.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/background/BackGround.java deleted file mode 100644 index ec78ed7..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/background/BackGround.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.background; - -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; - -public class BackGround { - - private ConfiguredChar left, offset_1, offset_2, offset_4, offset_8, offset_16, offset_32, offset_64, offset_128, right; - private int leftMargin, rightMargin; - - private BackGround() { - } - - public BackGround( - ConfiguredChar left, - ConfiguredChar offset_1, - ConfiguredChar offset_2, - ConfiguredChar offset_4, - ConfiguredChar offset_8, - ConfiguredChar offset_16, - ConfiguredChar offset_32, - ConfiguredChar offset_64, - ConfiguredChar offset_128, - ConfiguredChar right, - int leftMargin, - int rightMargin, - boolean removeShadow - ) { - this.left = left; - this.offset_1 = offset_1; - this.offset_2 = offset_2; - this.offset_4 = offset_4; - this.offset_8 = offset_8; - this.offset_16 = offset_16; - this.offset_32 = offset_32; - this.offset_64 = offset_64; - this.offset_128 = offset_128; - this.right = right; - this.leftMargin = leftMargin; - this.rightMargin = rightMargin; - } - - public ConfiguredChar getLeft() { - return left; - } - - public ConfiguredChar getOffset_1() { - return offset_1; - } - - public ConfiguredChar getOffset_2() { - return offset_2; - } - - public ConfiguredChar getOffset_4() { - return offset_4; - } - - public ConfiguredChar getOffset_8() { - return offset_8; - } - - public ConfiguredChar getOffset_16() { - return offset_16; - } - - public ConfiguredChar getOffset_32() { - return offset_32; - } - - public ConfiguredChar getOffset_64() { - return offset_64; - } - - public ConfiguredChar getOffset_128() { - return offset_128; - } - - public ConfiguredChar getRight() { - return right; - } - - public static Builder builder() { - return new Builder(); - } - - public String getBackGroundImage(int n) { - String offset = OffsetFont.getShortestNegChars(n + rightMargin + 2); - n = n + leftMargin + rightMargin + 2; - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(left.getCharacter()); - while (n >= 128) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_128.getCharacter()); - n -= 128; - } - if (n - 64 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_64.getCharacter()); - n -= 64; - } - if (n - 32 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_32.getCharacter()); - n -= 32; - } - if (n - 16 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_16.getCharacter()); - n -= 16; - } - if (n - 8 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_8.getCharacter()); - n -= 8; - } - if (n - 4 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_4.getCharacter()); - n -= 4; - } - if (n - 2 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_2.getCharacter()); - n -= 2; - } - if (n - 1 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(offset_1.getCharacter()); - } - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(right.getCharacter()); - stringBuilder.append(offset); - return stringBuilder.toString(); - } - - public static class Builder { - - private final BackGround backGround; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.backGround = new BackGround(); - } - - public Builder left(ConfiguredChar configuredChar) { - backGround.left = configuredChar; - return this; - } - - public Builder right(ConfiguredChar configuredChar) { - backGround.right = configuredChar; - return this; - } - - public Builder offset_1(ConfiguredChar configuredChar) { - backGround.offset_1 = configuredChar; - return this; - } - - public Builder offset_2(ConfiguredChar configuredChar) { - backGround.offset_2 = configuredChar; - return this; - } - - public Builder offset_4(ConfiguredChar configuredChar) { - backGround.offset_4 = configuredChar; - return this; - } - - public Builder offset_8(ConfiguredChar configuredChar) { - backGround.offset_8 = configuredChar; - return this; - } - - public Builder offset_16(ConfiguredChar configuredChar) { - backGround.offset_16 = configuredChar; - return this; - } - - public Builder offset_32(ConfiguredChar configuredChar) { - backGround.offset_32 = configuredChar; - return this; - } - - public Builder offset_64(ConfiguredChar configuredChar) { - backGround.offset_64 = configuredChar; - return this; - } - - public Builder offset_128(ConfiguredChar configuredChar) { - backGround.offset_128 = configuredChar; - return this; - } - - public Builder leftMargin(int margin) { - backGround.leftMargin = margin; - return this; - } - - public Builder rightMargin(int margin) { - backGround.rightMargin = margin; - return this; - } - - public BackGround build() { - return backGround; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/Bubble.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/Bubble.java deleted file mode 100644 index 9883c17..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/Bubble.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.bubble; - -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.util.FontUtils; - -public class Bubble { - - private String startFormat, endFormat; - private String displayName; - private ConfiguredChar left, middle, right, tail; - - private Bubble() { - } - - public Bubble(String startFormat, String endFormat, String displayName, ConfiguredChar left, ConfiguredChar middle, ConfiguredChar right, ConfiguredChar tail) { - this.startFormat = startFormat; - this.endFormat = endFormat; - this.displayName = displayName; - this.left = left; - this.middle = middle; - this.right = right; - this.tail = tail; - } - - public String getPrefixWithFont(int textWidth) { - return FontUtils.surroundNameplateFont(getPrefix(textWidth)); - } - - public String getPrefix(int textWidth) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("<#FEFEFE>"); - stringBuilder.append(OffsetFont.getShortestNegChars(textWidth + left.getWidth() + 1)); - stringBuilder.append(left.getCharacter()); - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - int mid_amount; - if (textWidth - 1 <= tail.getWidth()) { - mid_amount = -1; - } else { - mid_amount = (textWidth - 1 - tail.getWidth()) / (middle.getWidth()); - } - if (mid_amount == -1) { - stringBuilder.append("<#FDFEFE>").append(tail.getCharacter()).append("").append(OffsetFont.NEG_1.getCharacter()); - } else if (mid_amount == 0) { - stringBuilder.append("<#FDFEFE>").append(tail.getCharacter()).append("").append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(OffsetFont.getShortestNegChars(middle.getWidth() - (textWidth - 1 - tail.getWidth()) % middle.getWidth())); - stringBuilder.append(middle.getCharacter()).append(OffsetFont.NEG_1.getCharacter()); - } else { - stringBuilder.append(middle.getCharacter()).append(OffsetFont.NEG_1.getCharacter()); - for (int i = 0; i < mid_amount; i++) { - if (i == mid_amount / 2) stringBuilder.append("<#FDFEFE>").append(tail.getCharacter()).append("").append(OffsetFont.NEG_1.getCharacter()); - else stringBuilder.append(middle.getCharacter()).append(OffsetFont.NEG_1.getCharacter()); - } - stringBuilder.append(OffsetFont.getShortestNegChars(middle.getWidth() - (textWidth - 1 - tail.getWidth()) % middle.getWidth())); - stringBuilder.append(middle.getCharacter()).append(OffsetFont.NEG_1.getCharacter()); - } - stringBuilder.append("<#FDFEFE>"); - stringBuilder.append(right.getCharacter()); - stringBuilder.append(""); - stringBuilder.append(OffsetFont.getShortestNegChars(textWidth + right.getWidth())); - stringBuilder.append(""); - return stringBuilder.toString(); - } - - public String getSuffixWithFont(int textWidth) { - return FontUtils.surroundNameplateFont(getSuffix(textWidth)); - } - - public String getSuffix(int textWidth) { - return OffsetFont.getShortestNegChars(textWidth + textWidth % 2 + 1); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private final Bubble bubble; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.bubble = new Bubble(); - } - - public Builder startFormat(String startFormat) { - this.bubble.startFormat = startFormat; - return this; - } - - public Builder endFormat(String endFormat) { - this.bubble.endFormat = endFormat; - return this; - } - - public Builder left(ConfiguredChar left) { - this.bubble.left = left; - return this; - } - - public Builder displayName(String displayName) { - this.bubble.displayName = displayName; - return this; - } - - public Builder middle(ConfiguredChar middle) { - this.bubble.middle = middle; - return this; - } - - public Builder right(ConfiguredChar right) { - this.bubble.right = right; - return this; - } - - public Builder tail(ConfiguredChar tail) { - this.bubble.tail = tail; - return this; - } - - public Bubble build() { - return bubble; - } - } - - public String getStartFormat() { - return startFormat; - } - - public String getEndFormat() { - return endFormat; - } - - public String getDisplayName() { - return displayName; - } - - public ConfiguredChar getLeft() { - return left; - } - - public ConfiguredChar getMiddle() { - return middle; - } - - public ConfiguredChar getRight() { - return right; - } - - public ConfiguredChar getTail() { - return tail; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/provider/AbstractChatProvider.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/provider/AbstractChatProvider.java deleted file mode 100644 index 05a4766..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/bubble/provider/AbstractChatProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.bubble.provider; - -import net.momirealms.customnameplates.api.manager.BubbleManager; -import org.bukkit.entity.Player; -import org.bukkit.event.Listener; - -public abstract class AbstractChatProvider implements Listener { - - protected BubbleManager chatBubblesManager; - - public AbstractChatProvider(BubbleManager chatBubblesManager) { - this.chatBubblesManager = chatBubblesManager; - } - - public abstract void register(); - - public abstract void unregister(); - - public abstract boolean hasJoinedChannel(Player player, String channelID); - - public abstract boolean canJoinChannel(Player player, String channelID); - - public abstract boolean isIgnoring(Player sender, Player receiver); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/character/ConfiguredChar.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/character/ConfiguredChar.java deleted file mode 100644 index 09267c7..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/character/ConfiguredChar.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.character; - -import net.momirealms.customnameplates.api.util.LogUtils; - -public class ConfiguredChar { - - private char character; - private String pngFile; - private int height; - private int width; - private int ascent; - - private ConfiguredChar() { - - } - - public ConfiguredChar(char character, String pngFile, int height, int width, int ascent) { - this.character = character; - this.pngFile = pngFile; - this.height = height; - this.width = width; - this.ascent = ascent; - } - - public char getCharacter() { - return character; - } - - public static Builder builder() { - return new Builder(); - } - - public String getPngFile() { - return pngFile; - } - - public int getHeight() { - return height; - } - - public int getWidth() { - return width; - } - - public int getAscent() { - return ascent; - } - - public String getFile() { - return pngFile + ".png"; - } - - public static class Builder { - - private final ConfiguredChar configuredChar; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.configuredChar = new ConfiguredChar(); - } - - public Builder character(char character) { - configuredChar.character = character; - return this; - } - - public Builder png(String png) { - configuredChar.pngFile = png; - return this; - } - - public Builder height(int height) { - configuredChar.height = height; - return this; - } - - public Builder ascent(int ascent) { - configuredChar.ascent = ascent; - if (ascent >= configuredChar.height) { - LogUtils.warn("Invalid config for " + configuredChar.pngFile); - LogUtils.warn("Ascent " + ascent + " should be no higher than Height " + configuredChar.height); - } - return this; - } - - public Builder descent(int descent) { - if (descent < 0) { - LogUtils.warn("Invalid config for " + configuredChar.pngFile); - LogUtils.warn("Descent " + descent + " should be no lower than 0"); - } - configuredChar.ascent = configuredChar.height - descent; - return this; - } - - public Builder width(int width) { - configuredChar.width = width; - return this; - } - - public ConfiguredChar build() { - return configuredChar; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/font/FontData.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/font/FontData.java deleted file mode 100644 index b25a943..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/font/FontData.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.font; - -import java.util.HashMap; - -public class FontData { - - private final HashMap widthData; - private final int defaultWidth; - - public FontData(int defaultWidth) { - this.widthData = new HashMap<>(); - this.defaultWidth = defaultWidth; - } - - /** - * Register a char's width - * - * @param codePoint code point - * @param width width - */ - public void registerCharWidth(int codePoint, int width) { - widthData.put(codePoint, width); - } - - /** - * Get a character's width - * - * @param codePoint code point - * @return width - */ - public int getWidth(int codePoint) { - return widthData.getOrDefault(codePoint, defaultWidth); - } - - /** - * Get the data map - * - * @return map - */ - public HashMap getWidthData() { - return widthData; - } - - /** - * Override the data with another one - * - * @param fontData font data - */ - public void overrideWith(FontData fontData) { - if (fontData == null) return; - widthData.putAll(fontData.getWidthData()); - } - - @Override - public String toString() { - return "FontData{" + - "widthData=" + widthData + - ", defaultWidth=" + defaultWidth + - '}'; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/font/OffsetFont.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/font/OffsetFont.java deleted file mode 100644 index 381f2b6..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/font/OffsetFont.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.font; - -public enum OffsetFont { - - NEG_1('\uf801', -1, -3), - NEG_2('\uf802', -2, -4), - NEG_3('\uf803', -3, -5), - NEG_4('\uf804', -4, -6), - NEG_5('\uf805', -5, -7), - NEG_6('\uf806', -6, -8), - NEG_7('\uf807', -7, -9), - NEG_8('\uf808', -8, -10), - NEG_16('\uf809', -16, -18), - NEG_32('\uf80a', -32, -34), - NEG_64('\uf80b', -64, -66), - NEG_128('\uf80c', -128, -130), - POS_1('\uf811', 1, -1), - POS_2('\uf812', 2, 1), - POS_3('\uf813', 3, 2), - POS_4('\uf814', 4, 3), - POS_5('\uf815', 5, 4), - POS_6('\uf816', 6, 5), - POS_7('\uf817', 7, 6), - POS_8('\uf818', 8, 7), - POS_16('\uf819', 16, 15), - POS_32('\uf81a', 32, 31), - POS_64('\uf81b', 64, 63), - POS_128('\uf81c', 128, 127); - - private final char character; - private final int space; - private final int height; - - OffsetFont(char character, int space, int height) { - this.character = character; - this.space = space; - this.height = height; - } - - public char getCharacter() { - return this.character; - } - - public int getSpace() { - return this.space; - } - - public int getHeight() { - return this.height; - } - - /** - * Get offset characters - * - * @param offset offset - * @return chars - */ - public static String getOffsetChars(int offset) { - if (offset >= 0) { - return getShortestPosChars(offset); - } else { - return getShortestNegChars(-offset); - } - } - - /** - * get negative characters - * - * @param n n > 0 - * @return chars - */ - public static String getShortestNegChars(int n) { - StringBuilder stringBuilder = new StringBuilder(); - while (n >= 128) { - stringBuilder.append(OffsetFont.NEG_128.getCharacter()); - n -= 128; - } - if (n - 64 >= 0) { - stringBuilder.append(OffsetFont.NEG_64.getCharacter()); - n -= 64; - } - if (n - 32 >= 0) { - stringBuilder.append(OffsetFont.NEG_32.getCharacter()); - n -= 32; - } - if (n - 16 >= 0) { - stringBuilder.append(OffsetFont.NEG_16.getCharacter()); - n -= 16; - } - if (n - 8 >= 0) { - stringBuilder.append(OffsetFont.NEG_8.getCharacter()); - n -= 8; - } - if (n - 7 >= 0) { - stringBuilder.append(OffsetFont.NEG_7.getCharacter()); - n -= 7; - } - if (n - 6 >= 0) { - stringBuilder.append(OffsetFont.NEG_6.getCharacter()); - n -= 6; - } - if (n - 5 >= 0) { - stringBuilder.append(OffsetFont.NEG_5.getCharacter()); - n -= 5; - } - if (n - 4 >= 0) { - stringBuilder.append(OffsetFont.NEG_4.getCharacter()); - n -= 4; - } - if (n - 3 >= 0) { - stringBuilder.append(OffsetFont.NEG_3.getCharacter()); - n -= 3; - } - if (n - 2 >= 0) { - stringBuilder.append(OffsetFont.NEG_2.getCharacter()); - n -= 2; - } - if (n - 1 >= 0) { - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - } - return stringBuilder.toString(); - } - - /** - * get positive characters - * - * @param n n > 0 - * @return chars - */ - public static String getShortestPosChars(int n) { - StringBuilder stringBuilder = new StringBuilder(); - while (n >= 128) { - stringBuilder.append(OffsetFont.POS_128.getCharacter()); - n -= 128; - } - if (n - 64 >= 0) { - stringBuilder.append(OffsetFont.POS_64.getCharacter()); - n -= 64; - } - if (n - 32 >= 0) { - stringBuilder.append(OffsetFont.POS_32.getCharacter()); - n -= 32; - } - if (n - 16 >= 0) { - stringBuilder.append(OffsetFont.POS_16.getCharacter()); - n -= 16; - } - if (n - 8 >= 0) { - stringBuilder.append(OffsetFont.POS_8.getCharacter()); - n -= 8; - } - if (n - 7 >= 0) { - stringBuilder.append(OffsetFont.POS_7.getCharacter()); - n -= 7; - } - if (n - 6 >= 0) { - stringBuilder.append(OffsetFont.POS_6.getCharacter()); - n -= 6; - } - if (n - 5 >= 0) { - stringBuilder.append(OffsetFont.POS_5.getCharacter()); - n -= 5; - } - if (n - 4 >= 0) { - stringBuilder.append(OffsetFont.POS_4.getCharacter()); - n -= 4; - } - if (n - 3 >= 0) { - stringBuilder.append(OffsetFont.POS_3.getCharacter()); - n -= 3; - } - if (n - 2 >= 0) { - stringBuilder.append(OffsetFont.POS_2.getCharacter()); - n -= 2; - } - if (n - 1 >= 0) { - stringBuilder.append(OffsetFont.POS_1.getCharacter()); - } - return stringBuilder.toString(); - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/misc/ViewerText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/misc/ViewerText.java deleted file mode 100644 index 727ea99..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/misc/ViewerText.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.misc; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; - -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class ViewerText { - - private final Player owner; - private String processedText; - private final ClaimedText[] placeholders; - private final ConcurrentHashMap valueMap; - - public ViewerText(Player owner, String rawText) { - this.processedText = rawText; - this.valueMap = new ConcurrentHashMap<>(); - this.owner = owner; - List placeholders = CustomNameplatesPlugin.get().getPlaceholderManager().detectPlaceholders(rawText); - this.placeholders = new ClaimedText[placeholders.size()]; - int i = 0; - for (String placeholder : placeholders) { - processedText = processedText.replace(placeholder, "%s"); - if (placeholder.startsWith("%viewer_")) { - this.placeholders[i] = new ClaimedText(null, "%" + placeholder.substring("%viewer_".length()), false); - } else if (placeholder.startsWith("%rel_")) { - this.placeholders[i] = new ClaimedText(owner, placeholder, true); - } else { - this.placeholders[i] = new ClaimedText(owner, placeholder, false); - } - i++; - } - } - - public void updateForOwner() { - for (ClaimedText text : placeholders) { - text.update(); - } - } - - public boolean updateForViewer(Player viewer) { - String string; - if ("%s".equals(processedText)) { - string = placeholders[0].getValue(viewer); - } else if (placeholders.length != 0) { - Object[] values = new String[placeholders.length]; - for (int i = 0; i < placeholders.length; i++) { - values[i] = placeholders[i].getValue(viewer); - } - string = String.format(processedText, values); - } else { - string = processedText; - } - var uuid = viewer.getUniqueId(); - if (!valueMap.containsKey(uuid)) { - valueMap.put(uuid, string); - return true; - } - String previousValue = valueMap.get(uuid); - if (!previousValue.equals(string)) { - valueMap.put(uuid, string); - return true; - } - return false; - } - - public void removeViewer(Player viewer) { - valueMap.remove(viewer.getUniqueId()); - } - - public void clear() { - valueMap.clear(); - } - - public String getProcessedText() { - return processedText; - } - - public String getLatestValue(Player viewer) { - return valueMap.get(viewer.getUniqueId()); - } - - public Entity getOwner() { - return owner; - } - - public static class ClaimedText { - - private final String placeholder; - private final Player owner; - private String latestValue; - private final boolean relational; - - public ClaimedText(Player owner, String placeholder, boolean relational) { - this.placeholder = placeholder; - this.owner = owner; - this.latestValue = null; - this.relational = relational; - this.update(); - } - - public void update() { - if (owner == null || relational) return; - this.latestValue = PlaceholderAPI.setPlaceholders(owner, placeholder); - } - - public String getValue(Player viewer) { - return Objects.requireNonNullElseGet( - latestValue, - () -> { - if (relational) { - return PlaceholderAPI.setRelationalPlaceholders(viewer, owner, placeholder); - } else { - return PlaceholderAPI.setPlaceholders(owner == null ? viewer : owner, placeholder); - } - } - ); - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/CachedNameplate.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/CachedNameplate.java deleted file mode 100644 index 3722e46..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/CachedNameplate.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.nameplate; - -import net.momirealms.customnameplates.common.team.TeamColor; - -public class CachedNameplate { - - private TeamColor teamColor; - private String tagPrefix; - private String tagSuffix; - private String namePrefix; - private String nameSuffix; - private String playerName; - - public CachedNameplate() { - this.tagPrefix = ""; - this.tagSuffix = ""; - this.namePrefix = ""; - this.nameSuffix = ""; - this.playerName = ""; - this.teamColor = TeamColor.WHITE; - } - - public String getTagPrefix() { - return tagPrefix; - } - - public void setTagPrefix(String prefix) { - this.tagPrefix = prefix; - } - - public String getTagSuffix() { - return tagSuffix; - } - - public void setTagSuffix(String suffix) { - this.tagSuffix = suffix; - } - - public void setNamePrefix(String namePrefix) { - this.namePrefix = namePrefix; - } - - public void setNameSuffix(String nameSuffix) { - this.nameSuffix = nameSuffix; - } - - public String getNamePrefix() { - return namePrefix; - } - - public String getNameSuffix() { - return nameSuffix; - } - - public TeamColor getTeamColor() { - return teamColor; - } - - public void setTeamColor(TeamColor teamColor) { - this.teamColor = teamColor; - } - - public String getPlayerName() { - return playerName; - } - - public void setPlayerName(String playerName) { - this.playerName = playerName; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/Nameplate.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/Nameplate.java deleted file mode 100644 index 8ec611d..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/Nameplate.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.nameplate; - -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.util.FontUtils; -import net.momirealms.customnameplates.common.team.TeamColor; - -public class Nameplate { - - private String displayName; - private TeamColor teamColor; - private String namePrefix; - private String nameSuffix; - private ConfiguredChar left; - private ConfiguredChar middle; - private ConfiguredChar right; - - private Nameplate() { - } - - public Nameplate( - String displayName, - TeamColor teamColor, - String namePrefix, - String nameSuffix, - ConfiguredChar left, - ConfiguredChar middle, - ConfiguredChar right - ) { - this.displayName = displayName; - this.teamColor = teamColor; - this.left = left; - this.middle = middle; - this.right = right; - this.namePrefix = namePrefix; - this.nameSuffix = nameSuffix; - } - - public String getDisplayName() { - return displayName; - } - - public TeamColor getTeamColor() { - return teamColor; - } - - public ConfiguredChar getLeft() { - return left; - } - - public ConfiguredChar getMiddle() { - return middle; - } - - public ConfiguredChar getRight() { - return right; - } - - public String getNamePrefix() { - if (teamColor == TeamColor.NONE) { - return ""; - } - if (teamColor == TeamColor.CUSTOM) { - return namePrefix; - } - return "<" + teamColor.name() + ">"; - } - - public String getNameSuffix() { - if (teamColor == TeamColor.NONE) { - return ""; - } - if (teamColor == TeamColor.CUSTOM) { - return nameSuffix; - } - return ""; - } - - public static Builder builder() { - return new Builder(); - } - - public String getPrefixWithFont(int textWidth) { - return FontUtils.surroundNameplateFont(getPrefix(textWidth)); - } - - public String getPrefix(int textWidth) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("<#FEFEFE>"); - stringBuilder.append(OffsetFont.getShortestNegChars(textWidth + left.getWidth() + 1)); - stringBuilder.append(left.getCharacter()); - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - int mid_amount = (textWidth+2) / (middle.getWidth()); - if (mid_amount != 0) { - for (int i = 0; i < mid_amount; i++) { - stringBuilder.append(middle.getCharacter()); - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - } - stringBuilder.append(OffsetFont.getShortestNegChars(middle.getWidth() - (textWidth+2) % middle.getWidth() + 1)); // +1 - } - stringBuilder.append(""); - stringBuilder.append("<#FDFEFE>"); - stringBuilder.append(middle.getCharacter()); - stringBuilder.append(OffsetFont.NEG_1.getCharacter()); - stringBuilder.append(right.getCharacter()); - stringBuilder.append(OffsetFont.getShortestNegChars(textWidth + right.getWidth() + 1)); // -1; - stringBuilder.append(""); - return stringBuilder.toString(); - } - - public String getSuffixWithFont(int textWidth) { - return FontUtils.surroundNameplateFont(getSuffix(textWidth)); - } - - public String getSuffix(int textWidth) { - return OffsetFont.getShortestNegChars(textWidth + textWidth % 2 + 1); - } - - public static class Builder { - - private final Nameplate nameplate; - - public Builder() { - this.nameplate = new Nameplate(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder displayName(String display) { - nameplate.displayName = display; - return this; - } - - public Builder teamColor(TeamColor teamColor) { - nameplate.teamColor = teamColor; - return this; - } - - public Builder namePrefix(String namePrefix) { - nameplate.namePrefix = namePrefix; - return this; - } - - public Builder nameSuffix(String nameSuffix) { - nameplate.nameSuffix = nameSuffix; - return this; - } - - public Builder left(ConfiguredChar configuredChar) { - nameplate.left = configuredChar; - return this; - } - - public Builder middle(ConfiguredChar configuredChar) { - nameplate.middle = configuredChar; - return this; - } - - public Builder right(ConfiguredChar configuredChar) { - nameplate.right = configuredChar; - return this; - } - - public Nameplate build() { - return nameplate; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/BackGroundText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/BackGroundText.java deleted file mode 100644 index fe5b3de..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/BackGroundText.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.mechanic.background.BackGround; -import net.momirealms.customnameplates.api.util.FontUtils; -import org.bukkit.OfflinePlayer; - -public class BackGroundText { - - private String text; - private BackGround backGround; - private boolean removeShadow; - - private BackGroundText() { - } - - public BackGroundText(String text, BackGround backGround, boolean removeShadow) { - this.text = text; - this.backGround = backGround; - this.removeShadow = removeShadow; - } - - public String getText() { - return text; - } - - public BackGround getBackGround() { - return backGround; - } - - public String getValue(OfflinePlayer player) { - String parsed = PlaceholderAPI.setPlaceholders(player, text); - if (parsed.equals("")) return ""; - int parsedWidth = FontUtils.getTextWidth(parsed); - String bg = FontUtils.surroundNameplateFont(backGround.getBackGroundImage(parsedWidth)); - return (removeShadow ? "<#FFFEFD>" + bg + "" : bg)+ parsed; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final BackGroundText text; - - public Builder() { - this.text = new BackGroundText(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder text(String value) { - text.text = value; - return this; - } - - public Builder background(BackGround backGround) { - text.backGround = backGround; - return this; - } - - public Builder removeShadow(boolean remove) { - text.removeShadow = remove; - return this; - } - - public BackGroundText build() { - return text; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/CachedText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/CachedText.java deleted file mode 100644 index 82283af..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/CachedText.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -public class CachedText { - - private long refreshInterval; - private String text; - - private CachedText() { - } - - public CachedText(long refreshInterval, String text) { - this.refreshInterval = refreshInterval; - this.text = text; - } - - public long getRefreshInterval() { - return refreshInterval; - } - - public String getText() { - return text; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final CachedText text; - - public Builder() { - this.text = new CachedText(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder refreshInterval(long time) { - this.text.refreshInterval = time; - return this; - } - - public Builder text(String text) { - this.text.text = text; - return this; - } - - public CachedText build() { - return text; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/ConditionalText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/ConditionalText.java deleted file mode 100644 index dd1c761..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/ConditionalText.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.manager.RequirementManager; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.common.Pair; -import org.bukkit.OfflinePlayer; - -import java.util.List; - -public class ConditionalText { - - private List> textList; - - private ConditionalText() { - } - - public ConditionalText(List> textList) { - this.textList = textList; - } - - public static Builder builder() { - return new Builder(); - } - - public String getValue(OfflinePlayer player) { - Condition condition = new Condition(player); - for (Pair pair : textList) { - if (RequirementManager.isRequirementMet(condition, pair.right())) { - return PlaceholderAPI.setPlaceholders(player, pair.left()); - } - } - return ""; - } - - public static class Builder { - - private final ConditionalText conditionalText; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.conditionalText = new ConditionalText(); - } - - public Builder textList(List> textList) { - conditionalText.textList = textList; - return this; - } - - public ConditionalText build() { - return conditionalText; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/DescentText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/DescentText.java deleted file mode 100644 index e0fc65c..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/DescentText.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.util.FontUtils; -import org.bukkit.OfflinePlayer; - -public class DescentText { - - private int ascent; - private String text; - private boolean isUnicode; - - public DescentText(int ascent, String text, boolean isUnicode) { - this.ascent = ascent; - this.text = text; - this.isUnicode = isUnicode; - } - - private DescentText() { - - } - - public int getAscent() { - return ascent; - } - - public boolean isUnicode() { - return isUnicode; - } - - public static Builder builder() { - return new Builder(); - } - - public String getValue(OfflinePlayer player) { - var parsed = PlaceholderAPI.setPlaceholders(player, text); - return FontUtils.surroundAscentFont(parsed, ascent); - } - - public static class Builder { - - private final DescentText descentText; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.descentText = new DescentText(); - } - - public Builder ascent(int ascent) { - descentText.ascent = ascent; - return this; - } - - public Builder descent(int descent) { - descentText.ascent = 8 - descent; - return this; - } - - public Builder text(String text) { - descentText.text = text; - return this; - } - - public Builder unicode(boolean unicode) { - descentText.isUnicode = unicode; - return this; - } - - public DescentText build() { - return descentText; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/NameplateText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/NameplateText.java deleted file mode 100644 index d0981b4..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/NameplateText.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate; -import net.momirealms.customnameplates.api.util.FontUtils; -import org.bukkit.OfflinePlayer; - -public class NameplateText { - - private String text; - private Nameplate nameplate; - - private NameplateText() { - } - - public NameplateText(String text, Nameplate nameplate) { - this.text = text; - this.nameplate = nameplate; - } - - public String getText() { - return text; - } - - public Nameplate getNameplate() { - return nameplate; - } - - public String getValue(OfflinePlayer player) { - String temp; - switch (nameplate.getTeamColor()) { - case CUSTOM -> temp = nameplate.getNamePrefix() + text + nameplate.getNameSuffix(); - case NONE -> temp = text; - default -> temp = "<" + nameplate.getTeamColor().name() + ">" + text + ""; - } - String parsed = PlaceholderAPI.setPlaceholders(player, temp); - int parsedWidth = FontUtils.getTextWidth(parsed); - return nameplate.getPrefixWithFont(parsedWidth) + parsed + nameplate.getSuffixWithFont(parsedWidth); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final NameplateText text; - - public Builder() { - this.text = new NameplateText(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder text(String value) { - text.text = value; - return this; - } - - public Builder nameplate(Nameplate value) { - text.nameplate = value; - return this; - } - - public NameplateText build() { - return text; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/StaticText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/StaticText.java deleted file mode 100644 index d6675fe..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/StaticText.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.util.FontUtils; -import org.bukkit.OfflinePlayer; - -public class StaticText { - - private String text; - private int value; - private StaticState staticState; - - private StaticText() { - } - - public StaticText(String text, int value, StaticState staticState) { - this.text = text; - this.value = value; - this.staticState = staticState; - } - - public String getText() { - return text; - } - - public int getValue() { - return value; - } - - public static Builder builder() { - return new Builder(); - } - - public StaticState getStaticState() { - return staticState; - } - - public String getValue(OfflinePlayer player) { - String parsed = PlaceholderAPI.setPlaceholders(player, text); - int parsedWidth = FontUtils.getTextWidth(parsed); - switch (staticState) { - case LEFT -> { - return parsed + FontUtils.surroundNameplateFont(OffsetFont.getOffsetChars(value - parsedWidth)); - } - case RIGHT -> { - return FontUtils.surroundNameplateFont(OffsetFont.getOffsetChars(value - parsedWidth)) + parsed; - } - case MIDDLE -> { - int half = (value - parsedWidth) / 2; - String left = FontUtils.surroundNameplateFont(OffsetFont.getOffsetChars(half)); - String right = FontUtils.surroundNameplateFont(OffsetFont.getOffsetChars(value - parsedWidth - half)); - return left + parsed + right; - } - default -> { - return ""; - } - } - } - - public static class Builder { - - private final StaticText text; - - public Builder() { - this.text = new StaticText(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder value(int value) { - text.value = value; - return this; - } - - public Builder text(String value) { - text.text = value; - return this; - } - - public Builder state(StaticState state) { - text.staticState = state; - return this; - } - - public StaticText build() { - return text; - } - } - - public enum StaticState { - LEFT, - MIDDLE, - RIGHT - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/SwitchText.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/SwitchText.java deleted file mode 100644 index 7e6f6fc..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/SwitchText.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -import java.util.HashMap; - -public class SwitchText { - - private HashMap valueMap; - private String toParse; - private String defaultValue; - - public static Builder builder() { - return new Builder(); - } - - private SwitchText() { - - } - - public SwitchText(HashMap valueMap, String toParse) { - this.valueMap = valueMap; - this.toParse = toParse; - } - - public String getValue(OfflinePlayer player) { - String parsed = PlaceholderAPI.setPlaceholders(player, toParse); - return PlaceholderAPI.setPlaceholders(player, valueMap.getOrDefault(parsed, defaultValue)); - } - - public static class Builder { - - private final SwitchText switchText; - - public Builder() { - this.switchText = new SwitchText(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder toParse(String toParse) { - this.switchText.toParse = toParse; - return this; - } - - public Builder defaultValue(String value) { - this.switchText.defaultValue = value; - return this; - } - - public Builder valueMap(HashMap valueMap) { - this.switchText.valueMap = valueMap; - return this; - } - - public SwitchText build() { - return switchText; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/VanillaHud.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/VanillaHud.java deleted file mode 100644 index b18f70e..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/placeholder/VanillaHud.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.util.FontUtils; -import net.momirealms.customnameplates.api.util.LogUtils; -import org.bukkit.OfflinePlayer; - -public class VanillaHud { - - private String empty; - private String half; - private String full; - private String maxPapi; - private String currentPapi; - private boolean reverse; - - private VanillaHud() { - } - - public VanillaHud(char empty, char half, char full, String maxPapi, String currentPapi, boolean reverse) { - this.empty = String.valueOf(empty) + OffsetFont.NEG_2.getCharacter(); - this.half = String.valueOf(half) + OffsetFont.NEG_2.getCharacter(); - this.full = String.valueOf(full) + OffsetFont.NEG_2.getCharacter(); - this.maxPapi = maxPapi; - this.currentPapi = currentPapi; - this.reverse = reverse; - } - - public static Builder builder() { - return new Builder(); - } - - public String getValue(OfflinePlayer player) { - double current; - double max; - try { - current= Double.parseDouble(PlaceholderAPI.setPlaceholders(player, currentPapi)); - max = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, maxPapi)); - } catch (NumberFormatException e) { - current = 1; - max = 1; - LogUtils.warn("Invalid number format when parsing: " + currentPapi + "/" + maxPapi); - } - if (current >= max) current = max; - if (current < 0) current = 0; - int point = (int) ((current / max) * 20); - int full_amount = point / 2; - int half_amount = point % 2; - int empty_amount = 10 - full_amount - half_amount; - StringBuilder builder = new StringBuilder(); - if (reverse) { - builder - .append(String.valueOf(empty).repeat(empty_amount)) - .append(String.valueOf(half).repeat(half_amount)) - .append(String.valueOf(full).repeat(full_amount)); - } else { - builder - .append(String.valueOf(full).repeat(full_amount)) - .append(String.valueOf(half).repeat(half_amount)) - .append(String.valueOf(empty).repeat(empty_amount)); - } - return FontUtils.surroundNameplateFont(builder.toString()); - } - - public static class Builder{ - - private final VanillaHud hud; - - public Builder() { - hud = new VanillaHud(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder full(char full) { - hud.full = String.valueOf(full) + OffsetFont.NEG_2.getCharacter(); - return this; - } - - public Builder half(char half) { - hud.half = String.valueOf(half) + OffsetFont.NEG_2.getCharacter(); - return this; - } - - public Builder empty(char empty) { - hud.empty = String.valueOf(empty) + OffsetFont.NEG_2.getCharacter(); - return this; - } - - public Builder reverse(boolean reverse) { - hud.reverse = reverse; - return this; - } - - public Builder max(String max) { - hud.maxPapi = max; - return this; - } - - public Builder current(String current) { - hud.currentPapi = current; - return this; - } - - public VanillaHud build() { - return hud; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/team/TeamTagPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/team/TeamTagPlayer.java deleted file mode 100644 index 13a87f3..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/team/TeamTagPlayer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.tag.team; - -import net.momirealms.customnameplates.api.mechanic.misc.ViewerText; -import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer; - -public interface TeamTagPlayer extends NameplatePlayer { - - /** - * Set a player's prefix - * - * @param prefix prefix - */ - void setPrefix(String prefix); - - /** - * Set a player's suffix - * - * @param suffix suffix - */ - void setSuffix(String suffix); - - /** - * Get a player's prefix - * - * @return prefix - */ - ViewerText getPrefix(); - - /** - * Get a player's suffix - * - * @return suffix - */ - ViewerText getSuffix(); - - /** - * Destroy the tag - */ - void destroy(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/DynamicTextEntity.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/DynamicTextEntity.java deleted file mode 100644 index 8d16841..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/DynamicTextEntity.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; - -import net.momirealms.customnameplates.api.mechanic.misc.ViewerText; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; - -import java.util.UUID; - -public interface DynamicTextEntity { - - boolean canSee(Player viewer); - - void timer(); - - boolean canShow(); - - boolean isShownTo(Player viewer); - - void spawn(Player viewer, Pose pose); - - void spawn(Pose pose); - - void destroy(); - - void destroy(Player viewer); - - void teleport(double x, double y, double z, boolean onGround); - - void teleport(); - - void teleport(Player viewer, double x, double y, double z, boolean onGround); - - void setSneak(boolean sneaking, boolean respawn); - - void updateVisibility(); - - void removePlayerFromViewers(Player player); - - void addPlayerToViewers(Player player); - - double getOffset(); - - void setOffset(double v); - - ViewerText getViewerText(); - - int getEntityId(); - - void move(short x, short y, short z, boolean onGround); - - void move(Player viewer, short x, short y, short z, boolean onGround); - - void respawn(Player viewer, Pose pose); - - void respawn(Pose pose); - - void updateText(); - - void updateText(Player viewer); - - UUID getUUID(); - - void handlePose(Pose previous, Pose pose); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/DynamicTextTagSetting.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/DynamicTextTagSetting.java deleted file mode 100644 index d5f46c8..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/DynamicTextTagSetting.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; - -import net.momirealms.customnameplates.api.requirement.Requirement; - -public class DynamicTextTagSetting { - - private double verticalOffset; - private String rawText; - private int checkFrequency; - private int refreshFrequency; - private Requirement[] viewerRequirements; - private Requirement[] ownerRequirements; - - private DynamicTextTagSetting() { - verticalOffset = 0; - rawText = ""; - checkFrequency = 10; - refreshFrequency = 10; - viewerRequirements = new Requirement[0]; - ownerRequirements = new Requirement[0]; - } - - public DynamicTextTagSetting(double verticalOffset, String rawText, int checkFrequency, int refreshFrequency, Requirement[] viewerRequirements, Requirement[] ownerRequirements) { - this.verticalOffset = verticalOffset; - this.rawText = rawText; - this.checkFrequency = checkFrequency; - this.refreshFrequency = refreshFrequency; - this.viewerRequirements = viewerRequirements; - this.ownerRequirements = ownerRequirements; - } - - public static Builder builder() { - return new Builder(); - } - - public double getVerticalOffset() { - return verticalOffset; - } - - public String getRawText() { - return rawText; - } - - public int getCheckFrequency() { - return checkFrequency; - } - - public int getRefreshFrequency() { - return refreshFrequency; - } - - public Requirement[] getViewerRequirements() { - return viewerRequirements; - } - - public Requirement[] getOwnerRequirements() { - return ownerRequirements; - } - - public static class Builder { - - private final DynamicTextTagSetting setting; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.setting = new DynamicTextTagSetting(); - } - - public Builder checkFrequency(int checkFrequency) { - this.setting.checkFrequency = checkFrequency; - return this; - } - - public Builder refreshFrequency(int refreshFrequency) { - this.setting.refreshFrequency = refreshFrequency; - return this; - } - - public Builder rawText(String rawText) { - this.setting.rawText = rawText; - return this; - } - - public Builder verticalOffset(double verticalOffset) { - this.setting.verticalOffset = verticalOffset; - return this; - } - - public Builder ownerRequirements(Requirement[] ownerRequirements) { - this.setting.ownerRequirements = ownerRequirements; - return this; - } - - public Builder viewerRequirements(Requirement[] viewerRequirements) { - this.setting.viewerRequirements = viewerRequirements; - return this; - } - - public DynamicTextTagSetting build() { - return setting; - } - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/EntityTagPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/EntityTagPlayer.java deleted file mode 100644 index 1b9f401..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/EntityTagPlayer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; - -import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer; - -import java.util.Collection; - -public interface EntityTagPlayer extends NameplatePlayer { - - void addTag(DynamicTextEntity tag); - - void addTag(StaticTextEntity tag); - - DynamicTextEntity addTag(DynamicTextTagSetting setting); - - StaticTextEntity addTag(StaticTextTagSetting setting); - - void removeTag(DynamicTextEntity tag); - - void removeTag(StaticTextEntity tag); - - Collection getDynamicTags(); - - Collection getStaticTags(); - - void setHatOffset(double hatOffset); - - double getHatOffset(); - - void destroy(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/StaticTextEntity.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/StaticTextEntity.java deleted file mode 100644 index a750214..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/StaticTextEntity.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; - -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; - -import java.util.UUID; - -public interface StaticTextEntity { - - String getID(); - - boolean isShownTo(Player viewer); - - NearbyRule getComeRule(); - - NearbyRule getLeaveRule(); - - void removePlayerFromViewers(Player player); - - void addPlayerToViewers(Player player); - - double getOffset(); - - void setOffset(double v); - - void destroy(Player viewer); - - void teleport(double x, double y, double z, boolean onGround); - - void teleport(Player viewer, double x, double y, double z, boolean onGround); - - void teleport(); - - int getEntityId(); - - void setText(String text); - - String getText(Player viewer); - - void setText(Player viewer, String text); - - void removeText(Player viewer); - - void spawn(Player viewer, Pose pose); - - void destroy(); - - void move(short x, short y, short z, boolean onGround); - - void move(Player viewer, short x, short y, short z, boolean onGround); - - void respawn(Player viewer, Pose pose); - - void respawn(Pose pose); - - UUID getUUID(); - - void handlePose(Pose previous, Pose pose); - - void setSneak(boolean sneaking, boolean onGround); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/StaticTextTagSetting.java b/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/StaticTextTagSetting.java deleted file mode 100644 index 6d92ee3..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/StaticTextTagSetting.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; - -public class StaticTextTagSetting { - - private double verticalOffset; - - private NearbyRule comeRule; - private NearbyRule leaveRule; - private String defaultText; - private String id; - - public StaticTextTagSetting(double verticalOffset, NearbyRule comeRule, NearbyRule leaveRule, String defaultText, String id) { - this.verticalOffset = verticalOffset; - this.comeRule = comeRule; - this.leaveRule = leaveRule; - this.defaultText = defaultText; - this.id = id; - } - - private StaticTextTagSetting() { - verticalOffset = 0; - comeRule = (player, entity) -> true; - leaveRule = (player, entity) -> true; - defaultText = ""; - id = ""; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final StaticTextTagSetting setting; - - public static Builder of() { - return new Builder(); - } - - public Builder() { - this.setting = new StaticTextTagSetting(); - } - - public Builder verticalOffset(double verticalOffset) { - this.setting.verticalOffset = verticalOffset; - return this; - } - - public Builder comeRule(NearbyRule rule) { - this.setting.comeRule = rule; - return this; - } - - public Builder leaveRule(NearbyRule rule) { - this.setting.leaveRule = rule; - return this; - } - - public Builder defaultText(String defaultText) { - this.setting.defaultText = defaultText; - return this; - } - - public Builder id(String id) { - this.setting.id = id; - return this; - } - - public StaticTextTagSetting build() { - return setting; - } - } - - public double getVerticalOffset() { - return verticalOffset; - } - - public NearbyRule getComeRule() { - return comeRule; - } - - public String getDefaultText() { - return defaultText; - } - - public String getID() { - return id; - } - - public NearbyRule getLeaveRule() { - return leaveRule; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/network/PacketEvent.java b/api/src/main/java/net/momirealms/customnameplates/api/network/PacketEvent.java new file mode 100644 index 0000000..45963da --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/network/PacketEvent.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.network; + +import net.momirealms.customnameplates.common.event.Cancellable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class PacketEvent implements Cancellable { + + private boolean cancelled; + private List delayedTasks = null; + private final Object packet; + + public PacketEvent(Object packet) { + this.packet = packet; + } + + public Object getPacket() { + return packet; + } + + public void addDelayedTask(Runnable task) { + if (delayedTasks == null) { + delayedTasks = new ArrayList<>(); + } + delayedTasks.add(task); + } + + public List getDelayedTasks() { + return Optional.ofNullable(delayedTasks).orElse(Collections.emptyList()); + } + + @Override + public boolean cancelled() { + return cancelled; + } + + @Override + public void cancelled(boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/network/PacketSender.java b/api/src/main/java/net/momirealms/customnameplates/api/network/PacketSender.java new file mode 100644 index 0000000..4b335ca --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/network/PacketSender.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.network; + +import net.momirealms.customnameplates.api.CNPlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface PacketSender { + + void sendPacket(@NotNull CNPlayer player, Object packet); + + void sendPacket(@NotNull CNPlayer player, List packet); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/network/PipelineInjector.java b/api/src/main/java/net/momirealms/customnameplates/api/network/PipelineInjector.java new file mode 100644 index 0000000..d21cc59 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/network/PipelineInjector.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.network; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import net.momirealms.customnameplates.api.CNPlayer; + +public interface PipelineInjector { + + Channel getChannel(CNPlayer player); + + ChannelDuplexHandler createHandler(CNPlayer player); + + void inject(CNPlayer player); + + void uninject(CNPlayer player); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/network/Tracker.java b/api/src/main/java/net/momirealms/customnameplates/api/network/Tracker.java new file mode 100644 index 0000000..9694619 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/network/Tracker.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.network; + +import net.momirealms.customnameplates.api.CNPlayer; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class Tracker { + + private boolean isCrouching; + private double scale; + private final CNPlayer tracker; + + private final Set passengerIDs = Collections.synchronizedSet(new HashSet<>()); + + public Tracker(CNPlayer tracker) { + this.isCrouching = false; + this.scale = 1; + this.tracker = tracker; + } + + public Tracker(CNPlayer tracker, boolean isCrouching, double scale) { + this.isCrouching = isCrouching; + this.scale = scale; + this.tracker = tracker; + } + + public CNPlayer tracker() { + return tracker; + } + + public boolean isCrouching() { + return isCrouching; + } + + public void setCrouching(boolean crouching) { + isCrouching = crouching; + } + + public double getScale() { + return scale; + } + + public void setScale(double scale) { + this.scale = scale; + } + + public void addPassengerID(int passengerID) { + passengerIDs.add(passengerID); + } + + public void removePassengerID(int passengerID) { + passengerIDs.remove(passengerID); + } + + public Set getPassengerIDs() { + return passengerIDs; + } + + public boolean isEmpty() { + return passengerIDs.isEmpty(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/AbstractPlaceholder.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/AbstractPlaceholder.java new file mode 100644 index 0000000..48cb8ea --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/AbstractPlaceholder.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public abstract class AbstractPlaceholder implements Placeholder { + + protected String id; + protected int countId = PlaceholderCounter.getAndIncrease(); + protected int refreshInterval; + protected PlaceholderManager manager; + protected Set children = new HashSet<>(); + + protected AbstractPlaceholder(PlaceholderManager manager, String id, int refreshInterval) { + if (refreshInterval == 0) { + refreshInterval = -1; + } + if (!(id.startsWith("%") && id.endsWith("%"))) { + throw new IllegalArgumentException("placeholder must start and end with '%'"); + } + this.manager = manager; + this.id = id; + this.refreshInterval = refreshInterval; + } + + @Override + public int countId() { + return countId; + } + + @Override + public Set children() { + return children; + } + + @Override + public void addChild(Placeholder placeholder) { + children.add(placeholder); + } + + @Override + public void addChildren(Set placeholder) { + children.addAll(placeholder); + } + + @Override + public int refreshInterval() { + return refreshInterval; + } + + @Override + public String id() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbstractPlaceholder that = (AbstractPlaceholder) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/DummyPlaceholder.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/DummyPlaceholder.java new file mode 100644 index 0000000..7252c2b --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/DummyPlaceholder.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +import java.util.Objects; +import java.util.Set; + +public class DummyPlaceholder implements Placeholder { + + private final String id; + private final int counterId = PlaceholderCounter.getAndIncrease(); + + public DummyPlaceholder(String id) { + this.id = id; + } + + @Override + public void addChild(Placeholder placeholder) { + } + + @Override + public void addChildren(Set placeholder) { + } + + @Override + public Set children() { + return Set.of(); + } + + @Override + public int refreshInterval() { + return -1; + } + + @Override + public String id() { + return id; + } + + @Override + public int countId() { + return counterId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DummyPlaceholder that = (DummyPlaceholder) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/ResourcePackManager.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/Placeholder.java similarity index 65% rename from api/src/main/java/net/momirealms/customnameplates/api/manager/ResourcePackManager.java rename to api/src/main/java/net/momirealms/customnameplates/api/placeholder/Placeholder.java index 44f9e8e..e1b51e3 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/ResourcePackManager.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/Placeholder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,21 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.manager; +package net.momirealms.customnameplates.api.placeholder; -import java.io.File; +import java.util.Set; -public interface ResourcePackManager { +public interface Placeholder { - /** - * Generate the resource pack - */ - void generateResourcePack(); + void addChild(Placeholder placeholder); - /** - * Delete a directory - * - * @param file file - */ - void deleteDirectory(File file); + void addChildren(Set placeholder); + + Set children(); + + int refreshInterval(); + + String id(); + + int countId(); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderCounter.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderCounter.java new file mode 100644 index 0000000..afdc80b --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderCounter.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +public class PlaceholderCounter { + + private static int id = 1; + + public static int getAndIncrease() { + int i = id; + id++; + return i; + } + + public static void reset() { + id = 1; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java new file mode 100644 index 0000000..3a98398 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public interface PlaceholderManager extends Reloadable { + + void refreshPlaceholders(); + + int getRefreshInterval(String id); + + T registerPlaceholder(T placeholder); + + SharedPlaceholder registerSharedPlaceholder( + String id, + int refreshInterval, + Supplier contextSupplier + ); + + default SharedPlaceholder registerSharedPlaceholder( + String id, + Supplier contextSupplier + ) { + return registerSharedPlaceholder(id, getRefreshInterval(id), contextSupplier); + } + + PlayerPlaceholder registerPlayerPlaceholder( + String id, + int refreshInterval, + Function function + ); + + default PlayerPlaceholder registerPlayerPlaceholder( + String id, + Function function + ) { + return registerPlayerPlaceholder(id, getRefreshInterval(id), function); + } + + RelationalPlaceholder registerRelationalPlaceholder( + String id, + int refreshInterval, + BiFunction function + ); + + default RelationalPlaceholder registerRelationalPlaceholder( + String id, + BiFunction function + ) { + return registerRelationalPlaceholder(id, getRefreshInterval(id), function); + } + + Placeholder getPlaceholder(String id); + + Placeholder getRegisteredPlaceholder(String id); + + void unregisterPlaceholder(String id); + + default void unregisterPlaceholder(Placeholder placeholder) { + unregisterPlaceholder(placeholder.id()); + } + + List detectPlaceholders(String raw); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManagerImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManagerImpl.java new file mode 100644 index 0000000..c22f90e --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManagerImpl.java @@ -0,0 +1,700 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +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.MainTask; +import net.momirealms.customnameplates.api.feature.*; +import net.momirealms.customnameplates.api.feature.background.Background; +import net.momirealms.customnameplates.api.feature.bubble.Bubble; +import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig; +import net.momirealms.customnameplates.api.feature.image.Image; +import net.momirealms.customnameplates.api.feature.nameplate.Nameplate; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.api.placeholder.internal.*; +import net.momirealms.customnameplates.api.requirement.Requirement; +import net.momirealms.customnameplates.common.util.Pair; + +import java.io.File; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public class PlaceholderManagerImpl implements PlaceholderManager { + + private static final Pattern PATTERN = Pattern.compile("%([^%]*)%"); + + private final CustomNameplates plugin; + private final HashMap refreshIntervals = new HashMap<>(); + private final Map registeredPlaceholders = new HashMap<>(); + private final HashMap> childrenText = new HashMap<>(); + private final List delayedInitTexts = new ArrayList<>(); + private final HashMap nestedPlaceholders = new HashMap<>(); + + public PlaceholderManagerImpl(CustomNameplates plugin) { + this.plugin = plugin; + } + + @Override + public void load() { + YamlDocument document = ConfigManager.getMainConfig(); + Section section = document.getSection("other-settings.placeholder-refresh-interval"); + if (section != null) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Integer) { + refreshIntervals.put(id, (Integer) value); + } + } + } + + this.loadInternalPlaceholders(); + this.loadNestNameplatePlaceholders(); + this.loadCustomPlaceholders(); + + for (Map.Entry> entry : this.childrenText.entrySet()) { + Placeholder placeholder = entry.getKey(); + for (PreParsedDynamicText preParsedText : entry.getValue()) { + preParsedText.init(); + placeholder.addChildren(preParsedText.placeholders()); + } + this.nestedPlaceholders.put(placeholder.id().replace("%np_", "%nameplates_").replace("%rel_np_", "%rel_nameplates_"), placeholder); + } + this.childrenText.clear(); + + for (PreParsedDynamicText preParsedText : this.delayedInitTexts) { + preParsedText.init(); + } + this.delayedInitTexts.clear(); + } + + @Override + public void unload() { + this.refreshIntervals.clear(); + this.registeredPlaceholders.clear(); + this.delayedInitTexts.clear(); + this.childrenText.clear(); + this.nestedPlaceholders.clear(); + PlaceholderCounter.reset(); + } + + private void loadNestNameplatePlaceholders() { + PreParsedDynamicText nameTag = new PreParsedDynamicText(plugin.getNameplateManager().playerNameTag()); + Placeholder placeholder1 = this.registerPlayerPlaceholder("%np_tag-image%", (player -> { + String equippedNameplate = player.equippedNameplate(); + if (equippedNameplate.equals("none")) equippedNameplate = plugin.getNameplateManager().defaultNameplateId(); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(equippedNameplate); + if (nameplate == null) return ""; + String tag = nameTag.fastCreate(player).render(player); + float advance = plugin.getAdvanceManager().getLineAdvance(tag); + return AdventureHelper.surroundWithNameplatesFont(nameplate.createImage(advance, 1, 0)); + })); + Placeholder placeholder2 = this.registerRelationalPlaceholder("%rel_np_tag-image%", (p1, p2) -> { + String equippedNameplate = p1.equippedNameplate(); + if (equippedNameplate.equals("none")) equippedNameplate = plugin.getNameplateManager().defaultNameplateId(); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(equippedNameplate); + if (nameplate == null) return ""; + String tag = nameTag.fastCreate(p1).render(p2); + float advance = plugin.getAdvanceManager().getLineAdvance(tag); + return AdventureHelper.surroundWithNameplatesFont(nameplate.createImage(advance, 1, 0)); + }); + Placeholder placeholder3 = this.registerPlayerPlaceholder("%np_tag-text%", (player -> nameTag.fastCreate(player).render(player))); + Placeholder placeholder4 = this.registerRelationalPlaceholder("%rel_np_tag-text%", (p1, p2) -> nameTag.fastCreate(p1).render(p2)); + Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_tag%", (player -> { + String equippedNameplate = player.equippedNameplate(); + if (equippedNameplate.equals("none")) equippedNameplate = plugin.getNameplateManager().defaultNameplateId(); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(equippedNameplate); + String tag = nameTag.fastCreate(player).render(player); + if (nameplate == null) return tag; + float advance = plugin.getAdvanceManager().getLineAdvance(tag); + return AdventureHelper.surroundWithNameplatesFont(nameplate.createImagePrefix(advance, 1, 0)) + + tag + AdventureHelper.surroundWithNameplatesFont(nameplate.createImageSuffix(advance, 1, 0)); + })); + Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_tag%", (p1, p2) -> { + String equippedNameplate = p1.equippedNameplate(); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(equippedNameplate); + if (nameplate == null) return p1.name(); + String tag = nameTag.fastCreate(p1).render(p2); + float advance = plugin.getAdvanceManager().getLineAdvance(tag); + return AdventureHelper.surroundWithNameplatesFont(nameplate.createImagePrefix(advance, 1, 0)) + + tag + AdventureHelper.surroundWithNameplatesFont(nameplate.createImageSuffix(advance, 1, 0)); + }); + List list = List.of(nameTag); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + childrenText.put(placeholder4, list); + childrenText.put(placeholder5, list); + childrenText.put(placeholder6, list); + } + + private void loadInternalPlaceholders() { + this.registerPlayerPlaceholder("%player_name%", CNPlayer::name); + this.registerPlayerPlaceholder("%player_x%", (player -> String.valueOf((int) Math.floor(player.position().x())))); + this.registerPlayerPlaceholder("%player_y%", (player -> String.valueOf((int) Math.floor(player.position().y())))); + this.registerPlayerPlaceholder("%player_z%", (player -> String.valueOf((int) Math.floor(player.position().z())))); + this.registerPlayerPlaceholder("%player_world%", (CNPlayer::world)); + this.registerPlayerPlaceholder("%player_remaining_air%", (player -> String.valueOf(player.remainingAir()))); + for (int i = -256; i <= 256; i++) { + String characters = OffsetFont.createOffsets(i); + this.registerPlayerPlaceholder("%np_offset_" + i + "%", (p) -> AdventureHelper.surroundWithNameplatesFont(characters)); + } + this.registerPlayerPlaceholder("%np_equipped_nameplate%", CNPlayer::equippedNameplate); + this.registerPlayerPlaceholder("%np_equipped_bubble%", CNPlayer::equippedBubble); + this.registerPlayerPlaceholder("%np_equipped_nameplate-name%", (player) -> { + Nameplate nameplate = plugin.getNameplateManager().getNameplate(player.equippedNameplate()); + return Optional.ofNullable(nameplate).map(Nameplate::displayName).orElse(""); + }); + this.registerPlayerPlaceholder("%np_equipped_bubble-name%", (player) -> { + BubbleConfig bubble = plugin.getBubbleManager().getBubbleConfig(player.equippedBubble()); + return Optional.ofNullable(bubble).map(BubbleConfig::displayName).orElse(""); + }); + this.registerSharedPlaceholder("%shared_np_is-latest%", () -> String.valueOf(plugin.isUpToDate())); + this.registerPlayerPlaceholder("%np_is-latest%", (player) -> String.valueOf(plugin.isUpToDate())); + this.registerPlayerPlaceholder("%np_time%", (player) -> { + long time = player.playerTime() % 24_000; + String ap = time >= 6000 && time < 18000 ? " PM" : " AM"; + int hours = (int) (time / 1000) ; + int minutes = (int) ((time - hours * 1000 ) * 0.06); + hours += 6; + while (hours >= 12) hours -= 12; + if (minutes < 10) return hours + ":0" + minutes + ap; + else return hours + ":" + minutes + ap; + }); + this.registerPlayerPlaceholder("%np_actionbar%", (player) -> plugin.getActionBarManager().getExternalActionBar(player)); + for (Image image : plugin.getImageManager().getImages()) { + this.registerSharedPlaceholder("%shared_np_image_" + image.id() + "%", + () -> AdventureHelper.surroundWithNameplatesFont(String.valueOf(image.character().character()))); + this.registerPlayerPlaceholder("%np_image_" + image.id() + "%", + (player) -> AdventureHelper.surroundWithNameplatesFont(String.valueOf(image.character().character()))); + this.registerSharedPlaceholder("%shared_np_img_" + image.id() + "%", + () -> String.valueOf(image.character().character())); + this.registerPlayerPlaceholder("%np_img_" + image.id() + "%", + (player) -> String.valueOf(image.character().character())); + } + } + + private void loadCustomPlaceholders() { + plugin.getConfigManager().saveResource("configs" + File.separator + "custom-placeholders.yml"); + YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "custom-placeholders.yml")); + Section section1 = document.getSection("conditional-text"); + if (section1 != null) loadConditionTextSection(section1); + Section section2 = document.getSection("background-text"); + if (section2 != null) loadBackgroundTextSection(section2); + Section section3 = document.getSection("nameplate-text"); + if (section3 != null) loadNameplateTextSection(section3); + Section section4 = document.getSection("bubble-text"); + if (section4 != null) loadBubbleTextSection(section4); + Section section5 = document.getSection("switch-text"); + if (section5 != null) loadSwitchTextSection(section5); + Section section6 = document.getSection("vanilla-hud"); + if (section6 != null) loadVanillaHud(section6); + Section section7 = document.getSection("static-text"); + if (section7 != null) loadStaticText(section7); + Section section8 = document.getSection("shift-text"); + if (section8 != null) loadShiftText(section8); + } + + private void loadShiftText(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + String text = inner.getString("text"); + String font = inner.getString("font"); + ShiftText shiftText = new ShiftText(font, text); + Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_shift_" + id + "%", () -> { + String parsed = shiftText.getText().fastCreate(null).render(null); + return AdventureHelper.surroundWithMiniMessageFont(parsed, ConfigManager.namespace() + ":" + font); + }); + Placeholder placeholder2 = registerPlayerPlaceholder("%np_shift_" + id + "%", (player) -> { + String parsed = shiftText.getText().fastCreate(player).render(player); + return AdventureHelper.surroundWithMiniMessageFont(parsed, ConfigManager.namespace() + ":" + font); + }); + Placeholder placeholder3 = registerRelationalPlaceholder("%rel_np_shift_" + id + "%", (p1, p2) -> { + String parsed = shiftText.getText().fastCreate(p1).render(p2); + return AdventureHelper.surroundWithMiniMessageFont(parsed, ConfigManager.namespace() + ":" + font); + }); + List list = List.of(shiftText.getText()); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + } + } + } + + private void loadStaticText(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + StaticPosition position = StaticPosition.valueOf(inner.getString("position", "middle").toUpperCase(Locale.ENGLISH)); + int totalWidth = inner.getInt("value", 100); + StaticText staticText = new StaticText(totalWidth, position, inner.getString("text")); + Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_static_" + id + "%", staticText::create); + Placeholder placeholder2 = registerPlayerPlaceholder("%np_static_" + id + "%", staticText::create); + Placeholder placeholder3 = registerRelationalPlaceholder("%rel_np_static_" + id + "%", staticText::create); + List list = List.of(staticText.getText()); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + } + } + } + + private void loadVanillaHud(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + boolean reverse = inner.getBoolean("reverse", true); + Image empty = requireNonNull(plugin.getImageManager().getImage(inner.getString("images.empty")), "image.empty should not be null"); + Image half = requireNonNull(plugin.getImageManager().getImage(inner.getString("images.half")), "image.half should not be null"); + Image full = requireNonNull(plugin.getImageManager().getImage(inner.getString("images.full")), "image.full should not be null"); + String currentValue = section.getString("placeholder.value", "1"); + String maxValue = section.getString("placeholder.max-value", currentValue); + boolean removeShadow = section.getBoolean("remove-shadow", false); + VanillaHud vanillaHud = new VanillaHud(empty, half, full, reverse, currentValue, maxValue, removeShadow); + List list = List.of(vanillaHud.getCurrent(), vanillaHud.getMax()); + Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_vanilla_" + id + "%", vanillaHud::create); + Placeholder placeholder2 = registerPlayerPlaceholder("%np_vanilla_" + id + "%", vanillaHud::create); + Placeholder placeholder3 = registerRelationalPlaceholder("%np_vanilla_" + id + "%", vanillaHud::create); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + } + } + } + + private void loadSwitchTextSection(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + PreParsedDynamicText placeholderToSwitch = new PreParsedDynamicText(inner.getString("switch")); + PreParsedDynamicText defaultValue = new PreParsedDynamicText(inner.getString("default", "")); + ArrayList list = new ArrayList<>(); + list.add(placeholderToSwitch); + this.delayedInitTexts.add(defaultValue); + Map valueMap = new HashMap<>(); + Section results = inner.getSection("case"); + if (results != null) { + for (Map.Entry strEntry : results.getStringRouteMappedValues(false).entrySet()) { + if (strEntry.getValue() instanceof String string) { + PreParsedDynamicText preParsedDynamicText = new PreParsedDynamicText(string); + valueMap.put(strEntry.getKey(), preParsedDynamicText); + this.delayedInitTexts.add(preParsedDynamicText); + } + } + } + Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_switch_" + id + "%", () -> { + String value = placeholderToSwitch.fastCreate(null).render(null); + PreParsedDynamicText text = valueMap.getOrDefault(value, defaultValue); + return text.fastCreate(null).render(null); + }); + Placeholder placeholder2 = registerPlayerPlaceholder("%np_switch_" + id + "%", (player) -> { + String value = placeholderToSwitch.fastCreate(player).render(player); + PreParsedDynamicText text = valueMap.getOrDefault(value, defaultValue); + player.forceUpdate(text.placeholders(), Collections.emptySet()); + return text.fastCreate(player).render(player); + }); + Placeholder placeholder3 = registerRelationalPlaceholder("%rel_np_switch_" + id + "%", (p1, p2) -> { + String value = placeholderToSwitch.fastCreate(p1).render(p2); + PreParsedDynamicText text = valueMap.getOrDefault(value, defaultValue); + p1.forceUpdate(text.placeholders(), Set.of(p2)); + return text.fastCreate(p1).render(p2); + }); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + } + } + } + + private void loadBubbleTextSection(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + String bbID = inner.getString("bubble"); + Bubble bubble = plugin.getBubbleManager().getBubble(bbID); + if (bubble != null) { + AdaptiveImageText adaptiveImageText = AdaptiveImageText.create( + id, inner.getString("text"), bubble, inner.getBoolean("remove-shadow"), + inner.getInt("left-margin", 1), inner.getInt("right-margin", 1) + ); + List list = new ArrayList<>(); + list.add(adaptiveImageText.getPreParsedDynamicText()); + Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_bubble_" + id + "%", () -> adaptiveImageText.getFull(null, null)); + Placeholder placeholder2 = this.registerPlayerPlaceholder("%np_bubble_" + id + "%", (p) -> adaptiveImageText.getFull(p, p)); + Placeholder placeholder3 = this.registerRelationalPlaceholder("%rel_np_bubble_" + id + "%", adaptiveImageText::getFull); + Placeholder placeholder4 = this.registerSharedPlaceholder("%shared_np_bubble-text_" + id + "%", () -> adaptiveImageText.getText(null, null)); + Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_bubble-text_" + id + "%", (p) -> adaptiveImageText.getText(p, p)); + Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_bubble-text_" + id + "%", adaptiveImageText::getText); + Placeholder placeholder7 = this.registerSharedPlaceholder("%shared_np_bubble-image_" + id + "%", () -> adaptiveImageText.getImage(null, null)); + Placeholder placeholder8 = this.registerPlayerPlaceholder("%np_bubble-image_" + id + "%", (p) -> adaptiveImageText.getImage(p, p)); + Placeholder placeholder9 = this.registerRelationalPlaceholder("%rel_np_bubble-image_" + id + "%", adaptiveImageText::getImage); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + childrenText.put(placeholder4, list); + childrenText.put(placeholder5, list); + childrenText.put(placeholder6, list); + childrenText.put(placeholder7, list); + childrenText.put(placeholder8, list); + childrenText.put(placeholder9, list); + } else { + plugin.getPluginLogger().warn("Nameplate [" + bbID + "] not exists"); + } + } + } + } + + private void loadNameplateTextSection(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + String npID = inner.getString("nameplate"); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(npID); + if (nameplate != null) { + AdaptiveImageText adaptiveImageText = AdaptiveImageText.create( + id, inner.getString("text"), nameplate, inner.getBoolean("remove-shadow"), + inner.getInt("left-margin", 1), inner.getInt("right-margin", 1) + ); + List list = new ArrayList<>(); + list.add(adaptiveImageText.getPreParsedDynamicText()); + Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_nameplate_" + id + "%", () -> adaptiveImageText.getFull(null, null)); + Placeholder placeholder2 = this.registerPlayerPlaceholder("%np_nameplate_" + id + "%", (p) -> adaptiveImageText.getFull(p, p)); + Placeholder placeholder3 = this.registerRelationalPlaceholder("%rel_np_nameplate_" + id + "%", adaptiveImageText::getFull); + Placeholder placeholder4 = this.registerSharedPlaceholder("%shared_np_nameplate-text_" + id + "%", () -> adaptiveImageText.getText(null, null)); + Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_nameplate-text_" + id + "%", (p) -> adaptiveImageText.getText(p, p)); + Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_nameplate-text_" + id + "%", adaptiveImageText::getText); + Placeholder placeholder7 = this.registerSharedPlaceholder("%shared_np_nameplate-image_" + id + "%", () -> adaptiveImageText.getImage(null, null)); + Placeholder placeholder8 = this.registerPlayerPlaceholder("%np_nameplate-image_" + id + "%", (p) -> adaptiveImageText.getImage(p, p)); + Placeholder placeholder9 = this.registerRelationalPlaceholder("%rel_np_nameplate-image_" + id + "%", adaptiveImageText::getImage); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + childrenText.put(placeholder4, list); + childrenText.put(placeholder5, list); + childrenText.put(placeholder6, list); + childrenText.put(placeholder7, list); + childrenText.put(placeholder8, list); + childrenText.put(placeholder9, list); + } else { + plugin.getPluginLogger().warn("Nameplate [" + npID + "] not exists"); + } + } + } + } + + private void loadBackgroundTextSection(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section inner) { + String bgID = inner.getString("background"); + Background background = plugin.getBackgroundManager().getBackground(bgID); + if (background != null) { + AdaptiveImageText adaptiveImageText = AdaptiveImageText.create( + id, inner.getString("text"), background, inner.getBoolean("remove-shadow"), + inner.getInt("left-margin", 2), inner.getInt("right-margin", 0) + ); + List list = new ArrayList<>(); + list.add(adaptiveImageText.getPreParsedDynamicText()); + Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_background_" + id + "%", () -> adaptiveImageText.getFull(null, null)); + Placeholder placeholder2 = this.registerPlayerPlaceholder("%np_background_" + id + "%", (p) -> adaptiveImageText.getFull(p, p)); + Placeholder placeholder3 = this.registerRelationalPlaceholder("%rel_np_background_" + id + "%", adaptiveImageText::getFull); + Placeholder placeholder4 = this.registerSharedPlaceholder("%shared_np_background-text_" + id + "%", () -> adaptiveImageText.getText(null, null)); + Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_background-text_" + id + "%", (p) -> adaptiveImageText.getText(p, p)); + Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_background-text_" + id + "%", adaptiveImageText::getText); + Placeholder placeholder7 = this.registerSharedPlaceholder("%shared_np_background-image_" + id + "%", () -> adaptiveImageText.getImage(null, null)); + Placeholder placeholder8 = this.registerPlayerPlaceholder("%np_background-image_" + id + "%", (p) -> adaptiveImageText.getImage(p, p)); + Placeholder placeholder9 = this.registerRelationalPlaceholder("%rel_np_background-image_" + id + "%", adaptiveImageText::getImage); + childrenText.put(placeholder1, list); + childrenText.put(placeholder2, list); + childrenText.put(placeholder3, list); + childrenText.put(placeholder4, list); + childrenText.put(placeholder5, list); + childrenText.put(placeholder6, list); + childrenText.put(placeholder7, list); + childrenText.put(placeholder8, list); + childrenText.put(placeholder9, list); + } else { + plugin.getPluginLogger().warn("Background [" + bgID + "] not exists"); + } + } + } + } + + private void loadConditionTextSection(Section section) { + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String id = entry.getKey(); + if (entry.getValue() instanceof Section placeholderSection) { + ArrayList> orderedTexts = new ArrayList<>(); + for (Map.Entry conditionEntry : placeholderSection.getStringRouteMappedValues(false).entrySet()) { + if (conditionEntry.getValue() instanceof Section inner) { + String text = inner.getString("text"); + Requirement[] requirements = plugin.getRequirementManager().parseRequirements(inner.getSection("conditions")); + PreParsedDynamicText preParsedDynamicText = new PreParsedDynamicText(text); + orderedTexts.add(Pair.of(preParsedDynamicText, requirements)); + this.delayedInitTexts.add(preParsedDynamicText); + } + } + this.registerSharedPlaceholder("%shared_np_conditional_" + id + "%", () -> { + outer: + for (Pair orderedText : orderedTexts) { + for (Requirement requirement : orderedText.right()) { + if (!requirement.isSatisfied(null, null)) { + continue outer; + } + } + return orderedText.left().fastCreate(null).render(null); + } + return ""; + }); + this.registerPlayerPlaceholder("%np_conditional_" + id + "%", (player) -> { + for (Pair orderedText : orderedTexts) { + if (!player.isMet(orderedText.right())) { + continue; + } + PreParsedDynamicText text = orderedText.left(); + player.forceUpdate(text.placeholders(), Collections.emptySet()); + return text.fastCreate(player).render(player); + } + return ""; + }); + this.registerRelationalPlaceholder("%rel_np_conditional_" + id + "%", (p1, p2) -> { + for (Pair orderedText : orderedTexts) { + if (!p1.isMet(p2, orderedText.right())) { + continue; + } + PreParsedDynamicText text = orderedText.left(); + p1.forceUpdate(text.placeholders(), Set.of(p2)); + return text.fastCreate(p1).render(p2); + } + return ""; + }); + } + } + } + + @Override + public void refreshPlaceholders() { + for (CNPlayer player : plugin.getOnlinePlayers()) { + if (!player.isOnline()) continue; + Set featuresToNotifyUpdates = new HashSet<>(); + Map> relationalFeaturesToNotifyUpdates = new HashMap<>(); + List placeholdersToUpdate = player.getRefreshValueTask(); + List delayedPlaceholdersToUpdate = new ArrayList<>(); + for (Placeholder placeholder : placeholdersToUpdate) { + if (placeholder instanceof PlayerPlaceholder playerPlaceholder) { + TickStampData previous = player.getValue(placeholder); + if (previous == null) { + String value = playerPlaceholder.request(player); + player.setValue(placeholder, new TickStampData<>(value, MainTask.getTicks(), true)); + featuresToNotifyUpdates.addAll(player.getUsedFeatures(placeholder)); + } else { + if (previous.ticks() == MainTask.getTicks()) { + if (previous.hasValueChanged()) { + featuresToNotifyUpdates.addAll(player.getUsedFeatures(placeholder)); + } + continue; + } + String value = playerPlaceholder.request(player); + if (!previous.data().equals(value)) { + previous.data(value); + previous.updateTicks(true); + featuresToNotifyUpdates.addAll(player.getUsedFeatures(placeholder)); + } else { + previous.updateTicks(false); + } + } + } else if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) { + delayedPlaceholdersToUpdate.add(relationalPlaceholder); + } else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) { + TickStampData previous = player.getValue(placeholder); + if (previous == null) { + String value; + // if the shared placeholder has been updated by other players + if (MainTask.hasRequested(sharedPlaceholder.countId())) { + value = sharedPlaceholder.getLatestValue(); + } else { + value = sharedPlaceholder.request(); + } + player.setValue(placeholder, new TickStampData<>(value, MainTask.getTicks(), true)); + featuresToNotifyUpdates.addAll(player.getUsedFeatures(placeholder)); + } else { + // The placeholder has been refreshed by other codes + if (previous.ticks() == MainTask.getTicks()) { + if (previous.hasValueChanged()) { + featuresToNotifyUpdates.addAll(player.getUsedFeatures(placeholder)); + } + continue; + } + String value; + // if the shared placeholder has been updated by other players + if (MainTask.hasRequested(sharedPlaceholder.countId())) { + value = sharedPlaceholder.getLatestValue(); + } else { + value = sharedPlaceholder.request(); + } + if (!previous.data().equals(value)) { + previous.data(value); + previous.updateTicks(true); + featuresToNotifyUpdates.addAll(player.getUsedFeatures(placeholder)); + } else { + previous.updateTicks(false); + } + } + } + } + + for (RelationalPlaceholder placeholder : delayedPlaceholdersToUpdate) { + for (CNPlayer nearby : player.nearbyPlayers()) { + TickStampData previous = player.getRelationalValue(placeholder, nearby); + if (previous == null) { + String value = placeholder.request(player, nearby); + player.setRelationalValue(placeholder, nearby, new TickStampData<>(value, MainTask.getTicks(), true)); + for (Feature feature : player.getUsedFeatures(placeholder)) { + // Filter features that will not be updated for all players + if (!featuresToNotifyUpdates.contains(feature)) { + List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); + players.add(nearby); + } + } + } else { + if (previous.ticks() == MainTask.getTicks()) { + if (previous.hasValueChanged()) { + for (Feature feature : player.getUsedFeatures(placeholder)) { + // Filter features that will not be updated for all players + if (!featuresToNotifyUpdates.contains(feature)) { + List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); + players.add(nearby); + } + } + } + continue; + } + String value = placeholder.request(player, nearby); + if (!previous.data().equals(value)) { + previous.data(value); + previous.updateTicks(true); + for (Feature feature : player.getUsedFeatures(placeholder)) { + // Filter features that will not be updated for all players + if (!featuresToNotifyUpdates.contains(feature)) { + List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); + players.add(nearby); + } + } + } else { + previous.updateTicks(false); + } + } + } + } + + // Switch to another thread for updating + plugin.getScheduler().async().execute(() -> { + // Async task takes time and the player might have been offline + if (!player.isOnline()) return; + for (Feature feature : featuresToNotifyUpdates) { + feature.notifyPlaceholderUpdates(player, false); + } + for (Map.Entry> innerEntry : relationalFeaturesToNotifyUpdates.entrySet()) { + Feature feature = innerEntry.getKey(); + if (feature instanceof RelationalFeature relationalFeature) { + for (CNPlayer other : innerEntry.getValue()) { + relationalFeature.notifyPlaceholderUpdates(player, other, false); + } + } + } + }); + } + } + + @Override + public int getRefreshInterval(String id) { + return refreshIntervals.getOrDefault(id, ConfigManager.defaultRefreshInterval()); + } + + @Override + public T registerPlaceholder(T placeholder) { + Placeholder nested = nestedPlaceholders.get(placeholder.id()); + if (nested != null) { + placeholder.addChildren(nested.children()); + } + registeredPlaceholders.put(placeholder.id(), placeholder); + return placeholder; + } + + @Override + public PlayerPlaceholder registerPlayerPlaceholder(String id, int refreshInterval, Function function) { + PlayerPlaceholderImpl impl = new PlayerPlaceholderImpl(this, id, refreshInterval, function); + return registerPlaceholder(impl); + } + + @Override + public RelationalPlaceholder registerRelationalPlaceholder(String id, int refreshInterval, BiFunction function) { + RelationalPlaceholderImpl impl = new RelationalPlaceholderImpl(this, id, refreshInterval, function); + return registerPlaceholder(impl); + } + + @Override + public SharedPlaceholder registerSharedPlaceholder(String id, int refreshInterval, Supplier contextSupplier) { + SharedPlaceholderImpl impl = new SharedPlaceholderImpl(this, id, refreshInterval, contextSupplier); + return registerPlaceholder(impl); + } + + @Override + public Placeholder getPlaceholder(String id) { + Placeholder placeholder = registeredPlaceholders.get(id); + if (placeholder != null) return placeholder; + return plugin.getPlatform().registerPlatformPlaceholder(id); + } + + @Override + public Placeholder getRegisteredPlaceholder(String id) { + return registeredPlaceholders.get(id); + } + + @Override + public void unregisterPlaceholder(String id) { + registeredPlaceholders.remove(id); + } + + @Override + public List detectPlaceholders(String text) { + int firstPercent = text.indexOf('%'); + if (firstPercent == -1) return Collections.emptyList(); + if (firstPercent == 0 && text.charAt(text.length() - 1) == '%' && text.indexOf('%', 1) == text.length() - 1) { + return Collections.singletonList(text); + } + List placeholders = new ArrayList<>(); + Matcher m = PATTERN.matcher(text); + while (m.find()) { + placeholders.add(m.group()); + } + return placeholders; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlayerPlaceholder.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlayerPlaceholder.java new file mode 100644 index 0000000..24b4c64 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlayerPlaceholder.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +import net.momirealms.customnameplates.api.CNPlayer; + +public interface PlayerPlaceholder extends Placeholder { + + String request(CNPlayer player); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlayerPlaceholderImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlayerPlaceholderImpl.java new file mode 100644 index 0000000..908eb2d --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlayerPlaceholderImpl.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +import net.momirealms.customnameplates.api.CNPlayer; + +import java.util.function.Function; + +public class PlayerPlaceholderImpl extends AbstractPlaceholder implements PlayerPlaceholder { + + private final Function function; + + protected PlayerPlaceholderImpl(PlaceholderManager manager, String id, int refreshInterval, Function function) { + super(manager, id, refreshInterval); + this.function = function; + } + + @Override + public String request(CNPlayer player) { + return function.apply(player); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/NearbyRule.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/RelationalPlaceholder.java similarity index 72% rename from api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/NearbyRule.java rename to api/src/main/java/net/momirealms/customnameplates/api/placeholder/RelationalPlaceholder.java index 5107c66..83f47d9 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/tag/unlimited/NearbyRule.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/RelationalPlaceholder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,12 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.mechanic.tag.unlimited; +package net.momirealms.customnameplates.api.placeholder; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; +import net.momirealms.customnameplates.api.CNPlayer; -public interface NearbyRule { +public interface RelationalPlaceholder extends Placeholder { - boolean isPassed(Player viewer, Entity target); + String request(CNPlayer p1, CNPlayer p2); } diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/DefaultProvider.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/RelationalPlaceholderImpl.java similarity index 53% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/DefaultProvider.java rename to api/src/main/java/net/momirealms/customnameplates/api/placeholder/RelationalPlaceholderImpl.java index 7f068ec..faeba8f 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/DefaultProvider.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/RelationalPlaceholderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,22 +15,23 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.team.provider; +package net.momirealms.customnameplates.api.placeholder; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.scoreboard.Scoreboard; -import org.bukkit.scoreboard.Team; +import net.momirealms.customnameplates.api.CNPlayer; -public class DefaultProvider implements TeamProvider { +import java.util.function.BiFunction; + +public class RelationalPlaceholderImpl extends AbstractPlaceholder implements RelationalPlaceholder { + + private final BiFunction function; + + protected RelationalPlaceholderImpl(PlaceholderManager manager, String id, int refreshInterval, BiFunction function) { + super(manager, id, refreshInterval); + this.function = function; + } @Override - public String getTeam(Player player, Player ignore) { - Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); - Team team = scoreboard.getPlayerTeam(player); - if (team == null) { - return "CNP_" + player.getName(); - } - return team.getName(); + public String request(CNPlayer p1, CNPlayer p2) { + return function.apply(p1, p2); } } diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/TeamProvider.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/SharedPlaceholder.java similarity index 74% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/TeamProvider.java rename to api/src/main/java/net/momirealms/customnameplates/api/placeholder/SharedPlaceholder.java index 8cc5bae..99f6d84 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/TeamProvider.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/SharedPlaceholder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.team.provider; +package net.momirealms.customnameplates.api.placeholder; -import org.bukkit.entity.Player; +public interface SharedPlaceholder extends Placeholder { -public interface TeamProvider { + String request(); - String getTeam(Player player, Player viewer); + void update(); + + String getLatestValue(); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/SharedPlaceholderImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/SharedPlaceholderImpl.java new file mode 100644 index 0000000..948a1ca --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/SharedPlaceholderImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder; + +import java.util.function.Supplier; + +public class SharedPlaceholderImpl extends AbstractPlaceholder implements SharedPlaceholder { + + private final Supplier supplier; + private String latestValue; + + protected SharedPlaceholderImpl(PlaceholderManager manager, String id, int refreshInterval, Supplier supplier) { + super(manager, id, refreshInterval); + this.supplier = supplier; + } + + @Override + public String request() { + return supplier.get(); + } + + @Override + public void update() { + latestValue = request(); + } + + @Override + public String getLatestValue() { + return latestValue; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/AdaptiveImageText.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/AdaptiveImageText.java new file mode 100644 index 0000000..cb6d592 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/AdaptiveImageText.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder.internal; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.AdaptiveImage; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import org.jetbrains.annotations.NotNull; + +public class AdaptiveImageText { + + private final String id; + private final String text; + private final T t; + private final boolean removeShadow; + private final PreParsedDynamicText preParsedDynamicText; + private final int leftMargin; + private final int rightMargin; + + public AdaptiveImageText(String id, String text, T t, boolean removeShadow, int leftMargin, int rightMargin) { + this.text = text; + this.id = id; + this.t = t; + this.removeShadow = removeShadow; + this.preParsedDynamicText = new PreParsedDynamicText(text); + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + } + + public static AdaptiveImageText create(String id, final String text, final T t, final boolean removeShadow, int leftMargin, int rightMargin) { + return new AdaptiveImageText<>(id, text, t, removeShadow, leftMargin, rightMargin); + } + + public PreParsedDynamicText getPreParsedDynamicText() { + return preParsedDynamicText; + } + + public String getId() { + return id; + } + + public String getText() { + return text; + } + + @NotNull + public String getFull(CNPlayer p1, CNPlayer p2) { + String parsed = preParsedDynamicText.fastCreate(p1).render(p2); + if (parsed.isEmpty()) return ""; + float advance = CustomNameplates.getInstance().getAdvanceManager().getLineAdvance(parsed); + String prefix = t.createImagePrefix(advance, leftMargin, rightMargin); + String suffix = t.createImageSuffix(advance, leftMargin, rightMargin); + String prefixWithFont = AdventureHelper.surroundWithNameplatesFont(prefix); + String suffixWithFont = AdventureHelper.surroundWithNameplatesFont(suffix); + if (removeShadow) prefixWithFont = AdventureHelper.removeShadowTricky(prefixWithFont); + if (removeShadow) suffixWithFont = AdventureHelper.removeShadowTricky(suffixWithFont); + return prefixWithFont + parsed + suffixWithFont; + } + + @NotNull + public String getImage(CNPlayer p1, CNPlayer p2) { + String parsed = preParsedDynamicText.fastCreate(p1).render(p2); + if (parsed.isEmpty()) return ""; + float advance = CustomNameplates.getInstance().getAdvanceManager().getLineAdvance(parsed); + String image = t.createImage(advance, leftMargin, rightMargin); + String imageWithFont = AdventureHelper.surroundWithNameplatesFont(image); + if (removeShadow) imageWithFont = AdventureHelper.removeShadowTricky(imageWithFont); + return imageWithFont; + } + + @NotNull + public String getText(CNPlayer p1, CNPlayer p2) { + return preParsedDynamicText.fastCreate(p1).render(p2); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiEquals.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/ShiftText.java similarity index 55% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiEquals.java rename to api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/ShiftText.java index 89f71b1..856a7a7 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiEquals.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/ShiftText.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,18 +15,25 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; +package net.momirealms.customnameplates.api.placeholder.internal; -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; -import java.util.Objects; +public class ShiftText { -public record PapiEquals(String papi, String requirement) implements PapiRequirement{ + private final String font; + private final PreParsedDynamicText text; - @Override - public boolean isMet(OfflinePlayer player) { - String value = PlaceholderAPI.setPlaceholders(player, papi); - return Objects.equals(value, PlaceholderAPI.setPlaceholders(player, requirement)); + public ShiftText(String font, String text) { + this.font = font; + this.text = new PreParsedDynamicText(text); + } + + public String getFont() { + return font; + } + + public PreParsedDynamicText getText() { + return text; } } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/manager/BossBarManager.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/StaticPosition.java similarity index 80% rename from api/src/main/java/net/momirealms/customnameplates/api/manager/BossBarManager.java rename to api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/StaticPosition.java index 506c2d7..c6d00e0 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/manager/BossBarManager.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/StaticPosition.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,8 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.manager; +package net.momirealms.customnameplates.api.placeholder.internal; -public interface BossBarManager { +public enum StaticPosition { + LEFT, RIGHT, MIDDLE } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/StaticText.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/StaticText.java new file mode 100644 index 0000000..7cb9540 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/StaticText.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder.internal; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.OffsetFont; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.helper.AdventureHelper; + +public class StaticText { + + private final int width; + private final StaticPosition position; + private final PreParsedDynamicText text; + + public StaticText(int width, StaticPosition position, String text) { + this.width = width; + this.position = position; + this.text = new PreParsedDynamicText(text); + } + + public int getWidth() { + return width; + } + + public StaticPosition getPosition() { + return position; + } + + public PreParsedDynamicText getText() { + return text; + } + + public String create() { + return create(text.fastCreate(null).render(null)); + } + + public String create(CNPlayer player) { + return create(text.fastCreate(player).render(player)); + } + + public String create(CNPlayer p1, CNPlayer p2) { + return create(text.fastCreate(p1).render(p2)); + } + + public String create(String text) { + float parsedWidth = CustomNameplates.getInstance().getAdvanceManager().getLineAdvance(text); + switch (position) { + case LEFT -> { + return text + AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth)); + } + case RIGHT -> { + return AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth)) + text; + } + case MIDDLE -> { + int half = (int) ((width - parsedWidth) / 2); + String left = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(half)); + String right = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth - half)); + return left + text + right; + } + default -> { + return ""; + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/VanillaHud.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/VanillaHud.java new file mode 100644 index 0000000..06749a6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/internal/VanillaHud.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.placeholder.internal; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.OffsetFont; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.feature.image.Image; +import net.momirealms.customnameplates.api.helper.AdventureHelper; + +public class VanillaHud { + + private final String empty; + private final String half; + private final String full; + private final boolean reverse; + private final PreParsedDynamicText current; + private final PreParsedDynamicText max; + private final boolean removeShadow; + + public VanillaHud(Image empty, Image half, Image full, boolean reverse, String current, String max, boolean removeShadow) { + this.empty = String.valueOf(empty.character().character()) + OffsetFont.NEG_2.character(); + this.half = String.valueOf(half.character().character()) + OffsetFont.NEG_2.character(); + this.full = String.valueOf(full.character().character()) + OffsetFont.NEG_2.character(); + this.removeShadow = removeShadow; + this.reverse = reverse; + this.current = new PreParsedDynamicText(current); + this.max = new PreParsedDynamicText(max); + } + + public PreParsedDynamicText getCurrent() { + return current; + } + + public PreParsedDynamicText getMax() { + return max; + } + + public String create() { + return create(current.fastCreate(null).render(null), max.fastCreate(null).render(null)); + } + + public String create(CNPlayer player) { + return create(current.fastCreate(player).render(player), max.fastCreate(player).render(player)); + } + + public String create(CNPlayer p1, CNPlayer p2) { + return create(current.fastCreate(p1).render(p2), max.fastCreate(p1).render(p2)); + } + + private String create(String current, String max) { + double currentDouble; + double maxDouble; + try { + currentDouble= Double.parseDouble(current); + maxDouble = Double.parseDouble(max); + } catch (NumberFormatException e) { + currentDouble = 1; + maxDouble = 1; + CustomNameplates.getInstance().getPluginLogger().warn("Invalid number format when parsing: " + current + "/" + max); + } + if (currentDouble >= maxDouble) currentDouble = maxDouble; + if (currentDouble < 0) currentDouble = 0; + int point = (int) ((currentDouble / maxDouble) * 20); + int full_amount = point / 2; + int half_amount = point % 2; + int empty_amount = 10 - full_amount - half_amount; + StringBuilder builder = new StringBuilder(); + if (reverse) { + builder + .append(String.valueOf(empty).repeat(empty_amount)) + .append(String.valueOf(half).repeat(half_amount)) + .append(String.valueOf(full).repeat(full_amount)); + } else { + builder + .append(String.valueOf(full).repeat(full_amount)) + .append(String.valueOf(half).repeat(half_amount)) + .append(String.valueOf(empty).repeat(empty_amount)); + } + return removeShadow ? AdventureHelper.removeShadowTricky(AdventureHelper.surroundWithNameplatesFont(builder.toString())) : + AdventureHelper.surroundWithNameplatesFont(builder.toString()); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/AbstractRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/AbstractRequirement.java new file mode 100644 index 0000000..2a7b379 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/AbstractRequirement.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement; + +public abstract class AbstractRequirement implements Requirement { + + protected final int refreshInterval; + + public AbstractRequirement(int refreshInterval) { + this.refreshInterval = refreshInterval; + } + + @Override + public int refreshInterval() { + return refreshInterval; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/AbstractRequirementManager.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/AbstractRequirementManager.java new file mode 100644 index 0000000..59d90be --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/AbstractRequirementManager.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.requirement.builtin.*; +import net.momirealms.customnameplates.api.util.ConfigUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public abstract class AbstractRequirementManager implements RequirementManager { + + protected final CustomNameplates plugin; + private final HashMap requirementFactoryMap = new HashMap<>(); + + public AbstractRequirementManager(CustomNameplates plugin) { + this.plugin = plugin; + this.registerInternalRequirements(); + this.registerPlatformRequirements(); + } + + private void registerInternalRequirements() { + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("papi", ""), true); + return new NotInListRequirement(interval, dynamicText1, new HashSet<>(section.getStringList("values"))); + }, "!in-list"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("papi", ""), true); + return new InListRequirement(interval, dynamicText1, new HashSet<>(section.getStringList("values"))); + }, "in-list"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + String value2 = section.getString("value2", ""); + return new NotEndWithRequirement(interval, dynamicText1, value2); + }, "!ends-with", "!endsWith", "!end-with", "!endWith"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + String value2 = section.getString("value2", ""); + return new EndWithRequirement(interval, dynamicText1, value2); + }, "ends-with", "endsWith", "end-with", "endWith"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + String value2 = section.getString("value2", ""); + return new NotStartWithRequirement(interval, dynamicText1, value2); + }, "!starts-with", "!startsWith", "!start-with", "!startWith"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + String value2 = section.getString("value2", ""); + return new StartWithRequirement(interval, dynamicText1, value2); + }, "starts-with", "startsWith", "start-with", "startWith"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NotContainsRequirement(interval, dynamicText1, dynamicText2); + }, "!contains", "!contain"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new ContainsRequirement(interval, dynamicText1, dynamicText2); + }, "contains", "contain"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + String regex = section.getString("value2", ""); + return new RegexRequirement(interval, dynamicText1, regex); + }, "regex"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NumberLessRequirement(interval, dynamicText1, dynamicText2); + }, "<"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NumberNoGreaterRequirement(interval, dynamicText1, dynamicText2); + }, "<="); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NumberGreaterRequirement(interval, dynamicText1, dynamicText2); + }, ">"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NumberNoLessRequirement(interval, dynamicText1, dynamicText2); + }, ">="); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NumberEqualsRequirement(interval, dynamicText1, dynamicText2); + }, "="); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NumberNotEqualsRequirement(interval, dynamicText1, dynamicText2); + }, "!="); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new EqualsRequirement(interval, dynamicText1, dynamicText2); + }, "equals", "equal"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true); + PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true); + return new NotEqualsRequirement(interval, dynamicText1, dynamicText2); + }, "!equals", "!equal"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + Requirement[] requirements = parseRequirements(section); + return new OrRequirement(interval, requirements); + }, "||", "or"); + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + Requirement[] requirements = parseRequirements(section); + return new AndRequirement(interval, requirements); + }, "&&", "and"); + this.registerRequirement((args, interval) -> { + boolean has = (boolean) args; + return new HasNameplateRequirement(interval, has); + }, "has-nameplate"); + this.registerRequirement((args, interval) -> { + boolean has = (boolean) args; + return new HasBubbleRequirement(interval, has); + }, "has-bubble"); + } + + protected abstract void registerPlatformRequirements(); + + @Override + public void disable() { + this.requirementFactoryMap.clear(); + } + + @Override + public boolean registerRequirement(@NotNull RequirementFactory requirementFactory, @NotNull String... types) { + for (String type : types) { + if (this.requirementFactoryMap.containsKey(type)) return false; + } + for (String type : types) { + this.requirementFactoryMap.put(type, requirementFactory); + } + return true; + } + + @Override + public boolean unregisterRequirement(@NotNull String type) { + return this.requirementFactoryMap.remove(type) != null; + } + + @Override + public @Nullable RequirementFactory getRequirementFactory(@NotNull String type) { + return requirementFactoryMap.get(type); + } + + @Override + public boolean hasRequirement(@NotNull String type) { + return requirementFactoryMap.containsKey(type); + } + + @NotNull + @Override + public Requirement[] parseRequirements(Section section) { + List requirements = new ArrayList<>(); + if (section != null) + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + String typeOrName = entry.getKey(); + if (hasRequirement(typeOrName)) { + requirements.add(parseSimpleRequirement(typeOrName, entry.getValue())); + } else { + Section inner = section.getSection(typeOrName); + if (inner != null) { + requirements.add(parseRichRequirement(inner)); + } else { + plugin.getPluginLogger().warn("Invalid requirement type found at " + section.getRouteAsString() + "." + typeOrName); + } + } + } + return requirements.toArray(new Requirement[0]); + } + + @Override + public @NotNull Requirement parseRichRequirement(@Nullable Section section) { + if (section == null) { + return Requirement.empty(); + } + String type = section.getString("type"); + if (type == null) { + plugin.getPluginLogger().warn("No requirement type found at " + section.getRouteAsString()); + return Requirement.empty(); + } + var factory = getRequirementFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Requirement type: " + type + " not exists"); + return Requirement.empty(); + } + return factory.process(section.get("value"), section.getInt("refresh-interval", 10)); + } + + @Override + public @NotNull Requirement parseSimpleRequirement(@NotNull String type, @NotNull Object value) { + RequirementFactory factory = getRequirementFactory(type); + if (factory == null) { + plugin.getPluginLogger().warn("Requirement type: " + type + " doesn't exist."); + return Requirement.empty(); + } + return factory.process(value, 10); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/Condition.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/Condition.java deleted file mode 100644 index 788cdd7..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/requirement/Condition.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.requirement; - -import org.bukkit.OfflinePlayer; - -public class Condition { - - private OfflinePlayer player; - - public Condition() { - this.player = null; - } - - public Condition(OfflinePlayer player) { - this.player = player; - } - - public OfflinePlayer getOfflinePlayer() { - return player; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final Condition condition; - - public Builder() { - this.condition = new Condition(); - } - - public Builder player(OfflinePlayer player) { - condition.player = player; - return this; - } - - public Condition build() { - return condition; - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/EmptyRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/EmptyRequirement.java similarity index 56% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/EmptyRequirement.java rename to api/src/main/java/net/momirealms/customnameplates/api/requirement/EmptyRequirement.java index 0a7ea13..fca8eb7 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/EmptyRequirement.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/EmptyRequirement.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,20 +15,35 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.requirement; +package net.momirealms.customnameplates.api.requirement; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.requirement.Requirement; +import net.momirealms.customnameplates.api.CNPlayer; -/** - * Represents an empty requirement that always returns true when checking conditions. - */ public class EmptyRequirement implements Requirement { - public static EmptyRequirement instance = new EmptyRequirement(); + public static final EmptyRequirement INSTANCE = new EmptyRequirement(); + + public static Requirement instance() { + return INSTANCE; + } @Override - public boolean isConditionMet(Condition condition) { + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { return true; } + + @Override + public String type() { + return "empty"; + } + + @Override + public int refreshInterval() { + return -1; + } + + @Override + public int hashCode() { + return type().hashCode(); + } } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/Requirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/Requirement.java index 9424121..5a10a06 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/requirement/Requirement.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/Requirement.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,13 +17,19 @@ package net.momirealms.customnameplates.api.requirement; +import net.momirealms.customnameplates.api.CNPlayer; + public interface Requirement { - /** - * Is condition met the requirement - * - * @param condition condition - * @return meet or not - */ - boolean isConditionMet(Condition condition); -} + boolean isSatisfied(CNPlayer p1, CNPlayer p2); + + String type(); + + int refreshInterval(); + + int hashCode(); + + static Requirement empty() { + return EmptyRequirement.instance(); + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementExpansion.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementExpansion.java deleted file mode 100644 index df9ce14..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementExpansion.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.requirement; - -/** - * An abstract class representing a requirement expansion - * Requirement expansions are used to define custom requirements for various functionalities. - */ -public abstract class RequirementExpansion { - - /** - * Get the version of this requirement expansion. - * - * @return The version of the expansion. - */ - public abstract String getVersion(); - - /** - * Get the author of this requirement expansion. - * - * @return The author of the expansion. - */ - public abstract String getAuthor(); - - /** - * Get the type of requirement provided by this expansion. - * - * @return The type of requirement. - */ - public abstract String getRequirementType(); - - /** - * Get the factory for creating requirements defined by this expansion. - * - * @return The requirement factory. - */ - public abstract RequirementFactory getRequirementFactory(); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementFactory.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementFactory.java index 74b2840..2066941 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementFactory.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,15 +18,9 @@ package net.momirealms.customnameplates.api.requirement; /** - * An interface for a requirement factory that builds requirements. + * Interface representing a factory for creating requirements. */ public interface RequirementFactory { - /** - * Build a requirement with the given arguments. - * - * @param args The arguments used to build the requirement. - * @return The built requirement. - */ - Requirement build(Object args); + Requirement process(Object args, int refreshInterval); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementManager.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementManager.java new file mode 100644 index 0000000..2dddc70 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/RequirementManager.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface RequirementManager extends Reloadable { + + Requirement[] parseRequirements(Section section); + + boolean registerRequirement(@NotNull RequirementFactory requirementFactory, @NotNull String... types); + + boolean unregisterRequirement(@NotNull String type); + + @Nullable RequirementFactory getRequirementFactory(@NotNull String type); + + boolean hasRequirement(@NotNull String type); + + @NotNull Requirement parseRichRequirement(@NotNull Section section); + + @NotNull Requirement parseSimpleRequirement(@NotNull String type, @NotNull Object value); +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/AndRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/AndRequirement.java new file mode 100644 index 0000000..95ad58c --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/AndRequirement.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.api.requirement.Requirement; + +public class AndRequirement extends AbstractRequirement { + + private final Requirement[] requirements; + + public AndRequirement(int refreshInterval, Requirement[] requirements) { + super(refreshInterval); + this.requirements = requirements; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + for (Requirement requirement : requirements) { + if (!requirement.isSatisfied(p1, p2)) return false; + } + return true; + } + + @Override + public String type() { + return "&&"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + AndRequirement that = (AndRequirement) object; + int length = requirements.length; + if (that.requirements.length != length) + return false; + for (int i = 0; i < length; i++) { + Object e1 = requirements[i]; + Object e2 = that.requirements[i]; + if (e1 == e2) + continue; + if (!e1.equals(e2)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int hash = 281; + for (Requirement requirement : requirements) { + hash += requirement.hashCode(); + } + return hash; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/ContainsRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/ContainsRequirement.java new file mode 100644 index 0000000..9c8ac8a --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/ContainsRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class ContainsRequirement extends PlaceholdersRequirement { + + public ContainsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return a1.contains(a2); + } + + @Override + public String type() { + return "contains"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/EndWithRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/EndWithRequirement.java new file mode 100644 index 0000000..a4404ee --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/EndWithRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class EndWithRequirement extends PlaceholderRequirement { + + public EndWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return a1.endsWith(a2); + } + + @Override + public String type() { + return "ends-with"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/EqualsRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/EqualsRequirement.java new file mode 100644 index 0000000..8963cab --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/EqualsRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class EqualsRequirement extends PlaceholdersRequirement { + + public EqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return a1.equals(a2); + } + + @Override + public String type() { + return "equals"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/HasBubbleRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/HasBubbleRequirement.java new file mode 100644 index 0000000..7fdaad8 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/HasBubbleRequirement.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +public class HasBubbleRequirement extends AbstractRequirement { + + private final boolean has; + + public HasBubbleRequirement(int refreshInterval, boolean has) { + super(refreshInterval); + this.has = has; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String bubble = p1.equippedBubble(); + if (bubble.equals("none")) bubble = CustomNameplates.getInstance().getBubbleManager().defaultBubbleId(); + if (has) { + return !bubble.equals("none"); + } else { + return bubble.equals("none"); + } + } + + @Override + public String type() { + return "has-bubble"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + HasBubbleRequirement that = (HasBubbleRequirement) object; + return has == that.has; + } + + @Override + public int hashCode() { + return (has ? 781 : 3); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/HasNameplateRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/HasNameplateRequirement.java new file mode 100644 index 0000000..127dd3c --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/HasNameplateRequirement.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +public class HasNameplateRequirement extends AbstractRequirement { + + private final boolean has; + + public HasNameplateRequirement(int refreshInterval, boolean has) { + super(refreshInterval); + this.has = has; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String nameplate = p1.equippedNameplate(); + if (nameplate.equals("none")) nameplate = CustomNameplates.getInstance().getNameplateManager().defaultNameplateId(); + if (has) { + return !nameplate.equals("none"); + } else { + return nameplate.equals("none"); + } + } + + @Override + public String type() { + return "has-nameplate"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + HasNameplateRequirement that = (HasNameplateRequirement) object; + return has == that.has; + } + + @Override + public int hashCode() { + return (has ? 211 : 73); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/InListRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/InListRequirement.java new file mode 100644 index 0000000..44734cb --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/InListRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +import java.util.Set; + +public class InListRequirement extends PlaceholderRequirement> { + + public InListRequirement(int refreshInterval, PreParsedDynamicText text, Set any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, Set a2) { + return a2.contains(a1); + } + + @Override + public String type() { + return "in-list"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotContainsRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotContainsRequirement.java new file mode 100644 index 0000000..5ad6e9b --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotContainsRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NotContainsRequirement extends PlaceholdersRequirement { + + public NotContainsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return !a1.contains(a2); + } + + @Override + public String type() { + return "!contains"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotEndWithRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotEndWithRequirement.java new file mode 100644 index 0000000..aa59c6d --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotEndWithRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NotEndWithRequirement extends PlaceholderRequirement { + + public NotEndWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return !a1.endsWith(a2); + } + + @Override + public String type() { + return "!ends-with"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotEqualsRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotEqualsRequirement.java new file mode 100644 index 0000000..c61a68b --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotEqualsRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NotEqualsRequirement extends PlaceholdersRequirement { + + public NotEqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return !a1.equals(a2); + } + + @Override + public String type() { + return "!equals"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotInListRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotInListRequirement.java new file mode 100644 index 0000000..0427fa6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotInListRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +import java.util.Set; + +public class NotInListRequirement extends PlaceholderRequirement> { + + public NotInListRequirement(int refreshInterval, PreParsedDynamicText text, Set any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, Set a2) { + return !a2.contains(a1); + } + + @Override + public String type() { + return "!in-list"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotStartWithRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotStartWithRequirement.java new file mode 100644 index 0000000..1991647 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NotStartWithRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NotStartWithRequirement extends PlaceholderRequirement { + + public NotStartWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return !a1.startsWith(a2); + } + + @Override + public String type() { + return "!starts-with"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberEqualsRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberEqualsRequirement.java new file mode 100644 index 0000000..0a652ae --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberEqualsRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NumberEqualsRequirement extends PlaceholdersRequirement { + + public NumberEqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + double d1 = Double.parseDouble(a1); + double d2 = Double.parseDouble(a2); + return d1 == d2; + } + + @Override + public String type() { + return "="; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberGreaterRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberGreaterRequirement.java new file mode 100644 index 0000000..82f3365 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberGreaterRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NumberGreaterRequirement extends PlaceholdersRequirement { + + public NumberGreaterRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + double d1 = Double.parseDouble(a1); + double d2 = Double.parseDouble(a2); + return d1 > d2; + } + + @Override + public String type() { + return ">"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberLessRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberLessRequirement.java new file mode 100644 index 0000000..d16d1f5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberLessRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NumberLessRequirement extends PlaceholdersRequirement { + + public NumberLessRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + double d1 = Double.parseDouble(a1); + double d2 = Double.parseDouble(a2); + return d1 < d2; + } + + @Override + public String type() { + return "<"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNoGreaterRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNoGreaterRequirement.java new file mode 100644 index 0000000..26fd588 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNoGreaterRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NumberNoGreaterRequirement extends PlaceholdersRequirement { + + public NumberNoGreaterRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + double d1 = Double.parseDouble(a1); + double d2 = Double.parseDouble(a2); + return d1 <= d2; + } + + @Override + public String type() { + return "<="; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNoLessRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNoLessRequirement.java new file mode 100644 index 0000000..f173a04 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNoLessRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NumberNoLessRequirement extends PlaceholdersRequirement { + + public NumberNoLessRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + double d1 = Double.parseDouble(a1); + double d2 = Double.parseDouble(a2); + return d1 >= d2; + } + + @Override + public String type() { + return ">="; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNotEqualsRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNotEqualsRequirement.java new file mode 100644 index 0000000..7d39d9a --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/NumberNotEqualsRequirement.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class NumberNotEqualsRequirement extends PlaceholdersRequirement { + + public NumberNotEqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval, t1, t2); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + double d1 = Double.parseDouble(a1); + double d2 = Double.parseDouble(a2); + return d1 != d2; + } + + @Override + public String type() { + return "!="; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/OrRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/OrRequirement.java new file mode 100644 index 0000000..51eed47 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/OrRequirement.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.api.requirement.Requirement; + +public class OrRequirement extends AbstractRequirement { + + private final Requirement[] requirements; + + public OrRequirement(int refreshInterval, Requirement[] requirements) { + super(refreshInterval); + this.requirements = requirements; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + if (requirements.length == 0) return true; + for (Requirement requirement : requirements) { + if (requirement.isSatisfied(p1, p2)) return true; + } + return false; + } + + @Override + public String type() { + return "||"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + OrRequirement that = (OrRequirement) object; + int length = requirements.length; + if (that.requirements.length != length) + return false; + for (int i = 0; i < length; i++) { + Object e1 = requirements[i]; + Object e2 = that.requirements[i]; + if (e1 == e2) + continue; + if (!e1.equals(e2)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int hash = 271; + for (Requirement requirement : requirements) { + hash += requirement.hashCode(); + } + return hash; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/PlaceholderRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/PlaceholderRequirement.java new file mode 100644 index 0000000..ed82e25 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/PlaceholderRequirement.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +import java.util.Objects; +import java.util.Set; + +public abstract class PlaceholderRequirement extends AbstractRequirement { + + private final PreParsedDynamicText text; + private final T any; + + public PlaceholderRequirement(int refreshInterval, PreParsedDynamicText text, T any) { + super(refreshInterval); + this.text = text; + this.any = any; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + p1.forceUpdate(text.placeholders(), Set.of(p2)); + String a1 = text.fastCreate(p1).render(p2); + return checkArgument(a1, any); + } + + protected abstract boolean checkArgument(String a1, T a2); + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + PlaceholderRequirement that = (PlaceholderRequirement) object; + return Objects.equals(text, that.text) && Objects.equals(any, that.any); + } + + @Override + public int hashCode() { + return 283 + text.hashCode() * 53 + any.hashCode() * 457 + type().hashCode() * 3; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/PlaceholdersRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/PlaceholdersRequirement.java new file mode 100644 index 0000000..b2fd1e6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/PlaceholdersRequirement.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +import java.util.Objects; +import java.util.Set; + +public abstract class PlaceholdersRequirement extends AbstractRequirement { + + private final PreParsedDynamicText t1; + private final PreParsedDynamicText t2; + + public PlaceholdersRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) { + super(refreshInterval); + this.t1 = t1; + this.t2 = t2; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + p1.forceUpdate(t1.placeholders(), Set.of(p2)); + p1.forceUpdate(t2.placeholders(), Set.of(p2)); + String a1 = t1.fastCreate(p1).render(p2); + String a2 = t2.fastCreate(p1).render(p2); + return checkArgument(a1, a2); + } + + protected abstract boolean checkArgument(String a1, String a2); + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + PlaceholdersRequirement that = (PlaceholdersRequirement) object; + return Objects.equals(t1, that.t1) && Objects.equals(t2, that.t2); + } + + @Override + public int hashCode() { + return 73 + t1.hashCode() * 509 + t2.hashCode() * 211 + type().hashCode() * 311; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/RegexRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/RegexRequirement.java new file mode 100644 index 0000000..c80ca46 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/RegexRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class RegexRequirement extends PlaceholderRequirement { + + public RegexRequirement(int refreshInterval, PreParsedDynamicText text, String any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return a1.matches(a2); + } + + @Override + public String type() { + return "regex"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/StartWithRequirement.java b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/StartWithRequirement.java new file mode 100644 index 0000000..2c37d41 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/requirement/builtin/StartWithRequirement.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.requirement.builtin; + +import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; + +public class StartWithRequirement extends PlaceholderRequirement { + + public StartWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) { + super(refreshInterval, text, any); + } + + @Override + protected boolean checkArgument(String a1, String a2) { + return a1.startsWith(a2); + } + + @Override + public String type() { + return "starts-with"; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/scheduler/Scheduler.java b/api/src/main/java/net/momirealms/customnameplates/api/scheduler/Scheduler.java deleted file mode 100644 index 9b20769..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/scheduler/Scheduler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.scheduler; - -import org.bukkit.Location; - -import java.util.concurrent.TimeUnit; - -public interface Scheduler { - - /** - * Runs a task synchronously on the main server thread or region thread. - * - * @param runnable The task to run. - * @param location The location associated with the task. - */ - void runTaskSync(Runnable runnable, Location location); - - /** - * Runs a task synchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the first execution. - * @param periodTicks The period between subsequent executions in ticks. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); - - /** - * Runs a task asynchronously with a specified delay. - * - * @param runnable The task to run. - * @param delay The delay before the task execution. - * @param timeUnit The time unit for the delay. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit); - - /** - * Runs a task asynchronously. - * - * @param runnable The task to run. - */ - void runTaskAsync(Runnable runnable); - - /** - * Runs a task synchronously with a specified delay. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay before the task execution. - * @param timeUnit The time unit for the delay. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit); - - /** - * Runs a task synchronously with a specified delay in ticks. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the task execution. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); - - /** - * Runs a task asynchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param delay The delay before the first execution. - * @param period The period between subsequent executions. - * @param timeUnit The time unit for the delay and period. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit); -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/storage/DataStorageProvider.java b/api/src/main/java/net/momirealms/customnameplates/api/storage/DataStorageProvider.java new file mode 100644 index 0000000..f63c274 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/storage/DataStorageProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.storage; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customnameplates.api.storage.data.PlayerData; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * Interface representing a provider for data storage. + */ +public interface DataStorageProvider { + + /** + * Initializes the data storage provider with the given configuration. + * + * @param config the {@link YamlDocument} configuration for the storage provider + */ + void initialize(YamlDocument config); + + /** + * Disables the data storage provider, performing any necessary cleanup. + */ + void disable(); + + StorageType getStorageType(); + + CompletableFuture> getPlayerData(UUID uuid, Executor executor); + + CompletableFuture updatePlayerData(PlayerData playerData, Executor executor); + + Set getUniqueUsers(); +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BarColor.java b/api/src/main/java/net/momirealms/customnameplates/api/storage/StorageManager.java similarity index 52% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BarColor.java rename to api/src/main/java/net/momirealms/customnameplates/api/storage/StorageManager.java index 2048ba8..f86f0d2 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BarColor.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/storage/StorageManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,26 +15,32 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bossbar; +package net.momirealms.customnameplates.api.storage; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.common.plugin.feature.Reloadable; import org.jetbrains.annotations.NotNull; -import java.util.Locale; +import java.util.UUID; -public enum BarColor { - PINK, - BLUE, - RED, - GREEN, - YELLOW, - PURPLE, - WHITE; +public interface StorageManager extends Reloadable { - public static BarColor getColor(@NotNull String name) { - try { - return BarColor.valueOf(name.toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException e) { - throw new RuntimeException("Color: " + name + " doesn't exist"); - } - } + @NotNull + String getServerID(); + + @NotNull + DataStorageProvider getDataSource(); + + boolean isRedisEnabled(); + + byte[] toBytes(@NotNull PlayerData data); + + @NotNull + String toJson(@NotNull PlayerData data); + + @NotNull + PlayerData fromJson(UUID uuid, String json); + + @NotNull + PlayerData fromBytes(UUID uuid, byte[] data); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/data/StorageType.java b/api/src/main/java/net/momirealms/customnameplates/api/storage/StorageType.java similarity index 78% rename from api/src/main/java/net/momirealms/customnameplates/api/data/StorageType.java rename to api/src/main/java/net/momirealms/customnameplates/api/storage/StorageType.java index 3c78a0f..ae3b776 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/data/StorageType.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/storage/StorageType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,17 +15,16 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.data; +package net.momirealms.customnameplates.api.storage; public enum StorageType { - - JSON, - YAML, - H2, - SQLite, - MySQL, - MariaDB, - MongoDB, - Redis, - None + JSON, + YAML, + H2, + SQLite, + MySQL, + MariaDB, + MongoDB, + Redis, + NONE } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/storage/data/JsonData.java b/api/src/main/java/net/momirealms/customnameplates/api/storage/data/JsonData.java new file mode 100644 index 0000000..278bac3 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/storage/data/JsonData.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.storage.data; + +import com.google.gson.annotations.SerializedName; + +import java.util.UUID; + +public class JsonData { + + @SerializedName("nameplate") + private String nameplate; + @SerializedName("bubble") + private String bubble; + + public JsonData(String nameplate, String bubble) { + this.nameplate = nameplate; + this.bubble = bubble; + } + + public String nameplate() { + return nameplate; + } + + public String bubble() { + return bubble; + } + + public PlayerData toPlayerData(UUID uuid) { + return PlayerData.builder() + .uuid(uuid) + .nameplate(nameplate) + .bubble(bubble) + .build(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/storage/data/PlayerData.java b/api/src/main/java/net/momirealms/customnameplates/api/storage/data/PlayerData.java new file mode 100644 index 0000000..60592eb --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/storage/data/PlayerData.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.storage.data; + +import java.util.UUID; + +public interface PlayerData { + + String DEFAULT_NAMEPLATE = "none"; + String DEFAULT_BUBBLE = "none"; + + String nameplate(); + + String bubble(); + + UUID uuid(); + + static Builder builder() { + return new PlayerDataImpl.BuilderImpl(); + } + + static PlayerData empty(UUID uuid) { + return builder().uuid(uuid) + .nameplate(DEFAULT_NAMEPLATE) + .bubble(DEFAULT_BUBBLE) + .build(); + } + + interface Builder { + + Builder nameplate(String nameplate); + + Builder bubble(String bubble); + + Builder uuid(UUID uuid); + + PlayerData build(); + } + + default JsonData toGsonData() { + return new JsonData(nameplate(), bubble()); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/storage/data/PlayerDataImpl.java b/api/src/main/java/net/momirealms/customnameplates/api/storage/data/PlayerDataImpl.java new file mode 100644 index 0000000..d249b9d --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/storage/data/PlayerDataImpl.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.storage.data; + +import java.util.UUID; + +/** + * The PlayerData class holds data related to a player. + * It includes the player's name, their nameplate and bubble data. + */ +public class PlayerDataImpl implements PlayerData { + + protected UUID uuid; + protected String nameplate; + protected String bubble; + + public PlayerDataImpl(UUID uuid, String nameplate, String bubble) { + this.uuid = uuid; + this.nameplate = nameplate; + this.bubble = bubble; + } + + @Override + public String nameplate() { + return nameplate; + } + + @Override + public String bubble() { + return bubble; + } + + @Override + public UUID uuid() { + return uuid; + } + + /** + * The Builder class provides a fluent API for constructing PlayerData instances. + */ + public static class BuilderImpl implements Builder { + + private String nameplate; + private String bubble; + private UUID uuid; + + @Override + public BuilderImpl nameplate(String nameplate) { + this.nameplate = nameplate; + return this; + } + + @Override + public BuilderImpl bubble(String bubble) { + this.bubble = bubble; + return this; + } + + @Override + public BuilderImpl uuid(UUID uuid) { + this.uuid = uuid; + return this; + } + + @Override + public PlayerData build() { + return new PlayerDataImpl(uuid, nameplate, bubble); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/Alignment.java b/api/src/main/java/net/momirealms/customnameplates/api/util/Alignment.java new file mode 100644 index 0000000..9b25346 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/util/Alignment.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.util; + +public enum Alignment { + + CENTER(0), + LEFT(1), + RIGHT(2); + + final int id; + + Alignment(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/CharacterUtils.java b/api/src/main/java/net/momirealms/customnameplates/api/util/CharacterUtils.java new file mode 100644 index 0000000..1da4710 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/util/CharacterUtils.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.util; + +import java.util.ArrayList; + +public class CharacterUtils { + + public static char[] unicodeToChars(String unicodeString) { + String processedString = unicodeString.replace("\\u", ""); + int length = processedString.length() / 4; + char[] chars = new char[length]; + for (int i = 0; i < length; i++) { + int codePoint = Integer.parseInt(processedString.substring(i * 4, i * 4 + 4), 16); + if (Character.isSupplementaryCodePoint(codePoint)) { + chars[i] = Character.highSurrogate(codePoint); + chars[++i] = Character.lowSurrogate(codePoint); + } else { + chars[i] = (char) codePoint; + } + } + return chars; + } + + public static int toCodePoint(char[] chars) { + if (chars.length == 1) { + return chars[0]; + } else if (chars.length == 2) { + return Character.toCodePoint(chars[0], chars[1]); + } else { + throw new IllegalArgumentException("The given chars array contains more than 2 characters"); + } + } + + public static int[] toCodePoints(char[] chars) { + ArrayList codePoints = new ArrayList<>(); + for (int i = 0; i < chars.length; i++) { + int codePoint; + char c1 = chars[i]; + if (Character.isHighSurrogate(c1)) { + if (i + 1 < chars.length && Character.isLowSurrogate(chars[i + 1])) { + char c2 = chars[++i]; + codePoint = Character.toCodePoint(c1, c2); + } else { + throw new IllegalArgumentException("Illegal surrogate pair: High surrogate without matching low surrogate at index " + i); + } + } else { + codePoint = c1; + } + codePoints.add(codePoint); + } + return codePoints.stream().mapToInt(i -> i).toArray(); + } + + public static String char2Unicode(char c) { + StringBuilder stringBuilder_1 = new StringBuilder("\\u"); + StringBuilder stringBuilder_2 = new StringBuilder(Integer.toHexString(c)); + stringBuilder_2.reverse(); + for (int n = 4 - stringBuilder_2.length(), i = 0; i < n; i++) stringBuilder_2.append('0'); + for (int j = 0; j < 4; j++) stringBuilder_1.append(stringBuilder_2.charAt(3 - j)); + return stringBuilder_1.toString(); + } + + public static String char2Unicode(char[] c) { + StringBuilder builder = new StringBuilder(); + for (char value : c) { + builder.append(char2Unicode(value)); + } + return builder.toString(); + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/CompletableFutures.java b/api/src/main/java/net/momirealms/customnameplates/api/util/CompletableFutures.java deleted file mode 100644 index 01df60b..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/util/CompletableFutures.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.util; - -import com.google.common.collect.ImmutableList; - -import java.util.Collection; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collector; -import java.util.stream.Stream; - -public class CompletableFutures { - - private CompletableFutures() {} - - /** - * A collector for collecting a stream of CompletableFuture instances into a single CompletableFuture that completes - * when all of the input CompletableFutures complete. - * - * @param The type of CompletableFuture. - * @return A collector for CompletableFuture instances. - */ - public static > Collector, CompletableFuture> collector() { - return Collector.of( - ImmutableList.Builder::new, - ImmutableList.Builder::add, - (l, r) -> l.addAll(r.build()), - builder -> allOf(builder.build()) - ); - } - - /** - * Combines multiple CompletableFuture instances into a single CompletableFuture that completes when all of the input - * CompletableFutures complete. - * - * @param futures A stream of CompletableFuture instances. - * @return A CompletableFuture that completes when all input CompletableFutures complete. - */ - public static CompletableFuture allOf(Stream> futures) { - CompletableFuture[] arr = futures.toArray(CompletableFuture[]::new); - return CompletableFuture.allOf(arr); - } - - /** - * Combines multiple CompletableFuture instances into a single CompletableFuture that completes when all of the input - * CompletableFutures complete. - * - * @param futures A collection of CompletableFuture instances. - * @return A CompletableFuture that completes when all input CompletableFutures complete. - */ - public static CompletableFuture allOf(Collection> futures) { - CompletableFuture[] arr = futures.toArray(new CompletableFuture[0]); - return CompletableFuture.allOf(arr); - } -} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/ConfigUtils.java b/api/src/main/java/net/momirealms/customnameplates/api/util/ConfigUtils.java new file mode 100644 index 0000000..cd5c060 --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/util/ConfigUtils.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.util; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.CarouselText; +import net.momirealms.customnameplates.api.requirement.Requirement; + +import java.io.File; +import java.util.*; + +public class ConfigUtils { + + public static CarouselText carouselText(Section section) { + return new CarouselText( + section.getInt("duration", 200), + CustomNameplates.getInstance().getRequirementManager().parseRequirements(section.getSection("conditions")), + section.getString("text", ""), + section.getBoolean("update-on-display", true) + ); + } + + public static CarouselText[] carouselTexts(Section section) { + TreeMap map = new TreeMap<>(); + if (section == null) { + CustomNameplates.getInstance().getPluginLogger().warn("text-display-order section is null, this might cause bugs!"); + map.put(1, new CarouselText(100, new Requirement[0], "", false)); + return map.values().toArray(new CarouselText[0]); + } + for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { + if (!(entry.getValue() instanceof Section inner)) + continue; + int order; + try { + order = Integer.parseInt(entry.getKey()); + } catch (NumberFormatException e) { + CustomNameplates.getInstance().getPluginLogger().warn("Invalid order format: " + entry.getKey()); + continue; + } + map.put(order, carouselText(inner)); + } + return map.values().toArray(new CarouselText[0]); + } + + public static int argb(String arg) { + return argb(arg.split(",")); + } + + public static int argb(String[] args) { + int alpha = args.length >= 1 ? Integer.parseInt(args[0]) : 64; + int red = args.length >= 2 ? Integer.parseInt(args[1]) : 0; + int green = args.length >= 3 ? Integer.parseInt(args[2]) : 0; + int blue = args.length >= 4 ? Integer.parseInt(args[3]) : 0; + return argb(alpha, red, green, blue); + } + + public static int argb(int alpha, int red, int green, int blue) { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } + + public static Vector3 vector3(String arg) { + return vector3(arg.split(",")); + } + + public static Vector3 vector3(String[] args) { + float x = args.length >= 1 ? Float.parseFloat(args[0]) : 0f; + float y = args.length >= 2 ? Float.parseFloat(args[1]) : 0f; + float z = args.length >= 3 ? Float.parseFloat(args[2]) : 0f; + return vector3(x, y, z); + } + + public static Vector3 vector3(float x, float y, float z) { + return new Vector3(x, y, z); + } + + public static List getConfigsDeeply(File configFolder) { + ArrayList validConfigs = new ArrayList<>(); + Deque fileDeque = new ArrayDeque<>(); + fileDeque.push(configFolder); + while (!fileDeque.isEmpty()) { + File file = fileDeque.pop(); + File[] files = file.listFiles(); + if (files == null) continue; + for (File subFile : files) { + if (subFile.isDirectory()) { + fileDeque.push(subFile); + } else if (subFile.isFile() && subFile.getName().endsWith(".yml")) { + validConfigs.add(subFile); + } + } + } + validConfigs.sort(Comparator.comparing(File::getName)); + return validConfigs; + } + + public static File getFileInTheSameFolder(File file, String fileName) { + File folder = file.getParentFile(); + return new File(folder, fileName); + } + + public static T safeCast(Object object, Class clazz) { + try { + return clazz.cast(object); + } catch (ClassCastException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Cannot cast " + object.getClass().getSimpleName() + " to " + clazz.getSimpleName(), e); + return null; + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/FontUtils.java b/api/src/main/java/net/momirealms/customnameplates/api/util/FontUtils.java deleted file mode 100644 index e76abe5..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/util/FontUtils.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.util; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; - -public class FontUtils { - - private static String namespace; - private static String font; - - /** - * Surround the text with ascent font - * - * @param text text - * @param ascent ascent - * @return ascent font text - */ - public static String surroundAscentFont(String text, int ascent) { - return getAscentFontTag(ascent) + text + getFontTagCloser(); - } - - /** - * Surround the text with custom nameplates font - * - * @param text text - * @return font text - */ - public static String surroundNameplateFont(String text) { - return getMiniMessageFontTag() + text + getFontTagCloser(); - } - - private static String getAscentFontTag(int ascent) { - return ""; - } - - private static String getMiniMessageFontTag() { - return ""; - } - - private static String getFontTagCloser() { - return ""; - } - - /** - * Get a text's width - * - * @param text text - * @return width - */ - public static int getTextWidth(String text) { - return CustomNameplatesPlugin.get().getWidthManager().getTextWidth(text); - } - - /** - * Set namespace and font - * - * @param n namespace - * @param f font - */ - public static void setNameSpaceAndFont(String n, String f) { - namespace = n; - font = f; - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/LogUtils.java b/api/src/main/java/net/momirealms/customnameplates/api/util/LogUtils.java deleted file mode 100644 index dde8f82..0000000 --- a/api/src/main/java/net/momirealms/customnameplates/api/util/LogUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.api.util; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import org.jetbrains.annotations.NotNull; - -import java.util.logging.Level; - -public final class LogUtils { - - /** - * Log an informational message. - * - * @param message The message to log. - */ - public static void info(@NotNull String message) { - CustomNameplatesPlugin.getInstance().getLogger().info(message); - } - - /** - * Log a warning message. - * - * @param message The message to log. - */ - public static void warn(@NotNull String message) { - CustomNameplatesPlugin.getInstance().getLogger().warning(message); - } - - /** - * Log a severe error message. - * - * @param message The message to log. - */ - public static void severe(@NotNull String message) { - CustomNameplatesPlugin.getInstance().getLogger().severe(message); - } - - /** - * Log a warning message with a throwable exception. - * - * @param message The message to log. - * @param throwable The throwable exception to log. - */ - public static void warn(@NotNull String message, Throwable throwable) { - CustomNameplatesPlugin.getInstance().getLogger().log(Level.WARNING, message, throwable); - } - - /** - * Log a severe error message with a throwable exception. - * - * @param message The message to log. - * @param throwable The throwable exception to log. - */ - public static void severe(@NotNull String message, Throwable throwable) { - CustomNameplatesPlugin.getInstance().getLogger().log(Level.SEVERE, message, throwable); - } - - private LogUtils() { - throw new UnsupportedOperationException("This class cannot be instantiated"); - } -} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/SelfIncreaseEntityID.java b/api/src/main/java/net/momirealms/customnameplates/api/util/SelfIncreaseEntityID.java new file mode 100644 index 0000000..22136ef --- /dev/null +++ b/api/src/main/java/net/momirealms/customnameplates/api/util/SelfIncreaseEntityID.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.api.util; + +import java.util.concurrent.ThreadLocalRandom; + +public class SelfIncreaseEntityID { + + private static int id = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE / 4, Integer.MAX_VALUE / 3); + + public static int getAndIncrease() { + int i = id; + id++; + return i; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/util/LocationUtils.java b/api/src/main/java/net/momirealms/customnameplates/api/util/Vector3.java similarity index 50% rename from api/src/main/java/net/momirealms/customnameplates/api/util/LocationUtils.java rename to api/src/main/java/net/momirealms/customnameplates/api/util/Vector3.java index f7b7c8b..4af0956 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/util/LocationUtils.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/util/Vector3.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,21 +17,25 @@ package net.momirealms.customnameplates.api.util; -import org.bukkit.Location; -import org.bukkit.entity.Entity; +import net.momirealms.customnameplates.api.CustomNameplates; -public class LocationUtils { +public record Vector3(double x, double y, double z) { - /** - * Get distance by two entities - * - * @param e1 entity - * @param e2 entity - * @return distance - */ - public static double getDistance(Entity e1, Entity e2) { - Location loc1 = e1.getLocation(); - Location loc2 = e2.getLocation(); - return Math.sqrt(Math.pow(loc1.getX()-loc2.getX(), 2) + Math.pow(loc1.getZ()-loc2.getZ(), 2)); + public static final Vector3 ZERO = new Vector3(0, 0, 0); + + public Object toVec3() { + return CustomNameplates.getInstance().getPlatform().vec3(x, y, z); + } + + public Vector3 add(Vector3 another) { + return new Vector3(this.x + another.x, this.y + another.y, this.z + another.z); + } + + public Vector3 add(double x, double y, double z) { + return new Vector3(this.x + x, this.y + y, this.z + z); + } + + public Vector3 multiply(double value) { + return new Vector3(this.x * value, this.y * value, this.z * value); } } diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts new file mode 100644 index 0000000..4e48cbe --- /dev/null +++ b/backend/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("io.github.goooler.shadow") version "8.1.8" +} + +repositories { +} + +dependencies { + compileOnly(project(":common")) + compileOnly(project(":api")) + // YAML + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + // Adventure + compileOnly("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"]}") + // Gson + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") + // Database + compileOnly("org.xerial:sqlite-jdbc:${rootProject.properties["sqlite_driver_version"]}") + compileOnly("com.h2database:h2:${rootProject.properties["h2_driver_version"]}") + compileOnly("org.mongodb:mongodb-driver-sync:${rootProject.properties["mongodb_driver_version"]}") + compileOnly("com.zaxxer:HikariCP:${rootProject.properties["hikari_version"]}") + compileOnly("redis.clients:jedis:${rootProject.properties["jedis_version"]}") +} + +tasks { + shadowJar { + relocate ("net.kyori", "net.momirealms.customnameplates.libraries") + } +} \ No newline at end of file diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/StorageManagerImpl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/StorageManagerImpl.java new file mode 100644 index 0000000..9b30c03 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/StorageManagerImpl.java @@ -0,0 +1,196 @@ +package net.momirealms.customnameplates.backend.storage; + +import com.google.gson.JsonSyntaxException; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customnameplates.api.AbstractCNPlayer; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.JoinQuitListener; +import net.momirealms.customnameplates.api.helper.GsonHelper; +import net.momirealms.customnameplates.api.storage.DataStorageProvider; +import net.momirealms.customnameplates.api.storage.StorageManager; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.JsonData; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.backend.storage.method.DummyStorage; +import net.momirealms.customnameplates.backend.storage.method.database.nosql.MongoDBProvider; +import net.momirealms.customnameplates.backend.storage.method.database.nosql.RedisManager; +import net.momirealms.customnameplates.backend.storage.method.database.sql.H2Provider; +import net.momirealms.customnameplates.backend.storage.method.database.sql.MariaDBProvider; +import net.momirealms.customnameplates.backend.storage.method.database.sql.MySQLProvider; +import net.momirealms.customnameplates.backend.storage.method.database.sql.SQLiteProvider; +import net.momirealms.customnameplates.backend.storage.method.file.JsonProvider; +import net.momirealms.customnameplates.backend.storage.method.file.YAMLProvider; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.concurrent.Executor; + +public class StorageManagerImpl implements StorageManager, JoinQuitListener { + + private final CustomNameplates plugin; + private DataStorageProvider dataSource; + private StorageType previousType; + private boolean hasRedis; + private RedisManager redisManager; + private String serverID; + + public StorageManagerImpl(final CustomNameplates plugin) { + this.plugin = plugin; + } + + @SuppressWarnings("DuplicatedCode") + @Override + public void onPlayerJoin(CNPlayer player) { + UUID uuid = player.uuid(); + Executor async = plugin.getScheduler().async(); + if (hasRedis) { + this.redisManager.getPlayerData(uuid, async).thenAccept(playerData1 -> { + if (playerData1.isPresent()) { + PlayerData data = playerData1.get(); + handleDataLoad(player, data); + ((AbstractCNPlayer) player).setLoaded(true); + this.redisManager.updatePlayerData(data, async).thenAccept(result -> { + if (!result) { + plugin.getPluginLogger().warn("Failed to refresh redis player data for " + player.name()); + } + }); + } else { + this.getDataSource().getPlayerData(uuid, async).thenAccept(playerData2 -> { + if (playerData2.isPresent()) { + PlayerData data = playerData2.get(); + handleDataLoad(player, data); + ((AbstractCNPlayer) player).setLoaded(true); + this.redisManager.updatePlayerData(data, async).thenAccept(result -> { + if (!result) { + plugin.getPluginLogger().warn("Failed to refresh redis player data for " + player.name()); + } + }); + } else { + plugin.getPluginLogger().warn("Failed to load player data for " + player.name()); + } + }); + } + }); + } else { + this.getDataSource().getPlayerData(uuid, async).thenAccept(playerData -> { + if (playerData.isPresent()) { + PlayerData data = playerData.get(); + handleDataLoad(player, data); + ((AbstractCNPlayer) player).setLoaded(true); + } else { + plugin.getPluginLogger().warn("Failed to load player data for " + player.name()); + } + }); + } + } + + private void handleDataLoad(CNPlayer player, PlayerData data) { + player.equippedBubble(data.bubble()); + player.equippedNameplate(data.nameplate()); + } + + @Override + public void onPlayerQuit(CNPlayer player) { + ((AbstractCNPlayer) player).setLoaded(false); + } + + @Override + public void reload() { + YamlDocument config = plugin.getConfigManager().loadConfig("database.yml"); + this.serverID = config.getString("unique-server-id", "default"); + try { + config.save(new File(plugin.getDataFolder(), "database.yml")); + } catch (IOException e) { + throw new RuntimeException(e); + } + // Check if storage type has changed and reinitialize if necessary + StorageType storageType = StorageType.valueOf(config.getString("data-storage-method", "H2")); + if (storageType != previousType) { + if (this.dataSource != null) this.dataSource.disable(); + this.previousType = storageType; + switch (storageType) { + case H2 -> this.dataSource = new H2Provider(plugin); + case JSON -> this.dataSource = new JsonProvider(plugin); + case YAML -> this.dataSource = new YAMLProvider(plugin); + case SQLite -> this.dataSource = new SQLiteProvider(plugin); + case MySQL -> this.dataSource = new MySQLProvider(plugin); + case MariaDB -> this.dataSource = new MariaDBProvider(plugin); + case MongoDB -> this.dataSource = new MongoDBProvider(plugin); + case NONE -> this.dataSource = new DummyStorage(plugin); + } + if (this.dataSource != null) this.dataSource.initialize(config); + else plugin.getPluginLogger().severe("No storage type is set."); + } + + // Handle Redis configuration + if (!this.hasRedis && config.getBoolean("Redis.enable", false)) { + this.redisManager = new RedisManager(plugin); + this.redisManager.initialize(config); + this.hasRedis = true; + } + + // Disable Redis if it was enabled but is now disabled + if (this.hasRedis && !config.getBoolean("Redis.enable", false) && this.redisManager != null) { + this.hasRedis = false; + this.redisManager.disable(); + this.redisManager = null; + } + } + + @Override + public void disable() { + if (this.dataSource != null) + this.dataSource.disable(); + if (this.redisManager != null) + this.redisManager.disable(); + } + + @NotNull + @Override + public String getServerID() { + return serverID; + } + + @NotNull + @Override + public DataStorageProvider getDataSource() { + return dataSource; + } + + @Override + public boolean isRedisEnabled() { + return hasRedis; + } + + @Override + public byte[] toBytes(@NotNull PlayerData data) { + return toJson(data).getBytes(StandardCharsets.UTF_8); + } + + @NotNull + @Override + public String toJson(@NotNull PlayerData data) { + return GsonHelper.get().toJson(data.toGsonData()); + } + + @NotNull + @Override + public PlayerData fromJson(UUID uuid, String json) { + try { + return GsonHelper.get().fromJson(json, JsonData.class).toPlayerData(uuid); + } catch (JsonSyntaxException e) { + plugin.getPluginLogger().severe("Failed to get PlayerData from json. Json: " + json); + throw new RuntimeException(e); + } + } + + @NotNull + @Override + public PlayerData fromBytes(UUID uuid, byte[] data) { + return fromJson(uuid, new String(data, StandardCharsets.UTF_8)); + } +} diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/AbstractStorage.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/AbstractStorage.java new file mode 100644 index 0000000..19e08a1 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/AbstractStorage.java @@ -0,0 +1,24 @@ +package net.momirealms.customnameplates.backend.storage.method; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.DataStorageProvider; + +public abstract class AbstractStorage implements DataStorageProvider { + + protected CustomNameplates plugin; + + public AbstractStorage(CustomNameplates plugin) { + this.plugin = plugin; + } + + @Override + public void initialize(YamlDocument config) { + // This method can be overridden in subclasses to perform initialization tasks specific to the storage type. + } + + @Override + public void disable() { + // This method can be overridden in subclasses to perform cleanup or shutdown tasks specific to the storage type. + } +} diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/DummyStorage.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/DummyStorage.java new file mode 100644 index 0000000..8a2bde1 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/DummyStorage.java @@ -0,0 +1,38 @@ +package net.momirealms.customnameplates.backend.storage.method; + +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.PlayerData; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class DummyStorage extends AbstractStorage { + + public DummyStorage(CustomNameplates plugin) { + super(plugin); + } + + @Override + public StorageType getStorageType() { + return StorageType.NONE; + } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + return CompletableFuture.completedFuture(Optional.of(PlayerData.empty(uuid))); + } + + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + return CompletableFuture.completedFuture(true); + } + + @Override + public Set getUniqueUsers() { + return Set.of(); + } +} diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/nosql/MongoDBProvider.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/nosql/MongoDBProvider.java new file mode 100644 index 0000000..f432666 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/nosql/MongoDBProvider.java @@ -0,0 +1,154 @@ +package net.momirealms.customnameplates.backend.storage.method.database.nosql; + +import com.mongodb.*; +import com.mongodb.client.*; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Projections; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.result.UpdateResult; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.backend.storage.method.AbstractStorage; +import org.bson.Document; +import org.bson.UuidRepresentation; +import org.bson.conversions.Bson; +import org.bson.types.Binary; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class MongoDBProvider extends AbstractStorage { + + private MongoClient mongoClient; + private MongoDatabase database; + private String collectionPrefix; + + public MongoDBProvider(CustomNameplates plugin) { + super(plugin); + } + + @Override + public void initialize(YamlDocument config) { + Section section = config.getSection("MongoDB"); + if (section == null) { + plugin.getPluginLogger().warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); + return; + } + + collectionPrefix = section.getString("collection-prefix", "nameplates"); + var settings = MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD); + if (!section.getString("connection-uri", "").equals("")) { + settings.applyConnectionString(new ConnectionString(section.getString("connection-uri", ""))); + this.mongoClient = MongoClients.create(settings.build()); + this.database = mongoClient.getDatabase(section.getString("database", "minecraft")); + return; + } + + if (section.contains("user")) { + MongoCredential credential = MongoCredential.createCredential( + section.getString("user", "root"), + section.getString("database", "minecraft"), + section.getString("password", "password").toCharArray() + ); + settings.credential(credential); + } + + settings.applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(new ServerAddress( + section.getString("host", "localhost"), + section.getInt("port", 27017) + )))); + this.mongoClient = MongoClients.create(settings.build()); + this.database = mongoClient.getDatabase(section.getString("database", "minecraft")); + } + + @Override + public void disable() { + if (this.mongoClient != null) { + this.mongoClient.close(); + } + } + + /** + * Get the collection name for a specific subcategory of data. + * + * @param value The subcategory identifier. + * @return The full collection name including the prefix. + */ + public String getCollectionName(String value) { + return getCollectionPrefix() + "_" + value; + } + + /** + * Get the collection prefix used for MongoDB collections. + * + * @return The collection prefix. + */ + public String getCollectionPrefix() { + return collectionPrefix; + } + + @Override + public StorageType getStorageType() { + return StorageType.MongoDB; + } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + CompletableFuture> future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + MongoCollection collection = database.getCollection(getCollectionName("data")); + Document doc = collection.find(Filters.eq("uuid", uuid)).first(); + if (doc == null) { + future.complete(Optional.empty()); + } else if (plugin.getPlayer(uuid) != null) { + Binary binary = (Binary) doc.get("data"); + future.complete(Optional.of(plugin.getStorageManager().fromBytes(uuid, binary.getData()))); + } else { + future.complete(Optional.empty()); + } + }); + return future; + } + + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + CompletableFuture future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + MongoCollection collection = database.getCollection(getCollectionName("data")); + try { + Document query = new Document("uuid", playerData.uuid()); + Bson updates = Updates.combine(Updates.set("data", new Binary(plugin.getStorageManager().toBytes(playerData)))); + UpdateOptions options = new UpdateOptions().upsert(true); + UpdateResult result = collection.updateOne(query, updates, options); + future.complete(result.wasAcknowledged()); + } catch (MongoException e) { + future.completeExceptionally(e); + } + }); + return future; + } + + @Override + public Set getUniqueUsers() { + Set uuids = new HashSet<>(); + MongoCollection collection = database.getCollection(getCollectionName("data")); + try { + Bson projectionFields = Projections.fields(Projections.include("uuid")); + try (MongoCursor cursor = collection.find().projection(projectionFields).iterator()) { + while (cursor.hasNext()) { + uuids.add(cursor.next().get("uuid", UUID.class)); + } + } + } catch (MongoException e) { + plugin.getPluginLogger().warn("Failed to get unique data.", e); + } + return uuids; + } +} diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/nosql/RedisManager.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/nosql/RedisManager.java new file mode 100644 index 0000000..8b6e6d1 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/nosql/RedisManager.java @@ -0,0 +1,169 @@ +package net.momirealms.customnameplates.backend.storage.method.database.nosql; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.backend.storage.method.AbstractStorage; +import org.jetbrains.annotations.NotNull; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.exceptions.JedisException; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class RedisManager extends AbstractStorage { + + private static RedisManager instance; + private final static String STREAM = "customnameplate"; + private JedisPool jedisPool; + private String password; + private int port; + private String host; + private boolean useSSL; + + public RedisManager(CustomNameplates plugin) { + super(plugin); + instance = this; + } + + /** + * Get the singleton instance of the RedisManager. + * + * @return The RedisManager instance. + */ + public static RedisManager getInstance() { + return instance; + } + + /** + * Get a Jedis resource for interacting with the Redis server. + * + * @return A Jedis resource. + */ + public Jedis getJedis() { + return jedisPool.getResource(); + } + + /** + * Initialize the Redis connection and configuration based on the plugin's YAML configuration. + */ + @Override + public void initialize(YamlDocument config) { + Section section = config.getSection("Redis"); + if (section == null) { + plugin.getPluginLogger().warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); + return; + } + + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); + jedisPoolConfig.setTestWhileIdle(true); + jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000)); + jedisPoolConfig.setNumTestsPerEvictionRun(-1); + jedisPoolConfig.setMinEvictableIdleDuration(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis", 1800000))); + jedisPoolConfig.setMaxTotal(section.getInt("MaxTotal",8)); + jedisPoolConfig.setMaxIdle(section.getInt("MaxIdle",8)); + jedisPoolConfig.setMinIdle(section.getInt("MinIdle",1)); + jedisPoolConfig.setMaxWait(Duration.ofMillis(section.getInt("MaxWaitMillis"))); + + password = section.getString("password", ""); + port = section.getInt("port", 6379); + host = section.getString("host", "localhost"); + useSSL = section.getBoolean("use-ssl", false); + + if (password.isBlank()) { + jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, useSSL); + } else { + jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, password, useSSL); + } + try (Jedis jedis = jedisPool.getResource()) { + jedis.info(); + plugin.getPluginLogger().info("Redis server connected."); + } catch (JedisException e) { + plugin.getPluginLogger().warn("Failed to connect redis.", e); + } + } + + /** + * Disable the Redis connection by closing the JedisPool. + */ + @Override + public void disable() { + if (jedisPool != null && !jedisPool.isClosed()) + jedisPool.close(); + } + + @Override + public StorageType getStorageType() { + return StorageType.Redis; + } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + CompletableFuture> future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try (Jedis jedis = getJedis()) { + byte[] key = getRedisKey("cn_data", uuid); + byte[] data = jedis.get(key); + if (data != null) { + future.complete(Optional.of(plugin.getStorageManager().fromBytes(uuid, data))); + plugin.debug(() -> "Redis data retrieved for " + uuid + "; normal data"); + } else { + future.complete(Optional.empty()); + plugin.debug(() -> "Redis data retrieved for " + uuid + "; empty data"); + } + } catch (Exception e) { + plugin.getPluginLogger().warn("Failed to get redis data for " + uuid, e); + future.complete(Optional.empty()); + } + }); + return future; + } + + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + CompletableFuture future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try (Jedis jedis = getJedis()) { + jedis.setex( + getRedisKey("cn_data", playerData.uuid()), + 1200, + plugin.getStorageManager().toBytes(playerData) + ); + plugin.debug(() -> "Redis data set for " + playerData.uuid()); + future.complete(true); + } catch (Exception e) { + plugin.getPluginLogger().warn("Failed to set redis data for player " + playerData.uuid(), e); + future.complete(false); + } + }); + return future; + } + + @Override + public Set getUniqueUsers() { + return new HashSet<>(); + } + + /** + * Generate a Redis key for a specified key and UUID. + * + * @param key The key identifier. + * @param uuid The UUID to include in the key. + * @return A byte array representing the Redis key. + */ + private byte[] getRedisKey(String key, @NotNull UUID uuid) { + return (key + ":" + uuid).getBytes(StandardCharsets.UTF_8); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/AbstractHikariDatabase.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/AbstractHikariDatabase.java similarity index 50% rename from paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/AbstractHikariDatabase.java rename to backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/AbstractHikariDatabase.java index fd7845b..478c18c 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/AbstractHikariDatabase.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/AbstractHikariDatabase.java @@ -1,49 +1,25 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method.database.sql; +package net.momirealms.customnameplates.backend.storage.method.database.sql; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.LegacyDataStorageInterface; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.api.util.LogUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; -import java.util.concurrent.CompletableFuture; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; -/** - * An abstract base class for SQL databases using the HikariCP connection pool, which handles player data storage. - */ -public abstract class AbstractHikariDatabase extends AbstractSQLDatabase implements LegacyDataStorageInterface { +public abstract class AbstractHikariDatabase extends AbstractSQLDatabase { private HikariDataSource dataSource; private final String driverClass; private final String sqlBrand; - public AbstractHikariDatabase(CustomNameplatesPlugin plugin) { + public AbstractHikariDatabase(CustomNameplates plugin) { super(plugin); this.driverClass = getStorageType() == StorageType.MariaDB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver"; this.sqlBrand = getStorageType() == StorageType.MariaDB ? "MariaDB" : "MySQL"; @@ -51,27 +27,23 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme Class.forName(this.driverClass); } catch (ClassNotFoundException e1) { if (getStorageType() == StorageType.MariaDB) { - LogUtils.warn("No MariaDB driver is found"); + plugin.getPluginLogger().warn("No MariaDB driver is found"); } else if (getStorageType() == StorageType.MySQL) { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e2) { - LogUtils.warn("No MySQL driver is found"); + plugin.getPluginLogger().warn("No MySQL driver is found"); } } } } - /** - * Initialize the database connection pool and create tables if they don't exist. - */ @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); - ConfigurationSection section = config.getConfigurationSection(sqlBrand); + public void initialize(YamlDocument config) { + Section section = config.getSection(sqlBrand); if (section == null) { - LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); + plugin.getPluginLogger().warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); return; } @@ -91,7 +63,7 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme hikariConfig.setMinimumIdle(section.getInt("Pool-Settings.min-idle", 10)); hikariConfig.setMaxLifetime(section.getLong("Pool-Settings.max-lifetime", 180000L)); hikariConfig.setConnectionTimeout(section.getLong("Pool-Settings.time-out", 20000L)); - hikariConfig.setPoolName("CustomNameplatesHikariPool"); + hikariConfig.setPoolName("CustomFishingHikariPool"); try { hikariConfig.setKeepaliveTime(section.getLong("Pool-Settings.keep-alive-time", 60000L)); } catch (NoSuchMethodError ignored) { @@ -119,45 +91,12 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme super.createTableIfNotExist(); } - @Override - public CompletableFuture> getLegacyPlayerData(UUID uuid) { - var future = new CompletableFuture>(); - String sql = String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data")); - try (Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(sql)) { - statement.setString(1, uuid.toString()); - ResultSet rs = statement.executeQuery(); - if (rs.next()) { - String nameplate = rs.getString(2); - String bubbles = rs.getString(3); - future.complete(Optional.of(PlayerData.builder() - .setBubble(bubbles) - .setNameplate(nameplate) - .build())); - } else { - future.complete(Optional.empty()); - } - } catch (SQLException e) { - e.printStackTrace(); - } - return future; - } - - /** - * Disable the database by closing the connection pool. - */ @Override public void disable() { if (dataSource != null && !dataSource.isClosed()) dataSource.close(); } - /** - * Get a connection to the SQL database from the connection pool. - * - * @return A database connection. - * @throws SQLException If there is an error establishing a connection. - */ @Override public Connection getConnection() throws SQLException { return dataSource.getConnection(); diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/AbstractSQLDatabase.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/AbstractSQLDatabase.java new file mode 100644 index 0000000..e0b268f --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/AbstractSQLDatabase.java @@ -0,0 +1,184 @@ +package net.momirealms.customnameplates.backend.storage.method.database.sql; + +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.backend.storage.method.AbstractStorage; +import org.jetbrains.annotations.NotNull; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * An abstract base class for SQL database implementations that handle player data storage. + */ +public abstract class AbstractSQLDatabase extends AbstractStorage { + + protected String tablePrefix; + + public AbstractSQLDatabase(CustomNameplates plugin) { + super(plugin); + } + + /** + * Get a connection to the SQL database. + * + * @return A database connection. + * @throws SQLException If there is an error establishing a connection. + */ + public abstract Connection getConnection() throws SQLException; + + /** + * Create tables for storing data if they don't exist in the database. + */ + public void createTableIfNotExist() { + try (Connection connection = getConnection()) { + final String[] databaseSchema = getSchema(getStorageType().name().toLowerCase(Locale.ENGLISH)); + try (Statement statement = connection.createStatement()) { + for (String tableCreationStatement : databaseSchema) { + statement.execute(tableCreationStatement); + } + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to create tables", e); + } + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to get sql connection", e); + } catch (IOException e) { + plugin.getPluginLogger().warn("Failed to get schema resource", e); + } + } + + /** + * Get the SQL schema from a resource file. + * + * @param fileName The name of the schema file. + * @return An array of SQL statements to create tables. + * @throws IOException If there is an error reading the schema resource. + */ + private String[] getSchema(@NotNull String fileName) throws IOException { + return replaceSchemaPlaceholder(new String(Objects.requireNonNull(plugin.getResourceStream("schema/" + fileName + ".sql")) + .readAllBytes(), StandardCharsets.UTF_8)).split(";"); + } + + /** + * Replace placeholder values in SQL schema with the table prefix. + * + * @param sql The SQL schema string. + * @return The SQL schema string with placeholders replaced. + */ + private String replaceSchemaPlaceholder(@NotNull String sql) { + return sql.replace("{prefix}", tablePrefix); + } + + /** + * Get the name of a database table based on a sub-table name and the table prefix. + * + * @param sub The sub-table name. + * @return The full table name. + */ + public String getTableName(String sub) { + return getTablePrefix() + "_" + sub; + } + + /** + * Get the current table prefix. + * + * @return The table prefix. + */ + public String getTablePrefix() { + return tablePrefix; + } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + CompletableFuture> future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) + ) { + statement.setString(1, uuid.toString()); + ResultSet rs = statement.executeQuery(); + if (rs.next()) { + Blob blob = rs.getBlob("data"); + byte[] dataByteArray = blob.getBytes(1, (int) blob.length()); + blob.free(); + future.complete(Optional.of(plugin.getStorageManager().fromBytes(uuid, dataByteArray))); + } else if (plugin.getPlayer(uuid) != null) { + var data = PlayerData.empty(uuid); + this.insertPlayerData(uuid, data); + future.complete(Optional.of(data)); + } else { + future.complete(Optional.empty()); + } + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); + future.complete(Optional.empty()); + } + }); + return future; + } + + public void insertPlayerData(UUID uuid, PlayerData playerData) { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_INSERT_DATA_BY_UUID, getTableName("data"))) + ) { + statement.setString(1, uuid.toString()); + statement.setBlob(2, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData))); + statement.execute(); + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to insert " + uuid + "'s data.", e); + } + } + + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + CompletableFuture future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data"))) + ) { + statement.setBlob(1, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData))); + statement.setString(2, playerData.uuid().toString()); + statement.executeUpdate(); + future.complete(true); + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to update " + playerData.uuid() + "'s data.", e); + future.complete(false); + } + }); + return future; + } + + @Override + public Set getUniqueUsers() { + Set uuids = new HashSet<>(); + try (Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_ALL_UUID, getTableName("data")))) { + try (ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + UUID uuid = UUID.fromString(rs.getString("uuid")); + uuids.add(uuid); + } + } + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to get unique data.", e); + } + return uuids; + } + + public static class SqlConstants { + public static final String SQL_SELECT_BY_UUID = "SELECT * FROM `%s` WHERE `uuid` = ?"; + public static final String SQL_SELECT_ALL_UUID = "SELECT uuid FROM `%s`"; + public static final String SQL_UPDATE_BY_UUID = "UPDATE `%s` SET `data` = ? WHERE `uuid` = ?"; + public static final String SQL_INSERT_DATA_BY_UUID = "INSERT INTO `%s`(`uuid`, `data`) VALUES(?, ?)"; + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/H2Impl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/H2Provider.java similarity index 56% rename from paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/H2Impl.java rename to backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/H2Provider.java index b0fefbd..8b6c7ec 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/H2Impl.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/H2Provider.java @@ -1,27 +1,9 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +package net.momirealms.customnameplates.backend.storage.method.database.sql; -package net.momirealms.customnameplates.paper.storage.method.database.sql; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.libraries.dependencies.Dependency; -import org.bukkit.configuration.file.YamlConfiguration; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.common.dependency.Dependency; import java.io.File; import java.lang.reflect.Method; @@ -31,13 +13,13 @@ import java.util.EnumSet; /** * An implementation of AbstractSQLDatabase that uses the H2 embedded database for player data storage. */ -public class H2Impl extends AbstractSQLDatabase { +public class H2Provider extends AbstractSQLDatabase { private Object connectionPool; private Method disposeMethod; private Method getConnectionMethod; - public H2Impl(CustomNameplatesPlugin plugin) { + public H2Provider(CustomNameplates plugin) { super(plugin); } @@ -45,13 +27,12 @@ public class H2Impl extends AbstractSQLDatabase { * Initialize the H2 database and connection pool based on the configuration. */ @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); + public void initialize(YamlDocument config) { File databaseFile = new File(plugin.getDataFolder(), config.getString("H2.file", "data.db")); super.tablePrefix = config.getString("H2.table-prefix", "nameplates"); final String url = String.format("jdbc:h2:%s", databaseFile.getAbsolutePath()); - ClassLoader classLoader = ((CustomNameplatesPluginImpl) plugin).getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER)); + ClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.H2_DRIVER)); try { Class connectionClass = classLoader.loadClass("org.h2.jdbcx.JdbcConnectionPool"); Method createPoolMethod = connectionClass.getMethod("create", String.class, String.class, String.class); @@ -65,9 +46,6 @@ public class H2Impl extends AbstractSQLDatabase { super.createTableIfNotExist(); } - /** - * Disable the H2 database by disposing of the connection pool. - */ @Override public void disable() { if (connectionPool != null) { diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/MariaDBProvider.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/MariaDBProvider.java new file mode 100644 index 0000000..1324c63 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/MariaDBProvider.java @@ -0,0 +1,16 @@ +package net.momirealms.customnameplates.backend.storage.method.database.sql; + +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; + +public class MariaDBProvider extends AbstractHikariDatabase { + + public MariaDBProvider(CustomNameplates plugin) { + super(plugin); + } + + @Override + public StorageType getStorageType() { + return StorageType.MariaDB; + } +} diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/MySQLProvider.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/MySQLProvider.java new file mode 100644 index 0000000..cd3d030 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/MySQLProvider.java @@ -0,0 +1,16 @@ +package net.momirealms.customnameplates.backend.storage.method.database.sql; + +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; + +public class MySQLProvider extends AbstractHikariDatabase { + + public MySQLProvider(CustomNameplates plugin) { + super(plugin); + } + + @Override + public StorageType getStorageType() { + return StorageType.MySQL; + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/SQLiteImpl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/SQLiteProvider.java similarity index 60% rename from paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/SQLiteImpl.java rename to backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/SQLiteProvider.java index eacb229..1b8f320 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/SQLiteImpl.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/database/sql/SQLiteProvider.java @@ -1,30 +1,10 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +package net.momirealms.customnameplates.backend.storage.method.database.sql; -package net.momirealms.customnameplates.paper.storage.method.database.sql; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.libraries.dependencies.Dependency; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.common.dependency.Dependency; import java.io.File; import java.lang.reflect.Constructor; @@ -37,26 +17,24 @@ import java.util.Optional; import java.util.Properties; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; -/** - * An implementation of AbstractSQLDatabase that uses the SQLite database for player data storage. - */ -public class SQLiteImpl extends AbstractSQLDatabase { +public class SQLiteProvider extends AbstractSQLDatabase { private Connection connection; private File databaseFile; private Constructor connectionConstructor; + private ExecutorService executor; - public SQLiteImpl(CustomNameplatesPlugin plugin) { + public SQLiteProvider(CustomNameplates plugin) { super(plugin); } - /** - * Initialize the SQLite database and connection based on the configuration. - */ @Override - public void initialize() { - ClassLoader classLoader = ((CustomNameplatesPluginImpl) plugin).getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.SQLITE_DRIVER, Dependency.SLF4J_SIMPLE, Dependency.SLF4J_API)); + public void initialize(YamlDocument config) { + ClassLoader classLoader = plugin.getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.SQLITE_DRIVER, Dependency.SLF4J_SIMPLE, Dependency.SLF4J_API)); try { Class connectionClass = classLoader.loadClass("org.sqlite.jdbc4.JDBC4Connection"); connectionConstructor = connectionClass.getConstructor(String.class, String.class, Properties.class); @@ -64,17 +42,17 @@ public class SQLiteImpl extends AbstractSQLDatabase { throw new RuntimeException(e); } - YamlConfiguration config = plugin.getConfig("database.yml"); + this.executor = Executors.newFixedThreadPool(1); this.databaseFile = new File(plugin.getDataFolder(), config.getString("SQLite.file", "data") + ".db"); - super.tablePrefix = config.getString("SQLite.table-prefix", "nameplates"); + super.tablePrefix = config.getString("SQLite.table-prefix", "customfishing"); super.createTableIfNotExist(); } - /** - * Disable the SQLite database by closing the connection. - */ @Override public void disable() { + if (executor != null) { + executor.shutdown(); + } try { if (connection != null && !connection.isClosed()) connection.close(); @@ -83,68 +61,6 @@ public class SQLiteImpl extends AbstractSQLDatabase { } } - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data"))) - ) { - statement.setBytes(1, plugin.getStorageManager().toBytes(playerData)); - statement.setString(2, uuid.toString()); - statement.executeUpdate(); - future.complete(true); - } catch (SQLException e) { - LogUtils.warn("Failed to update " + uuid + "'s data.", e); - future.completeExceptionally(e); - } - }); - return future; - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) - ) { - statement.setString(1, uuid.toString()); - ResultSet rs = statement.executeQuery(); - if (rs.next()) { - byte[] dataByteArray = rs.getBytes("data"); - future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray))); - } else if (Bukkit.getPlayer(uuid) != null) { - var data = PlayerData.empty(); - this.insertPlayerData(uuid, data); - future.complete(Optional.of(data)); - } else { - future.complete(Optional.empty()); - } - } catch (SQLException e) { - LogUtils.warn("Failed to get " + uuid + "'s data.", e); - future.completeExceptionally(e); - } - }); - return future; - } - - @Override - public void insertPlayerData(UUID uuid, PlayerData playerData) { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_INSERT_DATA_BY_UUID, getTableName("data"))) - ) { - statement.setString(1, uuid.toString()); - statement.setBytes(2, plugin.getStorageManager().toBytes(playerData)); - statement.execute(); - } catch (SQLException e) { - LogUtils.warn("Failed to insert " + uuid + "'s data.", e); - } - } - @Override public StorageType getStorageType() { return StorageType.SQLite; @@ -175,4 +91,68 @@ public class SQLiteImpl extends AbstractSQLDatabase { throw new RuntimeException(e); } } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + CompletableFuture> future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) + ) { + statement.setString(1, uuid.toString()); + ResultSet rs = statement.executeQuery(); + if (rs.next()) { + byte[] dataByteArray = rs.getBytes("data"); + future.complete(Optional.of(plugin.getStorageManager().fromBytes(uuid, dataByteArray))); + } else if (plugin.getPlayer(uuid) != null) { + var data = PlayerData.empty(uuid); + this.insertPlayerData(uuid, data); + future.complete(Optional.of(data)); + } else { + future.complete(Optional.empty()); + } + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to get " + uuid + "'s data.", e); + future.complete(Optional.empty()); + } + }); + return future; + } + + public void insertPlayerData(UUID uuid, PlayerData playerData) { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_INSERT_DATA_BY_UUID, getTableName("data"))) + ) { + statement.setString(1, uuid.toString()); + statement.setBytes(2, plugin.getStorageManager().toBytes(playerData)); + statement.execute(); + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to insert " + uuid + "'s data.", e); + } + } + + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + CompletableFuture future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try ( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data"))) + ) { + statement.setBytes(1, plugin.getStorageManager().toBytes(playerData)); + statement.setString(2, playerData.uuid().toString()); + statement.executeUpdate(); + future.complete(true); + } catch (SQLException e) { + plugin.getPluginLogger().warn("Failed to update " + playerData.uuid() + "'s data.", e); + future.complete(false); + } + }); + return future; + } + } \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/file/JsonImpl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/file/JsonProvider.java similarity index 62% rename from paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/file/JsonImpl.java rename to backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/file/JsonProvider.java index 2ba1f20..7032137 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/file/JsonImpl.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/file/JsonProvider.java @@ -1,28 +1,11 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method.file; +package net.momirealms.customnameplates.backend.storage.method.file; import com.google.gson.Gson; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.storage.method.AbstractStorage; -import org.bukkit.Bukkit; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.JsonData; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.backend.storage.method.AbstractStorage; import java.io.File; import java.io.FileInputStream; @@ -34,14 +17,15 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * A data storage implementation that uses JSON files to store player data. */ -public class JsonImpl extends AbstractStorage { +public class JsonProvider extends AbstractStorage { @SuppressWarnings("ResultOfMethodCallIgnored") - public JsonImpl(CustomNameplatesPluginImpl plugin) { + public JsonProvider(CustomNameplates plugin) { super(plugin); File folder = new File(plugin.getDataFolder(), "data"); if (!folder.exists()) folder.mkdirs(); @@ -53,17 +37,37 @@ public class JsonImpl extends AbstractStorage { } @Override - public CompletableFuture> getPlayerData(UUID uuid) { - File file = getPlayerDataFile(uuid); - PlayerData playerData; - if (file.exists()) { - playerData = readFromJsonFile(file, PlayerData.class); - } else if (Bukkit.getPlayer(uuid) != null) { - playerData = PlayerData.empty(); - } else { - playerData = null; - } - return CompletableFuture.completedFuture(Optional.ofNullable(playerData)); + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + CompletableFuture> future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + File file = getPlayerDataFile(uuid); + PlayerData playerData; + if (file.exists()) { + playerData = readFromJsonFile(file, JsonData.class).toPlayerData(uuid); + } else if (plugin.getPlayer(uuid) != null) { + playerData = PlayerData.empty(uuid); + } else { + playerData = null; + } + future.complete(Optional.ofNullable(playerData)); + }); + return future; + } + + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + CompletableFuture future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try { + this.saveToJsonFile(playerData.toGsonData(), getPlayerDataFile(playerData.uuid())); + } catch (Exception e) { + future.complete(false); + } + future.complete(true); + }); + return future; } /** @@ -87,7 +91,7 @@ public class JsonImpl extends AbstractStorage { try (FileWriter file = new FileWriter(filepath)) { gson.toJson(obj, file); } catch (IOException e) { - e.printStackTrace(); + throw new RuntimeException(e); } } @@ -117,20 +121,14 @@ public class JsonImpl extends AbstractStorage { try (FileInputStream fis = new FileInputStream(file)) { fis.read(fileBytes); } catch (IOException e) { - e.printStackTrace(); + throw new RuntimeException(e); } return fileBytes; } - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - this.saveToJsonFile(playerData, getPlayerDataFile(uuid)); - return CompletableFuture.completedFuture(true); - } - // Retrieve a set of unique user UUIDs based on JSON data files in the 'data' folder. @Override - public Set getUniqueUsers(boolean legacy) { + public Set getUniqueUsers() { // No legacy files File folder = new File(plugin.getDataFolder(), "data"); Set uuids = new HashSet<>(); diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/file/YAMLProvider.java b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/file/YAMLProvider.java new file mode 100644 index 0000000..6aa83c3 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/storage/method/file/YAMLProvider.java @@ -0,0 +1,105 @@ +package net.momirealms.customnameplates.backend.storage.method.file; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.storage.StorageType; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.backend.storage.method.AbstractStorage; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class YAMLProvider extends AbstractStorage { + + @SuppressWarnings("ResultOfMethodCallIgnored") + public YAMLProvider(CustomNameplates plugin) { + super(plugin); + File folder = new File(plugin.getDataFolder(), "data"); + if (!folder.exists()) folder.mkdirs(); + } + + @Override + public StorageType getStorageType() { + return StorageType.YAML; + } + + /** + * Get the file associated with a player's UUID for storing YAML data. + * + * @param uuid The UUID of the player. + * @return The file for the player's data. + */ + public File getPlayerDataFile(UUID uuid) { + return new File(plugin.getDataFolder(), "data" + File.separator + uuid + ".yml"); + } + + @Override + public CompletableFuture> getPlayerData(UUID uuid, Executor executor) { + CompletableFuture> future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + File dataFile = getPlayerDataFile(uuid); + if (!dataFile.exists()) { + if (plugin.getPlayer(uuid) != null) { + future.complete(Optional.of(PlayerData.empty(uuid))); + } else { + future.complete(Optional.empty()); + } + return; + } + YamlDocument data = plugin.getConfigManager().loadData(dataFile); + PlayerData playerData = PlayerData.builder() + .uuid(uuid) + .nameplate(data.getString("nameplate", "none")) + .bubble(data.getString("bubble", "none")) + .build(); + future.complete(Optional.of(playerData)); + }); + return future; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public CompletableFuture updatePlayerData(PlayerData playerData, Executor executor) { + CompletableFuture future = new CompletableFuture<>(); + if (executor == null) executor = plugin.getScheduler().async(); + executor.execute(() -> { + try { + File dataFile = getPlayerDataFile(playerData.uuid()); + if (!dataFile.exists()) { + dataFile.getParentFile().mkdirs(); + dataFile.createNewFile(); + } + YamlDocument data = plugin.getConfigManager().loadData(dataFile); + data.set("bubble", playerData.bubble()); + data.set("nameplate", playerData.nameplate()); + data.save(dataFile); + future.complete(true); + } catch (IOException e) { + future.complete(false); + } + }); + return future; + } + + @Override + public Set getUniqueUsers() { + File folder = new File(plugin.getDataFolder(), "data"); + Set uuids = new HashSet<>(); + if (folder.exists()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + uuids.add(UUID.fromString(file.getName().substring(0, file.getName().length() - 4))); + } + } + } + return uuids; + } +} diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.fsh b/backend/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.fsh similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.fsh rename to backend/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.fsh diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.json b/backend/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.json similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.json rename to backend/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.json diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.vsh b/backend/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.vsh similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.vsh rename to backend/src/main/resources/ResourcePack/assets/minecraft/shaders/core/rendertype_text.vsh diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/bars.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/bars.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/bars.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/bars.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/blue_progress.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/green_progress.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/pink_progress.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/purple_progress.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/red_progress.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/white_progress.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_background.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_background.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_background.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_background.png diff --git a/paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_progress.png b/backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_progress.png similarity index 100% rename from paper/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_progress.png rename to backend/src/main/resources/ResourcePack/assets/minecraft/textures/gui/sprites/boss_bar/yellow_progress.png diff --git a/paper/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.fsh b/backend/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.fsh similarity index 100% rename from paper/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.fsh rename to backend/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.fsh diff --git a/paper/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.json b/backend/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.json similarity index 100% rename from paper/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.json rename to backend/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.json diff --git a/paper/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.vsh b/backend/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.vsh similarity index 100% rename from paper/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.vsh rename to backend/src/main/resources/ResourcePack/overlay_1_20_5/assets/minecraft/shaders/core/rendertype_text.vsh diff --git a/paper/src/main/resources/ResourcePack/pack.mcmeta b/backend/src/main/resources/ResourcePack/pack.mcmeta similarity index 100% rename from paper/src/main/resources/ResourcePack/pack.mcmeta rename to backend/src/main/resources/ResourcePack/pack.mcmeta diff --git a/backend/src/main/resources/ResourcePack/pack.png b/backend/src/main/resources/ResourcePack/pack.png new file mode 100644 index 0000000..3748cf7 Binary files /dev/null and b/backend/src/main/resources/ResourcePack/pack.png differ diff --git a/paper/src/main/resources/ResourcePack/pack_1_20_5.mcmeta b/backend/src/main/resources/ResourcePack/pack_1_20_5.mcmeta similarity index 100% rename from paper/src/main/resources/ResourcePack/pack_1_20_5.mcmeta rename to backend/src/main/resources/ResourcePack/pack_1_20_5.mcmeta diff --git a/backend/src/main/resources/config.yml b/backend/src/main/resources/config.yml new file mode 100644 index 0000000..7139595 --- /dev/null +++ b/backend/src/main/resources/config.yml @@ -0,0 +1,213 @@ +# Do not modify this value +config-version: '${config_version}' +# Enables or disables debug mode +debug: false +# Enables or disables metrics collection via BStats +metrics: true +# Enables automatic update checks +update-checker: true +# Forces a specific locale (e.g., zh_cn) +force-locale: '' +# Module Settings +# Modifying these modules may result in distorted images. If this occurs, reinstall the newly generated resource pack. +modules: + nameplates: true + backgrounds: true + bubbles: true + bossbars: true + actionbars: true + images: true +# Plugin Integrations +integrations: + # Should Nameplates merge resource packs with these plugins during reload? + resource-pack: + ItemsAdder: false + ItemsAdder-old-method: false + Oraxen: false + # Some chats, such as staff chats or menu inputs, may need to be excluded from capture. + # Supported chat plugins are listed below, which allow blacklist channels and /ignore functionality to work correctly. + chat: + TrChat: false + VentureChat: false + HuskChat: false + CarbonChat: false + AdvancedChat: false + Essentials: false +# Resource Pack Generation Settings +resource-pack: + # Disables resource pack generation at server startup + disable-generation-on-start: false + # Namespace identifier + namespace: "nameplates" + # Font selection + font: "default" + # The starting character for custom fonts + # This character is used, but it won't affect normal Korean characters during chat, as they belong to the "minecraft:default" font. + initial-char: '뀁' + # Specify directories for PNG file generation. + # This helps maintain an organized resource pack structure. + image-path: + nameplates: 'font/nameplates/' + backgrounds: 'font/backgrounds/' + images: 'font/images/' + bubbles: 'font/bubbles/' + space-split: 'font/base/' + # Shader Settings + shader: + # Enables shader generation + enable: true + # Hides scoreboard numbers + hide-scoreboard-number: false + # Enables support for animated text shaders + animated-text: false + # Enables ItemsAdder text effect support + ItemsAdder-text-effects: false + # Bossbar transparency settings + transparent-bossbar: + # Specify the color of the bossbar to hide + color: YELLOW + # Generate transparent bossbars for Minecraft 1.20.2+ + "1_20_2+": true + # Generate transparent bossbars for Minecraft 1.17-1.20.1 + "1_17-1_20_1": true + # Legacy Unicode support for Minecraft 1.20+ clients, as these images were removed in 1.20, affecting the creation of decent texts. + # Enabling this increases your resource pack size by approximately 900KB. + legacy-unicodes: true +# Additional Settings +other-settings: + # It is recommended to use the MiniMessage format. If you prefer legacy color codes using "&", enable this support. + # Disabling it will improve color formatting performance. + legacy-color-code-support: true + # Set a delay for actionbar/bossbar to prevent compatibility issues + send-delay: 0 + # Determines whether CustomNameplates should capture actionbars from other plugins + catch-other-plugin-actionbar: true + # Should the plugin listen for chat canceled events by unknown chat plugins? + unsafe-chat-event: false + # Set the default placeholder refresh interval + default-placeholder-refresh-interval: 1 + # Set the refresh interval for better performance in ticks (Especially for those heavy placeholders) + placeholder-refresh-interval: + "%player_name%": 100 + "%vault_prefix%": 1 + # CustomNameplates provides some templates for reading the default Minecraft fonts + # You can replace the files under /font or add new configuration to let the plugin get these templates. + # Templates can be used in advance-data.yml + font-templates: + space: + space: + " ": 4 + "\\u200c": 0 + unihex: + unifont: + file: unifont.zip + generate: false + size_overrides: + - from: "\\u3001" + to: "\\u30FF" + left: 0 + right: 15 + - from: "\\u3200" + to: "\\u9FFF" + left: 0 + right: 15 + - from: "\\u1100" + to: "\\u11FF" + left: 0 + right: 15 + - from: "\\u3130" + to: "\\u318F" + left: 0 + right: 15 + - from: "\\uA960" + to: "\\uA97F" + left: 0 + right: 15 + - from: "\\uD7B0" + to: "\\uD7FF" + left: 0 + right: 15 + - from: "\\uAC00" + to: "\\uD7AF" + left: 1 + right: 15 + - from: "\\uF900" + to: "\\uFAFF" + left: 0 + right: 15 + - from: "\\uFF01" + to: "\\uFF5E" + left: 0 + right: 15 + unifont_jp: + file: unifont_jp.zip + generate: false + filter: + "jp": true + size_overrides: + - from: "\\u3200" + to: "\\u9FFF" + left: 0 + right: 15 + - from: "\\uF900" + to: "\\uFAFF" + left: 0 + right: 15 + bitmap: + ascii: + codepoints: ascii + file: ascii.png + height: 8 + custom: false # Is this a non-default Minecraft font? If enabled, plugin will create a new font image in the generated resource pack + ascii_sga: + codepoints: ascii_sga + file: ascii_sga.png + height: 8 + custom: false + asciillager: + codepoints: asciillager + file: asciillager.png + height: 8 + custom: false + nonlatin_european: + codepoints: nonlatin_european + file: nonlatin_european.png + height: 8 + custom: false + accented: + codepoints: accented + file: accented.png + height: 12 + custom: false + legacy_unicode: + legacy_unicode: + file: unicode_page_%02x.png + height: 8 + custom: false + unicode: + unicode: + file: unicode_page_%02x.png + sizes: glyph_sizes.bin + ttf: + example: + generate: false + file: example.ttf + size: 10.0 + oversample: 8.0 + skip: [] + # If the font is a bitmap font, the number represents the ascent + # If the font is a ttf font, the number represents the Y shift + shift-fonts: + shift_0: + - space + - nonlatin_european:7 + - accented:9 + - ascii:7 + - unifont_jp + - unifont + shift_1: + - space + - nonlatin_european:3 + - accented:6 + - ascii:3 + - legacy_unicode:3 \ No newline at end of file diff --git a/backend/src/main/resources/configs/actionbar.yml b/backend/src/main/resources/configs/actionbar.yml new file mode 100644 index 0000000..2224f63 --- /dev/null +++ b/backend/src/main/resources/configs/actionbar.yml @@ -0,0 +1,24 @@ +# Important: Players can only see one actionbar at any given moment, no overlap allowed! +actionbar: + # Players need this permission to display the actionbar, the conditions can be customized + conditions: + condition_1: + type: permission + refresh-interval: 20 # Refresh rate, in ticks, to recheck the permission status + value: + - actionbar.show + text-display-order: + 1: + # Duration of the text display, measured in ticks (20 ticks = 1 second) + # -1 means the text will stay on the screen forever! + duration: -1 + # The message to be shown in the actionbar + text: '%np_conditional_actionbar%' + # Optional settings for finer control: + # When enabling conditions, make sure at least one valid actionbar is displayed to the player, + # or it may lead to an endless loop with nothing visible. + # If the player doesn't meet the condition, this actionbar will simply be skipped. + # The condition is only checked once per cycle, until the next time it comes around. + conditions: {} + #2: ... + #3: ... \ No newline at end of file diff --git a/paper/src/main/resources/configs/font-width-data.yml b/backend/src/main/resources/configs/advance-data.yml similarity index 60% rename from paper/src/main/resources/configs/font-width-data.yml rename to backend/src/main/resources/configs/advance-data.yml index 2740277..d08d829 100644 --- a/paper/src/main/resources/configs/font-width-data.yml +++ b/backend/src/main/resources/configs/advance-data.yml @@ -1,48 +1,52 @@ # namespace:font minecraft:default: - # default width - default: 8 - # available font data template: unifont/ascii/nonlatin_european/accented/unicode(legacy) + # default advance + default: 9.0 + # Sets the loading sequence of the templates template-loading-sequence: - unifont - nonlatin_european - accented - ascii + - space # The values here would override the same character in the template values: - 默: 8 + 默: 9.0 minecraft:uniform: - default: 8 + default: 9.0 template-loading-sequence: - unifont + - space minecraft:alt: - default: 8 + default: 9.0 template-loading-sequence: - ascii_sga + - space minecraft:asciillager: - default: 8 + default: 9.0 template-loading-sequence: - asciillager + - space customcrops:default: - default: 8 + default: 9.0 template-loading-sequence: [] values: - 뀁: -2 - 뀂: 4 - 뀃: 6 - 뀄: 6 - 뀅: 4 + 뀁: -1.0 + 뀂: 5.0 + 뀃: 7.0 + 뀄: 7.0 + 뀅: 5.0 minecraft:customcrops: - default: 8 + default: 9.0 template-loading-sequence: [] values: - 뀁: -2 - 뀂: 4 - 뀃: 6 - 뀄: 6 - 뀅: 4 \ No newline at end of file + 뀁: -1.0 + 뀂: 5.0 + 뀃: 7.0 + 뀄: 7.0 + 뀅: 5.0 \ No newline at end of file diff --git a/backend/src/main/resources/configs/bossbar.yml b/backend/src/main/resources/configs/bossbar.yml new file mode 100644 index 0000000..eb97057 --- /dev/null +++ b/backend/src/main/resources/configs/bossbar.yml @@ -0,0 +1,39 @@ +# You can create as many bossbar sections as you'd like +bossbar_1: + # Choose the color of the bossbar (Options: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW) + color: YELLOW + # Decide how the progress bar looks (Options: "progress", "notched_6", "notched_10", "notched_12", "notched_20") + overlay: PROGRESS + conditions: + condition_1: + type: permission + refresh-interval: 20 # How often, in ticks, to check if the player still has the required permission + value: + - bossbar.show + # The bossbar will cycle through the text in this exact order: + text-display-order: + 1: + # Time (in ticks) this text stays on the screen (100 ticks = 5 seconds) + duration: 100 + # What message to display in the bossbar + text: '%np_background_hello%' + 2: + # Display for 10 seconds + duration: 200 + text: '%np_background_time% %np_background_location% %np_background_weather%' + # How often to refresh this text, in ticks (1 tick = 1/20 of a second) + refresh-frequency: 1 + 3: + # Show for 5 seconds + duration: 100 + text: '%np_background_update%' + # Optional extra control: + # Make sure at least one bossbar message is shown, or else it could result in an endless cycle with nothing visible! + # If the player doesn’t meet the condition, this message will be skipped. + # Conditions are only checked once per cycle until it comes back around. + conditions: + permission: nameplates.admin # Only players with this permission will see this specific message + equals: + # Compare two values and act based on the result + value1: '%np_is-latest%' + value2: 'false' # Only display if the value is "false" \ No newline at end of file diff --git a/backend/src/main/resources/configs/bubble.yml b/backend/src/main/resources/configs/bubble.yml new file mode 100644 index 0000000..1e83365 --- /dev/null +++ b/backend/src/main/resources/configs/bubble.yml @@ -0,0 +1,44 @@ +# Requirements for sending the bubble +sender-requirements: + permission: bubbles.send + '!gamemode': spectator + potion-effect: "INVISIBILITY<0" + +viewer-requirements: + permission: bubbles.see + +# blacklist channels +blacklist-channels: + - Private + - Staff + +# ALL: all the players can see the bubble +# JOINED: players in the same channel can see each other's bubble +# CAN_JOIN: players that have permission to certain channels would see the bubble in that channel +channel-mode: ALL + +# Default bubble to display if player's bubble is "none" +default-bubble: 'chat' + +# Y offset from the highest nameplate +y-offset: 0.1 + +view-range: 0.5 + +# Bubble display time (in ticks) +stay-duration: 150 +appear-duration: 4 +disappear-duration: 2 + +bubble-settings: + chat: + display-name: "White Bubbles" + max-lines: 3 + lines: + 1: chat_1 + 2: chat_2 + 3: chat_3 + line-width: 150 + background-color: 0,0,0,0 + text-prefix: "" + text-suffix: "" \ No newline at end of file diff --git a/paper/src/main/resources/configs/custom-placeholders.yml b/backend/src/main/resources/configs/custom-placeholders.yml similarity index 64% rename from paper/src/main/resources/configs/custom-placeholders.yml rename to backend/src/main/resources/configs/custom-placeholders.yml index 806464a..a185913 100644 --- a/paper/src/main/resources/configs/custom-placeholders.yml +++ b/backend/src/main/resources/configs/custom-placeholders.yml @@ -2,7 +2,7 @@ conditional-text: actionbar: priority_1: - text: '%nameplates_background_other_actionbar%' + text: '%np_background_other_actionbar%' conditions: '||': '!=': @@ -10,13 +10,13 @@ conditional-text: value2: "300" '!gamemode': survival priority_2: - text: '%nameplates_static_money_hud%%nameplates_offset_-180%%nameplates_static_other_actionbar%' + text: '%np_static_money_hud%%np_offset_-180%%np_static_other_actionbar%' conditions: '!equals': - value1: '%nameplates_actionbar%' + value1: '%np_actionbar%' value2: "" priority_3: - text: '%nameplates_static_money_hud%' + text: '%np_static_money_hud%' weather: priority_1: text: 'Sunny' @@ -44,77 +44,73 @@ nameplate-text: background-text: location: background: bedrock_1 - text: '%nameplates_image_compass% %nameplates_descent_location%' + text: '%np_image_compass% %np_shift_location%' remove-shadow: true time: background: bedrock_1 - text: '%nameplates_image_clock% %nameplates_descent_time%' + text: '%np_image_clock% %np_shift_time%' remove-shadow: true weather: background: bedrock_1 - text: '%nameplates_image_weather% %nameplates_descent_weather%' + text: '%np_image_weather% %np_shift_weather%' remove-shadow: true hello: background: bedrock_1 - text: '%nameplates_image_bubble% %nameplates_descent_hello%' + text: '%np_image_bubble% %np_shift_hello%' remove-shadow: true update: background: bedrock_1 - text: '%nameplates_image_bell% %nameplates_descent_update%' + text: '%np_image_bell% %np_shift_update%' remove-shadow: true other_actionbar: background: bedrock_2 - text: '%nameplates_actionbar%' + text: '%np_actionbar%' remove-shadow: true # https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/custom-placeholders/static-text static-text: money_hud: position: right - text: '%nameplates_image_coin% %nameplates_descent_money%' + text: '%np_image_coin% %np_shift_money%' value: 180 other_actionbar: position: middle - text: "%nameplates_background_other_actionbar%" + text: "%np_background_other_actionbar%" value: 180 # https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/custom-placeholders/descent-text -descent-text: +shift-text: + player_name: + text: "%player_name%" + font: shift_0 location: - text: "Your Location: %player_x%, %player_y%, %player_z%" - descent: 5 - is-unicode: false + text: "Your Location: %np_switch_world% (%player_x%,%player_y%,%player_z%)" + font: shift_1 time: - text: "Time: %nameplates_time%" - descent: 5 - is-unicode: false + text: "Time: %np_time%" + font: shift_1 weather: - text: "Weather: %nameplates_conditional_weather%" - descent: 5 - is-unicode: false + text: "Weather: %np_conditional_weather%" + font: shift_1 update: text: "A newer version of CustomNameplates is available!" - descent: 5 - is-unicode: false + font: shift_1 money: text: "%vault_eco_balance%" - descent: 23 - is-unicode: false + font: shift_1 hello: text: "Hello 여보세요 你好 こんにちは, Thanks for using CustomNameplates" - descent: 5 - is-unicode: true + font: shift_1 # https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/custom-placeholders/switch-text switch-text: - season: - switch: '%customcrops_season%' + world: + switch: '%player_world%' case: - 'Spring': 'Spring' - 'Summer': 'Summer' - 'Autumn': 'Autumn' - 'Winter': 'Winter' - default: 'Invalid season' + 'world': "Overworld" + 'world_nether': "The Nether" + 'world_the_end': "The End" + default: "Unknown world" # https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/custom-placeholders/vanilla-hud vanilla-hud: diff --git a/backend/src/main/resources/configs/nameplate.yml b/backend/src/main/resources/configs/nameplate.yml new file mode 100644 index 0000000..4e7960d --- /dev/null +++ b/backend/src/main/resources/configs/nameplate.yml @@ -0,0 +1,67 @@ +# Duration (in seconds) for which the nameplate preview will be displayed. +preview-duration: 5 + +# Default nameplate shown when a player's nameplate is set to "none." +default-nameplate: 'none' + +# Whether to make the nameplate always visible to the player +always-show: false + +# Configuration for nameplate behavior and appearance. +nameplate: + # Prefix to be displayed before the player's name. + # The prefix here will become part of the nameplate + prefix: '' + # Placeholder for the player's name + # The default configuration uses shift to ensure that the player name not affected by the clientside `force-unicode-font` setting. + player-name: '%np_shift_player_name%' + # Suffix to be displayed after the player's name. + # The suffix here will become part of the nameplate + suffix: '' + +# Configuration for Unlimited tags. +unlimited: + tag_1: + text: '%np_tag-image%' + translation: 0,0.2,0 + viewer-conditions: { } + owner-conditions: + condition_potion: + type: potion-effect + value: "INVISIBILITY<0" + refresh-interval: 1 + self-disguised: false + affected-by-crouching: true + affected-by-scale-attribute: true + line-width: 1024 + background-color: 0,0,0,0 + tag_2: + text: '%np_tag-text%' + translation: 0.001,0.2,0.001 + viewer-conditions: { } + owner-conditions: + has-nameplate: true + condition_potion: + type: potion-effect + value: "INVISIBILITY<0" + refresh-interval: 1 + self-disguised: false + affected-by-crouching: true + affected-by-scale-attribute: true + line-width: 1024 + background-color: 0,0,0,0 + tag_3: + text: '%np_tag-text%' + translation: 0,0.2,0 + viewer-conditions: { } + owner-conditions: + has-nameplate: false + condition_potion: + type: potion-effect + value: "INVISIBILITY<0" + refresh-interval: 1 + self-disguised: false + affected-by-crouching: true + affected-by-scale-attribute: true + line-width: 1024 + background-color: 64,0,0,0 \ No newline at end of file diff --git a/paper/src/main/resources/contents/backgrounds/b0.png b/backend/src/main/resources/contents/backgrounds/b0.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b0.png rename to backend/src/main/resources/contents/backgrounds/b0.png diff --git a/paper/src/main/resources/contents/backgrounds/b1.png b/backend/src/main/resources/contents/backgrounds/b1.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b1.png rename to backend/src/main/resources/contents/backgrounds/b1.png diff --git a/paper/src/main/resources/contents/backgrounds/b128.png b/backend/src/main/resources/contents/backgrounds/b128.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b128.png rename to backend/src/main/resources/contents/backgrounds/b128.png diff --git a/paper/src/main/resources/contents/backgrounds/b16.png b/backend/src/main/resources/contents/backgrounds/b16.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b16.png rename to backend/src/main/resources/contents/backgrounds/b16.png diff --git a/paper/src/main/resources/contents/backgrounds/b2.png b/backend/src/main/resources/contents/backgrounds/b2.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b2.png rename to backend/src/main/resources/contents/backgrounds/b2.png diff --git a/paper/src/main/resources/contents/backgrounds/b32.png b/backend/src/main/resources/contents/backgrounds/b32.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b32.png rename to backend/src/main/resources/contents/backgrounds/b32.png diff --git a/paper/src/main/resources/contents/backgrounds/b4.png b/backend/src/main/resources/contents/backgrounds/b4.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b4.png rename to backend/src/main/resources/contents/backgrounds/b4.png diff --git a/paper/src/main/resources/contents/backgrounds/b64.png b/backend/src/main/resources/contents/backgrounds/b64.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b64.png rename to backend/src/main/resources/contents/backgrounds/b64.png diff --git a/paper/src/main/resources/contents/backgrounds/b8.png b/backend/src/main/resources/contents/backgrounds/b8.png similarity index 100% rename from paper/src/main/resources/contents/backgrounds/b8.png rename to backend/src/main/resources/contents/backgrounds/b8.png diff --git a/paper/src/main/resources/contents/backgrounds/bedrock_1.yml b/backend/src/main/resources/contents/backgrounds/bedrock_1.yml similarity index 78% rename from paper/src/main/resources/contents/backgrounds/bedrock_1.yml rename to backend/src/main/resources/contents/backgrounds/bedrock_1.yml index 75a8212..304e825 100644 --- a/paper/src/main/resources/contents/backgrounds/bedrock_1.yml +++ b/backend/src/main/resources/contents/backgrounds/bedrock_1.yml @@ -1,17 +1,12 @@ -left-margin: 1 -right-margin: 1 - left: image: b0 height: 14 ascent: 7 - width: 1 right: image: b0 height: 14 ascent: 7 - width: 1 middle: height: 14 diff --git a/paper/src/main/resources/contents/backgrounds/bedrock_2.yml b/backend/src/main/resources/contents/backgrounds/bedrock_2.yml similarity index 78% rename from paper/src/main/resources/contents/backgrounds/bedrock_2.yml rename to backend/src/main/resources/contents/backgrounds/bedrock_2.yml index 2cdfd1c..cdc9017 100644 --- a/paper/src/main/resources/contents/backgrounds/bedrock_2.yml +++ b/backend/src/main/resources/contents/backgrounds/bedrock_2.yml @@ -1,17 +1,12 @@ -left-margin: 1 -right-margin: 1 - left: image: b0 height: 14 ascent: 12 - width: 1 right: image: b0 height: 14 ascent: 12 - width: 1 middle: height: 14 diff --git a/backend/src/main/resources/contents/bubbles/chat_1.yml b/backend/src/main/resources/contents/bubbles/chat_1.yml new file mode 100644 index 0000000..0a44c2d --- /dev/null +++ b/backend/src/main/resources/contents/bubbles/chat_1.yml @@ -0,0 +1,16 @@ +left: + image: chat_1_left + height: 13 + ascent: 9 +middle: + image: chat_1_middle + height: 13 + ascent: 9 +right: + image: chat_1_right + height: 13 + ascent: 9 +tail: + image: chat_1_tail + height: 13 + ascent: 9 \ No newline at end of file diff --git a/paper/src/main/resources/contents/bubbles/chat_left.png b/backend/src/main/resources/contents/bubbles/chat_1_left.png similarity index 100% rename from paper/src/main/resources/contents/bubbles/chat_left.png rename to backend/src/main/resources/contents/bubbles/chat_1_left.png diff --git a/paper/src/main/resources/contents/bubbles/chat_middle.png b/backend/src/main/resources/contents/bubbles/chat_1_middle.png similarity index 100% rename from paper/src/main/resources/contents/bubbles/chat_middle.png rename to backend/src/main/resources/contents/bubbles/chat_1_middle.png diff --git a/paper/src/main/resources/contents/bubbles/chat_right.png b/backend/src/main/resources/contents/bubbles/chat_1_right.png similarity index 100% rename from paper/src/main/resources/contents/bubbles/chat_right.png rename to backend/src/main/resources/contents/bubbles/chat_1_right.png diff --git a/paper/src/main/resources/contents/bubbles/chat_tail.png b/backend/src/main/resources/contents/bubbles/chat_1_tail.png similarity index 100% rename from paper/src/main/resources/contents/bubbles/chat_tail.png rename to backend/src/main/resources/contents/bubbles/chat_1_tail.png diff --git a/backend/src/main/resources/contents/bubbles/chat_2.yml b/backend/src/main/resources/contents/bubbles/chat_2.yml new file mode 100644 index 0000000..61a5f8f --- /dev/null +++ b/backend/src/main/resources/contents/bubbles/chat_2.yml @@ -0,0 +1,16 @@ +left: + image: chat_2_left + height: 23 + ascent: 19 +middle: + image: chat_2_middle + height: 23 + ascent: 19 +right: + image: chat_2_right + height: 23 + ascent: 19 +tail: + image: chat_2_tail + height: 23 + ascent: 19 \ No newline at end of file diff --git a/backend/src/main/resources/contents/bubbles/chat_2_left.png b/backend/src/main/resources/contents/bubbles/chat_2_left.png new file mode 100644 index 0000000..4eb5014 Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_2_left.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_2_middle.png b/backend/src/main/resources/contents/bubbles/chat_2_middle.png new file mode 100644 index 0000000..faaf5ab Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_2_middle.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_2_right.png b/backend/src/main/resources/contents/bubbles/chat_2_right.png new file mode 100644 index 0000000..90eaeb0 Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_2_right.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_2_tail.png b/backend/src/main/resources/contents/bubbles/chat_2_tail.png new file mode 100644 index 0000000..87b9890 Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_2_tail.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_3.yml b/backend/src/main/resources/contents/bubbles/chat_3.yml new file mode 100644 index 0000000..bcfb617 --- /dev/null +++ b/backend/src/main/resources/contents/bubbles/chat_3.yml @@ -0,0 +1,16 @@ +left: + image: chat_3_left + height: 33 + ascent: 29 +middle: + image: chat_3_middle + height: 33 + ascent: 29 +right: + image: chat_3_right + height: 33 + ascent: 29 +tail: + image: chat_3_tail + height: 33 + ascent: 29 \ No newline at end of file diff --git a/backend/src/main/resources/contents/bubbles/chat_3_left.png b/backend/src/main/resources/contents/bubbles/chat_3_left.png new file mode 100644 index 0000000..91e170c Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_3_left.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_3_middle.png b/backend/src/main/resources/contents/bubbles/chat_3_middle.png new file mode 100644 index 0000000..54c3027 Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_3_middle.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_3_right.png b/backend/src/main/resources/contents/bubbles/chat_3_right.png new file mode 100644 index 0000000..323ef07 Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_3_right.png differ diff --git a/backend/src/main/resources/contents/bubbles/chat_3_tail.png b/backend/src/main/resources/contents/bubbles/chat_3_tail.png new file mode 100644 index 0000000..e1f03f3 Binary files /dev/null and b/backend/src/main/resources/contents/bubbles/chat_3_tail.png differ diff --git a/paper/src/main/resources/contents/images/bell.png b/backend/src/main/resources/contents/images/bell.png similarity index 100% rename from paper/src/main/resources/contents/images/bell.png rename to backend/src/main/resources/contents/images/bell.png diff --git a/backend/src/main/resources/contents/images/bell.yml b/backend/src/main/resources/contents/images/bell.yml new file mode 100644 index 0000000..3c56bfd --- /dev/null +++ b/backend/src/main/resources/contents/images/bell.yml @@ -0,0 +1,6 @@ +image: bell +height: 10 +ascent: 4 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/bubble.png b/backend/src/main/resources/contents/images/bubble.png similarity index 100% rename from paper/src/main/resources/contents/images/bubble.png rename to backend/src/main/resources/contents/images/bubble.png diff --git a/backend/src/main/resources/contents/images/bubble.yml b/backend/src/main/resources/contents/images/bubble.yml new file mode 100644 index 0000000..5f6dc46 --- /dev/null +++ b/backend/src/main/resources/contents/images/bubble.yml @@ -0,0 +1,6 @@ +image: bubble +height: 10 +ascent: 4 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/clock.png b/backend/src/main/resources/contents/images/clock.png similarity index 100% rename from paper/src/main/resources/contents/images/clock.png rename to backend/src/main/resources/contents/images/clock.png diff --git a/backend/src/main/resources/contents/images/clock.yml b/backend/src/main/resources/contents/images/clock.yml new file mode 100644 index 0000000..42a821c --- /dev/null +++ b/backend/src/main/resources/contents/images/clock.yml @@ -0,0 +1,6 @@ +image: clock +height: 10 +ascent: 4 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/coin.png b/backend/src/main/resources/contents/images/coin.png similarity index 100% rename from paper/src/main/resources/contents/images/coin.png rename to backend/src/main/resources/contents/images/coin.png diff --git a/backend/src/main/resources/contents/images/coin.yml b/backend/src/main/resources/contents/images/coin.yml new file mode 100644 index 0000000..848fd60 --- /dev/null +++ b/backend/src/main/resources/contents/images/coin.yml @@ -0,0 +1,6 @@ +image: coin +height: 10 +ascent: -14 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/compass.png b/backend/src/main/resources/contents/images/compass.png similarity index 100% rename from paper/src/main/resources/contents/images/compass.png rename to backend/src/main/resources/contents/images/compass.png diff --git a/backend/src/main/resources/contents/images/compass.yml b/backend/src/main/resources/contents/images/compass.yml new file mode 100644 index 0000000..c50db32 --- /dev/null +++ b/backend/src/main/resources/contents/images/compass.yml @@ -0,0 +1,6 @@ +image: compass +height: 10 +ascent: 4 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/stamina_0.png b/backend/src/main/resources/contents/images/stamina_0.png similarity index 100% rename from paper/src/main/resources/contents/images/stamina_0.png rename to backend/src/main/resources/contents/images/stamina_0.png diff --git a/paper/src/main/resources/contents/images/stamina_0.yml b/backend/src/main/resources/contents/images/stamina_0.yml similarity index 51% rename from paper/src/main/resources/contents/images/stamina_0.yml rename to backend/src/main/resources/contents/images/stamina_0.yml index d42f253..3335c3d 100644 --- a/paper/src/main/resources/contents/images/stamina_0.yml +++ b/backend/src/main/resources/contents/images/stamina_0.yml @@ -1,4 +1,6 @@ image: stamina_0 height: 9 ascent: -16 -width: 9 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/stamina_1.png b/backend/src/main/resources/contents/images/stamina_1.png similarity index 100% rename from paper/src/main/resources/contents/images/stamina_1.png rename to backend/src/main/resources/contents/images/stamina_1.png diff --git a/paper/src/main/resources/contents/images/stamina_1.yml b/backend/src/main/resources/contents/images/stamina_1.yml similarity index 51% rename from paper/src/main/resources/contents/images/stamina_1.yml rename to backend/src/main/resources/contents/images/stamina_1.yml index 54d67f5..0cfb24c 100644 --- a/paper/src/main/resources/contents/images/stamina_1.yml +++ b/backend/src/main/resources/contents/images/stamina_1.yml @@ -1,4 +1,6 @@ image: stamina_1 height: 9 ascent: -16 -width: 9 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/stamina_2.png b/backend/src/main/resources/contents/images/stamina_2.png similarity index 100% rename from paper/src/main/resources/contents/images/stamina_2.png rename to backend/src/main/resources/contents/images/stamina_2.png diff --git a/paper/src/main/resources/contents/images/stamina_2.yml b/backend/src/main/resources/contents/images/stamina_2.yml similarity index 51% rename from paper/src/main/resources/contents/images/stamina_2.yml rename to backend/src/main/resources/contents/images/stamina_2.yml index da57ae0..d2a9278 100644 --- a/paper/src/main/resources/contents/images/stamina_2.yml +++ b/backend/src/main/resources/contents/images/stamina_2.yml @@ -1,4 +1,6 @@ image: stamina_2 height: 9 ascent: -16 -width: 9 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/weather.png b/backend/src/main/resources/contents/images/weather.png similarity index 100% rename from paper/src/main/resources/contents/images/weather.png rename to backend/src/main/resources/contents/images/weather.png diff --git a/backend/src/main/resources/contents/images/weather.yml b/backend/src/main/resources/contents/images/weather.yml new file mode 100644 index 0000000..8597510 --- /dev/null +++ b/backend/src/main/resources/contents/images/weather.yml @@ -0,0 +1,6 @@ +image: weather +height: 10 +ascent: 4 +shadow: + remove: true + opacity: 254 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/cat.yml b/backend/src/main/resources/contents/nameplates/cat.yml similarity index 71% rename from paper/src/main/resources/contents/nameplates/cat.yml rename to backend/src/main/resources/contents/nameplates/cat.yml index d5ebbe7..14c3d0a 100644 --- a/paper/src/main/resources/contents/nameplates/cat.yml +++ b/backend/src/main/resources/contents/nameplates/cat.yml @@ -1,17 +1,13 @@ -name-color: WHITE display-name: Sad Cat left: image: cat_left height: 16 ascent: 12 - width: 14 middle: image: cat_middle height: 16 ascent: 12 - width: 16 right: image: cat_right height: 16 - ascent: 12 - width: 14 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/cat_left.png b/backend/src/main/resources/contents/nameplates/cat_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/cat_left.png rename to backend/src/main/resources/contents/nameplates/cat_left.png diff --git a/paper/src/main/resources/contents/nameplates/cat_middle.png b/backend/src/main/resources/contents/nameplates/cat_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/cat_middle.png rename to backend/src/main/resources/contents/nameplates/cat_middle.png diff --git a/paper/src/main/resources/contents/nameplates/cat_right.png b/backend/src/main/resources/contents/nameplates/cat_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/cat_right.png rename to backend/src/main/resources/contents/nameplates/cat_right.png diff --git a/paper/src/main/resources/contents/nameplates/cheems.yml b/backend/src/main/resources/contents/nameplates/cheems.yml similarity index 80% rename from paper/src/main/resources/contents/nameplates/cheems.yml rename to backend/src/main/resources/contents/nameplates/cheems.yml index b5ed359..274cdfb 100644 --- a/paper/src/main/resources/contents/nameplates/cheems.yml +++ b/backend/src/main/resources/contents/nameplates/cheems.yml @@ -4,14 +4,11 @@ left: image: cheems_left height: 16 ascent: 12 - width: 16 middle: image: cheems_middle height: 16 ascent: 12 - width: 16 right: image: cheems_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/cheems_left.png b/backend/src/main/resources/contents/nameplates/cheems_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/cheems_left.png rename to backend/src/main/resources/contents/nameplates/cheems_left.png diff --git a/paper/src/main/resources/contents/nameplates/cheems_middle.png b/backend/src/main/resources/contents/nameplates/cheems_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/cheems_middle.png rename to backend/src/main/resources/contents/nameplates/cheems_middle.png diff --git a/paper/src/main/resources/contents/nameplates/cheems_right.png b/backend/src/main/resources/contents/nameplates/cheems_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/cheems_right.png rename to backend/src/main/resources/contents/nameplates/cheems_right.png diff --git a/paper/src/main/resources/contents/nameplates/egg.yml b/backend/src/main/resources/contents/nameplates/egg.yml similarity index 79% rename from paper/src/main/resources/contents/nameplates/egg.yml rename to backend/src/main/resources/contents/nameplates/egg.yml index a6fa1e3..123316a 100644 --- a/paper/src/main/resources/contents/nameplates/egg.yml +++ b/backend/src/main/resources/contents/nameplates/egg.yml @@ -4,14 +4,11 @@ left: image: egg_left height: 16 ascent: 12 - width: 16 middle: image: egg_middle height: 16 ascent: 12 - width: 16 right: image: egg_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/egg_left.png b/backend/src/main/resources/contents/nameplates/egg_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/egg_left.png rename to backend/src/main/resources/contents/nameplates/egg_left.png diff --git a/paper/src/main/resources/contents/nameplates/egg_middle.png b/backend/src/main/resources/contents/nameplates/egg_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/egg_middle.png rename to backend/src/main/resources/contents/nameplates/egg_middle.png diff --git a/paper/src/main/resources/contents/nameplates/egg_right.png b/backend/src/main/resources/contents/nameplates/egg_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/egg_right.png rename to backend/src/main/resources/contents/nameplates/egg_right.png diff --git a/paper/src/main/resources/contents/nameplates/halloween.yml b/backend/src/main/resources/contents/nameplates/halloween.yml similarity index 80% rename from paper/src/main/resources/contents/nameplates/halloween.yml rename to backend/src/main/resources/contents/nameplates/halloween.yml index a5b49ed..8a22330 100644 --- a/paper/src/main/resources/contents/nameplates/halloween.yml +++ b/backend/src/main/resources/contents/nameplates/halloween.yml @@ -4,14 +4,11 @@ left: image: halloween_left height: 16 ascent: 12 - width: 16 middle: image: halloween_middle height: 16 ascent: 12 - width: 16 right: image: halloween_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/halloween_left.png b/backend/src/main/resources/contents/nameplates/halloween_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/halloween_left.png rename to backend/src/main/resources/contents/nameplates/halloween_left.png diff --git a/paper/src/main/resources/contents/nameplates/halloween_middle.png b/backend/src/main/resources/contents/nameplates/halloween_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/halloween_middle.png rename to backend/src/main/resources/contents/nameplates/halloween_middle.png diff --git a/paper/src/main/resources/contents/nameplates/halloween_right.png b/backend/src/main/resources/contents/nameplates/halloween_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/halloween_right.png rename to backend/src/main/resources/contents/nameplates/halloween_right.png diff --git a/paper/src/main/resources/contents/nameplates/hutao.yml b/backend/src/main/resources/contents/nameplates/hutao.yml similarity index 79% rename from paper/src/main/resources/contents/nameplates/hutao.yml rename to backend/src/main/resources/contents/nameplates/hutao.yml index 19f2446..f655dcf 100644 --- a/paper/src/main/resources/contents/nameplates/hutao.yml +++ b/backend/src/main/resources/contents/nameplates/hutao.yml @@ -4,14 +4,11 @@ left: image: hutao_left height: 16 ascent: 12 - width: 16 middle: image: hutao_middle height: 16 ascent: 12 - width: 16 right: image: hutao_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/hutao_left.png b/backend/src/main/resources/contents/nameplates/hutao_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/hutao_left.png rename to backend/src/main/resources/contents/nameplates/hutao_left.png diff --git a/paper/src/main/resources/contents/nameplates/hutao_middle.png b/backend/src/main/resources/contents/nameplates/hutao_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/hutao_middle.png rename to backend/src/main/resources/contents/nameplates/hutao_middle.png diff --git a/paper/src/main/resources/contents/nameplates/hutao_right.png b/backend/src/main/resources/contents/nameplates/hutao_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/hutao_right.png rename to backend/src/main/resources/contents/nameplates/hutao_right.png diff --git a/paper/src/main/resources/contents/nameplates/rabbit.yml b/backend/src/main/resources/contents/nameplates/rabbit.yml similarity index 80% rename from paper/src/main/resources/contents/nameplates/rabbit.yml rename to backend/src/main/resources/contents/nameplates/rabbit.yml index 01a1993..16837b5 100644 --- a/paper/src/main/resources/contents/nameplates/rabbit.yml +++ b/backend/src/main/resources/contents/nameplates/rabbit.yml @@ -4,14 +4,11 @@ left: image: rabbit_left height: 16 ascent: 12 - width: 16 middle: image: rabbit_middle height: 16 ascent: 12 - width: 16 right: image: rabbit_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/rabbit_left.png b/backend/src/main/resources/contents/nameplates/rabbit_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/rabbit_left.png rename to backend/src/main/resources/contents/nameplates/rabbit_left.png diff --git a/paper/src/main/resources/contents/nameplates/rabbit_middle.png b/backend/src/main/resources/contents/nameplates/rabbit_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/rabbit_middle.png rename to backend/src/main/resources/contents/nameplates/rabbit_middle.png diff --git a/paper/src/main/resources/contents/nameplates/rabbit_right.png b/backend/src/main/resources/contents/nameplates/rabbit_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/rabbit_right.png rename to backend/src/main/resources/contents/nameplates/rabbit_right.png diff --git a/paper/src/main/resources/contents/nameplates/starsky.yml b/backend/src/main/resources/contents/nameplates/starsky.yml similarity index 70% rename from paper/src/main/resources/contents/nameplates/starsky.yml rename to backend/src/main/resources/contents/nameplates/starsky.yml index 8c2aed0..3636ec4 100644 --- a/paper/src/main/resources/contents/nameplates/starsky.yml +++ b/backend/src/main/resources/contents/nameplates/starsky.yml @@ -1,17 +1,14 @@ name-color: WHITE -display-name: Star Sky +display-name: Starry Sky left: image: starsky_left height: 16 ascent: 12 - width: 16 middle: image: starsky_middle height: 16 ascent: 12 - width: 16 right: image: starsky_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/starsky_left.png b/backend/src/main/resources/contents/nameplates/starsky_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/starsky_left.png rename to backend/src/main/resources/contents/nameplates/starsky_left.png diff --git a/paper/src/main/resources/contents/nameplates/starsky_middle.png b/backend/src/main/resources/contents/nameplates/starsky_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/starsky_middle.png rename to backend/src/main/resources/contents/nameplates/starsky_middle.png diff --git a/paper/src/main/resources/contents/nameplates/starsky_right.png b/backend/src/main/resources/contents/nameplates/starsky_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/starsky_right.png rename to backend/src/main/resources/contents/nameplates/starsky_right.png diff --git a/paper/src/main/resources/contents/nameplates/trident.yml b/backend/src/main/resources/contents/nameplates/trident.yml similarity index 80% rename from paper/src/main/resources/contents/nameplates/trident.yml rename to backend/src/main/resources/contents/nameplates/trident.yml index 094bbc3..4326dcc 100644 --- a/paper/src/main/resources/contents/nameplates/trident.yml +++ b/backend/src/main/resources/contents/nameplates/trident.yml @@ -4,14 +4,11 @@ left: image: trident_left height: 16 ascent: 12 - width: 16 middle: image: trident_middle height: 16 ascent: 12 - width: 16 right: image: trident_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/trident_left.png b/backend/src/main/resources/contents/nameplates/trident_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/trident_left.png rename to backend/src/main/resources/contents/nameplates/trident_left.png diff --git a/paper/src/main/resources/contents/nameplates/trident_middle.png b/backend/src/main/resources/contents/nameplates/trident_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/trident_middle.png rename to backend/src/main/resources/contents/nameplates/trident_middle.png diff --git a/paper/src/main/resources/contents/nameplates/trident_right.png b/backend/src/main/resources/contents/nameplates/trident_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/trident_right.png rename to backend/src/main/resources/contents/nameplates/trident_right.png diff --git a/paper/src/main/resources/contents/nameplates/wither.yml b/backend/src/main/resources/contents/nameplates/wither.yml similarity index 80% rename from paper/src/main/resources/contents/nameplates/wither.yml rename to backend/src/main/resources/contents/nameplates/wither.yml index eda71a3..2ef3d1b 100644 --- a/paper/src/main/resources/contents/nameplates/wither.yml +++ b/backend/src/main/resources/contents/nameplates/wither.yml @@ -4,14 +4,11 @@ left: image: wither_left height: 16 ascent: 12 - width: 13 middle: image: wither_middle height: 16 ascent: 12 - width: 16 right: image: wither_right height: 16 - ascent: 12 - width: 13 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/wither_left.png b/backend/src/main/resources/contents/nameplates/wither_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/wither_left.png rename to backend/src/main/resources/contents/nameplates/wither_left.png diff --git a/paper/src/main/resources/contents/nameplates/wither_middle.png b/backend/src/main/resources/contents/nameplates/wither_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/wither_middle.png rename to backend/src/main/resources/contents/nameplates/wither_middle.png diff --git a/paper/src/main/resources/contents/nameplates/wither_right.png b/backend/src/main/resources/contents/nameplates/wither_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/wither_right.png rename to backend/src/main/resources/contents/nameplates/wither_right.png diff --git a/paper/src/main/resources/contents/nameplates/xmas.yml b/backend/src/main/resources/contents/nameplates/xmas.yml similarity index 80% rename from paper/src/main/resources/contents/nameplates/xmas.yml rename to backend/src/main/resources/contents/nameplates/xmas.yml index 7ca5e64..56a85f2 100644 --- a/paper/src/main/resources/contents/nameplates/xmas.yml +++ b/backend/src/main/resources/contents/nameplates/xmas.yml @@ -4,14 +4,11 @@ left: image: xmas_left height: 16 ascent: 12 - width: 16 middle: image: xmas_middle height: 16 ascent: 12 - width: 16 right: image: xmas_right height: 16 - ascent: 12 - width: 16 \ No newline at end of file + ascent: 12 \ No newline at end of file diff --git a/paper/src/main/resources/contents/nameplates/xmas_left.png b/backend/src/main/resources/contents/nameplates/xmas_left.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/xmas_left.png rename to backend/src/main/resources/contents/nameplates/xmas_left.png diff --git a/paper/src/main/resources/contents/nameplates/xmas_middle.png b/backend/src/main/resources/contents/nameplates/xmas_middle.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/xmas_middle.png rename to backend/src/main/resources/contents/nameplates/xmas_middle.png diff --git a/paper/src/main/resources/contents/nameplates/xmas_right.png b/backend/src/main/resources/contents/nameplates/xmas_right.png similarity index 100% rename from paper/src/main/resources/contents/nameplates/xmas_right.png rename to backend/src/main/resources/contents/nameplates/xmas_right.png diff --git a/paper/src/main/resources/database.yml b/backend/src/main/resources/database.yml similarity index 92% rename from paper/src/main/resources/database.yml rename to backend/src/main/resources/database.yml index 7b882a4..47f2306 100644 --- a/paper/src/main/resources/database.yml +++ b/backend/src/main/resources/database.yml @@ -1,3 +1,5 @@ +config-version: '${config_version}' + # file: # JSON # YAML @@ -11,7 +13,7 @@ # MariaDB (preferred over MySQL) # MongoDB # -data-storage-method: H2 +data-storage-method: H2 # `NONE` if you want to disable data storage SQLite: file: 'sqlite' @@ -67,7 +69,7 @@ MongoDB: Redis: enable: false host: localhost - #password: 123456 + password: "" port: 6379 use-ssl: false MaxTotal: 10 diff --git a/paper/src/main/resources/font/accented.png b/backend/src/main/resources/font/accented.png similarity index 100% rename from paper/src/main/resources/font/accented.png rename to backend/src/main/resources/font/accented.png diff --git a/paper/src/main/resources/font/ascii.png b/backend/src/main/resources/font/ascii.png similarity index 100% rename from paper/src/main/resources/font/ascii.png rename to backend/src/main/resources/font/ascii.png diff --git a/paper/src/main/resources/font/ascii_sga.png b/backend/src/main/resources/font/ascii_sga.png similarity index 100% rename from paper/src/main/resources/font/ascii_sga.png rename to backend/src/main/resources/font/ascii_sga.png diff --git a/paper/src/main/resources/font/asciillager.png b/backend/src/main/resources/font/asciillager.png similarity index 100% rename from paper/src/main/resources/font/asciillager.png rename to backend/src/main/resources/font/asciillager.png diff --git a/backend/src/main/resources/font/glyph_sizes.bin b/backend/src/main/resources/font/glyph_sizes.bin new file mode 100644 index 0000000..61a7979 Binary files /dev/null and b/backend/src/main/resources/font/glyph_sizes.bin differ diff --git a/paper/src/main/resources/font/nonlatin_european.png b/backend/src/main/resources/font/nonlatin_european.png similarity index 100% rename from paper/src/main/resources/font/nonlatin_european.png rename to backend/src/main/resources/font/nonlatin_european.png diff --git a/paper/src/main/resources/font/unicode_page_00.png b/backend/src/main/resources/font/unicode_page_00.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_00.png rename to backend/src/main/resources/font/unicode_page_00.png diff --git a/paper/src/main/resources/font/unicode_page_01.png b/backend/src/main/resources/font/unicode_page_01.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_01.png rename to backend/src/main/resources/font/unicode_page_01.png diff --git a/paper/src/main/resources/font/unicode_page_02.png b/backend/src/main/resources/font/unicode_page_02.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_02.png rename to backend/src/main/resources/font/unicode_page_02.png diff --git a/paper/src/main/resources/font/unicode_page_03.png b/backend/src/main/resources/font/unicode_page_03.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_03.png rename to backend/src/main/resources/font/unicode_page_03.png diff --git a/paper/src/main/resources/font/unicode_page_04.png b/backend/src/main/resources/font/unicode_page_04.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_04.png rename to backend/src/main/resources/font/unicode_page_04.png diff --git a/paper/src/main/resources/font/unicode_page_05.png b/backend/src/main/resources/font/unicode_page_05.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_05.png rename to backend/src/main/resources/font/unicode_page_05.png diff --git a/paper/src/main/resources/font/unicode_page_06.png b/backend/src/main/resources/font/unicode_page_06.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_06.png rename to backend/src/main/resources/font/unicode_page_06.png diff --git a/paper/src/main/resources/font/unicode_page_07.png b/backend/src/main/resources/font/unicode_page_07.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_07.png rename to backend/src/main/resources/font/unicode_page_07.png diff --git a/paper/src/main/resources/font/unicode_page_09.png b/backend/src/main/resources/font/unicode_page_09.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_09.png rename to backend/src/main/resources/font/unicode_page_09.png diff --git a/paper/src/main/resources/font/unicode_page_0a.png b/backend/src/main/resources/font/unicode_page_0a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_0a.png rename to backend/src/main/resources/font/unicode_page_0a.png diff --git a/paper/src/main/resources/font/unicode_page_0b.png b/backend/src/main/resources/font/unicode_page_0b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_0b.png rename to backend/src/main/resources/font/unicode_page_0b.png diff --git a/paper/src/main/resources/font/unicode_page_0c.png b/backend/src/main/resources/font/unicode_page_0c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_0c.png rename to backend/src/main/resources/font/unicode_page_0c.png diff --git a/paper/src/main/resources/font/unicode_page_0d.png b/backend/src/main/resources/font/unicode_page_0d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_0d.png rename to backend/src/main/resources/font/unicode_page_0d.png diff --git a/paper/src/main/resources/font/unicode_page_0e.png b/backend/src/main/resources/font/unicode_page_0e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_0e.png rename to backend/src/main/resources/font/unicode_page_0e.png diff --git a/paper/src/main/resources/font/unicode_page_0f.png b/backend/src/main/resources/font/unicode_page_0f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_0f.png rename to backend/src/main/resources/font/unicode_page_0f.png diff --git a/paper/src/main/resources/font/unicode_page_10.png b/backend/src/main/resources/font/unicode_page_10.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_10.png rename to backend/src/main/resources/font/unicode_page_10.png diff --git a/paper/src/main/resources/font/unicode_page_11.png b/backend/src/main/resources/font/unicode_page_11.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_11.png rename to backend/src/main/resources/font/unicode_page_11.png diff --git a/paper/src/main/resources/font/unicode_page_12.png b/backend/src/main/resources/font/unicode_page_12.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_12.png rename to backend/src/main/resources/font/unicode_page_12.png diff --git a/paper/src/main/resources/font/unicode_page_13.png b/backend/src/main/resources/font/unicode_page_13.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_13.png rename to backend/src/main/resources/font/unicode_page_13.png diff --git a/paper/src/main/resources/font/unicode_page_14.png b/backend/src/main/resources/font/unicode_page_14.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_14.png rename to backend/src/main/resources/font/unicode_page_14.png diff --git a/paper/src/main/resources/font/unicode_page_15.png b/backend/src/main/resources/font/unicode_page_15.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_15.png rename to backend/src/main/resources/font/unicode_page_15.png diff --git a/paper/src/main/resources/font/unicode_page_16.png b/backend/src/main/resources/font/unicode_page_16.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_16.png rename to backend/src/main/resources/font/unicode_page_16.png diff --git a/paper/src/main/resources/font/unicode_page_17.png b/backend/src/main/resources/font/unicode_page_17.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_17.png rename to backend/src/main/resources/font/unicode_page_17.png diff --git a/paper/src/main/resources/font/unicode_page_18.png b/backend/src/main/resources/font/unicode_page_18.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_18.png rename to backend/src/main/resources/font/unicode_page_18.png diff --git a/paper/src/main/resources/font/unicode_page_19.png b/backend/src/main/resources/font/unicode_page_19.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_19.png rename to backend/src/main/resources/font/unicode_page_19.png diff --git a/paper/src/main/resources/font/unicode_page_1a.png b/backend/src/main/resources/font/unicode_page_1a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_1a.png rename to backend/src/main/resources/font/unicode_page_1a.png diff --git a/paper/src/main/resources/font/unicode_page_1b.png b/backend/src/main/resources/font/unicode_page_1b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_1b.png rename to backend/src/main/resources/font/unicode_page_1b.png diff --git a/paper/src/main/resources/font/unicode_page_1c.png b/backend/src/main/resources/font/unicode_page_1c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_1c.png rename to backend/src/main/resources/font/unicode_page_1c.png diff --git a/paper/src/main/resources/font/unicode_page_1d.png b/backend/src/main/resources/font/unicode_page_1d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_1d.png rename to backend/src/main/resources/font/unicode_page_1d.png diff --git a/paper/src/main/resources/font/unicode_page_1e.png b/backend/src/main/resources/font/unicode_page_1e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_1e.png rename to backend/src/main/resources/font/unicode_page_1e.png diff --git a/paper/src/main/resources/font/unicode_page_1f.png b/backend/src/main/resources/font/unicode_page_1f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_1f.png rename to backend/src/main/resources/font/unicode_page_1f.png diff --git a/paper/src/main/resources/font/unicode_page_20.png b/backend/src/main/resources/font/unicode_page_20.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_20.png rename to backend/src/main/resources/font/unicode_page_20.png diff --git a/paper/src/main/resources/font/unicode_page_21.png b/backend/src/main/resources/font/unicode_page_21.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_21.png rename to backend/src/main/resources/font/unicode_page_21.png diff --git a/paper/src/main/resources/font/unicode_page_22.png b/backend/src/main/resources/font/unicode_page_22.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_22.png rename to backend/src/main/resources/font/unicode_page_22.png diff --git a/paper/src/main/resources/font/unicode_page_23.png b/backend/src/main/resources/font/unicode_page_23.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_23.png rename to backend/src/main/resources/font/unicode_page_23.png diff --git a/paper/src/main/resources/font/unicode_page_24.png b/backend/src/main/resources/font/unicode_page_24.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_24.png rename to backend/src/main/resources/font/unicode_page_24.png diff --git a/paper/src/main/resources/font/unicode_page_25.png b/backend/src/main/resources/font/unicode_page_25.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_25.png rename to backend/src/main/resources/font/unicode_page_25.png diff --git a/paper/src/main/resources/font/unicode_page_26.png b/backend/src/main/resources/font/unicode_page_26.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_26.png rename to backend/src/main/resources/font/unicode_page_26.png diff --git a/paper/src/main/resources/font/unicode_page_27.png b/backend/src/main/resources/font/unicode_page_27.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_27.png rename to backend/src/main/resources/font/unicode_page_27.png diff --git a/paper/src/main/resources/font/unicode_page_28.png b/backend/src/main/resources/font/unicode_page_28.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_28.png rename to backend/src/main/resources/font/unicode_page_28.png diff --git a/paper/src/main/resources/font/unicode_page_29.png b/backend/src/main/resources/font/unicode_page_29.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_29.png rename to backend/src/main/resources/font/unicode_page_29.png diff --git a/paper/src/main/resources/font/unicode_page_2a.png b/backend/src/main/resources/font/unicode_page_2a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_2a.png rename to backend/src/main/resources/font/unicode_page_2a.png diff --git a/paper/src/main/resources/font/unicode_page_2b.png b/backend/src/main/resources/font/unicode_page_2b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_2b.png rename to backend/src/main/resources/font/unicode_page_2b.png diff --git a/paper/src/main/resources/font/unicode_page_2c.png b/backend/src/main/resources/font/unicode_page_2c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_2c.png rename to backend/src/main/resources/font/unicode_page_2c.png diff --git a/paper/src/main/resources/font/unicode_page_2d.png b/backend/src/main/resources/font/unicode_page_2d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_2d.png rename to backend/src/main/resources/font/unicode_page_2d.png diff --git a/paper/src/main/resources/font/unicode_page_2e.png b/backend/src/main/resources/font/unicode_page_2e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_2e.png rename to backend/src/main/resources/font/unicode_page_2e.png diff --git a/paper/src/main/resources/font/unicode_page_2f.png b/backend/src/main/resources/font/unicode_page_2f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_2f.png rename to backend/src/main/resources/font/unicode_page_2f.png diff --git a/paper/src/main/resources/font/unicode_page_30.png b/backend/src/main/resources/font/unicode_page_30.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_30.png rename to backend/src/main/resources/font/unicode_page_30.png diff --git a/paper/src/main/resources/font/unicode_page_31.png b/backend/src/main/resources/font/unicode_page_31.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_31.png rename to backend/src/main/resources/font/unicode_page_31.png diff --git a/paper/src/main/resources/font/unicode_page_32.png b/backend/src/main/resources/font/unicode_page_32.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_32.png rename to backend/src/main/resources/font/unicode_page_32.png diff --git a/paper/src/main/resources/font/unicode_page_33.png b/backend/src/main/resources/font/unicode_page_33.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_33.png rename to backend/src/main/resources/font/unicode_page_33.png diff --git a/paper/src/main/resources/font/unicode_page_34.png b/backend/src/main/resources/font/unicode_page_34.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_34.png rename to backend/src/main/resources/font/unicode_page_34.png diff --git a/paper/src/main/resources/font/unicode_page_35.png b/backend/src/main/resources/font/unicode_page_35.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_35.png rename to backend/src/main/resources/font/unicode_page_35.png diff --git a/paper/src/main/resources/font/unicode_page_36.png b/backend/src/main/resources/font/unicode_page_36.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_36.png rename to backend/src/main/resources/font/unicode_page_36.png diff --git a/paper/src/main/resources/font/unicode_page_37.png b/backend/src/main/resources/font/unicode_page_37.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_37.png rename to backend/src/main/resources/font/unicode_page_37.png diff --git a/paper/src/main/resources/font/unicode_page_38.png b/backend/src/main/resources/font/unicode_page_38.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_38.png rename to backend/src/main/resources/font/unicode_page_38.png diff --git a/paper/src/main/resources/font/unicode_page_39.png b/backend/src/main/resources/font/unicode_page_39.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_39.png rename to backend/src/main/resources/font/unicode_page_39.png diff --git a/paper/src/main/resources/font/unicode_page_3a.png b/backend/src/main/resources/font/unicode_page_3a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_3a.png rename to backend/src/main/resources/font/unicode_page_3a.png diff --git a/paper/src/main/resources/font/unicode_page_3b.png b/backend/src/main/resources/font/unicode_page_3b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_3b.png rename to backend/src/main/resources/font/unicode_page_3b.png diff --git a/paper/src/main/resources/font/unicode_page_3c.png b/backend/src/main/resources/font/unicode_page_3c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_3c.png rename to backend/src/main/resources/font/unicode_page_3c.png diff --git a/paper/src/main/resources/font/unicode_page_3d.png b/backend/src/main/resources/font/unicode_page_3d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_3d.png rename to backend/src/main/resources/font/unicode_page_3d.png diff --git a/paper/src/main/resources/font/unicode_page_3e.png b/backend/src/main/resources/font/unicode_page_3e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_3e.png rename to backend/src/main/resources/font/unicode_page_3e.png diff --git a/paper/src/main/resources/font/unicode_page_3f.png b/backend/src/main/resources/font/unicode_page_3f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_3f.png rename to backend/src/main/resources/font/unicode_page_3f.png diff --git a/paper/src/main/resources/font/unicode_page_40.png b/backend/src/main/resources/font/unicode_page_40.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_40.png rename to backend/src/main/resources/font/unicode_page_40.png diff --git a/paper/src/main/resources/font/unicode_page_41.png b/backend/src/main/resources/font/unicode_page_41.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_41.png rename to backend/src/main/resources/font/unicode_page_41.png diff --git a/paper/src/main/resources/font/unicode_page_42.png b/backend/src/main/resources/font/unicode_page_42.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_42.png rename to backend/src/main/resources/font/unicode_page_42.png diff --git a/paper/src/main/resources/font/unicode_page_43.png b/backend/src/main/resources/font/unicode_page_43.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_43.png rename to backend/src/main/resources/font/unicode_page_43.png diff --git a/paper/src/main/resources/font/unicode_page_44.png b/backend/src/main/resources/font/unicode_page_44.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_44.png rename to backend/src/main/resources/font/unicode_page_44.png diff --git a/paper/src/main/resources/font/unicode_page_45.png b/backend/src/main/resources/font/unicode_page_45.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_45.png rename to backend/src/main/resources/font/unicode_page_45.png diff --git a/paper/src/main/resources/font/unicode_page_46.png b/backend/src/main/resources/font/unicode_page_46.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_46.png rename to backend/src/main/resources/font/unicode_page_46.png diff --git a/paper/src/main/resources/font/unicode_page_47.png b/backend/src/main/resources/font/unicode_page_47.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_47.png rename to backend/src/main/resources/font/unicode_page_47.png diff --git a/paper/src/main/resources/font/unicode_page_48.png b/backend/src/main/resources/font/unicode_page_48.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_48.png rename to backend/src/main/resources/font/unicode_page_48.png diff --git a/paper/src/main/resources/font/unicode_page_49.png b/backend/src/main/resources/font/unicode_page_49.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_49.png rename to backend/src/main/resources/font/unicode_page_49.png diff --git a/paper/src/main/resources/font/unicode_page_4a.png b/backend/src/main/resources/font/unicode_page_4a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_4a.png rename to backend/src/main/resources/font/unicode_page_4a.png diff --git a/paper/src/main/resources/font/unicode_page_4b.png b/backend/src/main/resources/font/unicode_page_4b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_4b.png rename to backend/src/main/resources/font/unicode_page_4b.png diff --git a/paper/src/main/resources/font/unicode_page_4c.png b/backend/src/main/resources/font/unicode_page_4c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_4c.png rename to backend/src/main/resources/font/unicode_page_4c.png diff --git a/paper/src/main/resources/font/unicode_page_4d.png b/backend/src/main/resources/font/unicode_page_4d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_4d.png rename to backend/src/main/resources/font/unicode_page_4d.png diff --git a/paper/src/main/resources/font/unicode_page_4e.png b/backend/src/main/resources/font/unicode_page_4e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_4e.png rename to backend/src/main/resources/font/unicode_page_4e.png diff --git a/paper/src/main/resources/font/unicode_page_4f.png b/backend/src/main/resources/font/unicode_page_4f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_4f.png rename to backend/src/main/resources/font/unicode_page_4f.png diff --git a/paper/src/main/resources/font/unicode_page_50.png b/backend/src/main/resources/font/unicode_page_50.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_50.png rename to backend/src/main/resources/font/unicode_page_50.png diff --git a/paper/src/main/resources/font/unicode_page_51.png b/backend/src/main/resources/font/unicode_page_51.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_51.png rename to backend/src/main/resources/font/unicode_page_51.png diff --git a/paper/src/main/resources/font/unicode_page_52.png b/backend/src/main/resources/font/unicode_page_52.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_52.png rename to backend/src/main/resources/font/unicode_page_52.png diff --git a/paper/src/main/resources/font/unicode_page_53.png b/backend/src/main/resources/font/unicode_page_53.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_53.png rename to backend/src/main/resources/font/unicode_page_53.png diff --git a/paper/src/main/resources/font/unicode_page_54.png b/backend/src/main/resources/font/unicode_page_54.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_54.png rename to backend/src/main/resources/font/unicode_page_54.png diff --git a/paper/src/main/resources/font/unicode_page_55.png b/backend/src/main/resources/font/unicode_page_55.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_55.png rename to backend/src/main/resources/font/unicode_page_55.png diff --git a/paper/src/main/resources/font/unicode_page_56.png b/backend/src/main/resources/font/unicode_page_56.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_56.png rename to backend/src/main/resources/font/unicode_page_56.png diff --git a/paper/src/main/resources/font/unicode_page_57.png b/backend/src/main/resources/font/unicode_page_57.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_57.png rename to backend/src/main/resources/font/unicode_page_57.png diff --git a/paper/src/main/resources/font/unicode_page_58.png b/backend/src/main/resources/font/unicode_page_58.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_58.png rename to backend/src/main/resources/font/unicode_page_58.png diff --git a/paper/src/main/resources/font/unicode_page_59.png b/backend/src/main/resources/font/unicode_page_59.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_59.png rename to backend/src/main/resources/font/unicode_page_59.png diff --git a/paper/src/main/resources/font/unicode_page_5a.png b/backend/src/main/resources/font/unicode_page_5a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_5a.png rename to backend/src/main/resources/font/unicode_page_5a.png diff --git a/paper/src/main/resources/font/unicode_page_5b.png b/backend/src/main/resources/font/unicode_page_5b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_5b.png rename to backend/src/main/resources/font/unicode_page_5b.png diff --git a/paper/src/main/resources/font/unicode_page_5c.png b/backend/src/main/resources/font/unicode_page_5c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_5c.png rename to backend/src/main/resources/font/unicode_page_5c.png diff --git a/paper/src/main/resources/font/unicode_page_5d.png b/backend/src/main/resources/font/unicode_page_5d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_5d.png rename to backend/src/main/resources/font/unicode_page_5d.png diff --git a/paper/src/main/resources/font/unicode_page_5e.png b/backend/src/main/resources/font/unicode_page_5e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_5e.png rename to backend/src/main/resources/font/unicode_page_5e.png diff --git a/paper/src/main/resources/font/unicode_page_5f.png b/backend/src/main/resources/font/unicode_page_5f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_5f.png rename to backend/src/main/resources/font/unicode_page_5f.png diff --git a/paper/src/main/resources/font/unicode_page_60.png b/backend/src/main/resources/font/unicode_page_60.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_60.png rename to backend/src/main/resources/font/unicode_page_60.png diff --git a/paper/src/main/resources/font/unicode_page_61.png b/backend/src/main/resources/font/unicode_page_61.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_61.png rename to backend/src/main/resources/font/unicode_page_61.png diff --git a/paper/src/main/resources/font/unicode_page_62.png b/backend/src/main/resources/font/unicode_page_62.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_62.png rename to backend/src/main/resources/font/unicode_page_62.png diff --git a/paper/src/main/resources/font/unicode_page_63.png b/backend/src/main/resources/font/unicode_page_63.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_63.png rename to backend/src/main/resources/font/unicode_page_63.png diff --git a/paper/src/main/resources/font/unicode_page_64.png b/backend/src/main/resources/font/unicode_page_64.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_64.png rename to backend/src/main/resources/font/unicode_page_64.png diff --git a/paper/src/main/resources/font/unicode_page_65.png b/backend/src/main/resources/font/unicode_page_65.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_65.png rename to backend/src/main/resources/font/unicode_page_65.png diff --git a/paper/src/main/resources/font/unicode_page_66.png b/backend/src/main/resources/font/unicode_page_66.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_66.png rename to backend/src/main/resources/font/unicode_page_66.png diff --git a/paper/src/main/resources/font/unicode_page_67.png b/backend/src/main/resources/font/unicode_page_67.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_67.png rename to backend/src/main/resources/font/unicode_page_67.png diff --git a/paper/src/main/resources/font/unicode_page_68.png b/backend/src/main/resources/font/unicode_page_68.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_68.png rename to backend/src/main/resources/font/unicode_page_68.png diff --git a/paper/src/main/resources/font/unicode_page_69.png b/backend/src/main/resources/font/unicode_page_69.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_69.png rename to backend/src/main/resources/font/unicode_page_69.png diff --git a/paper/src/main/resources/font/unicode_page_6a.png b/backend/src/main/resources/font/unicode_page_6a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_6a.png rename to backend/src/main/resources/font/unicode_page_6a.png diff --git a/paper/src/main/resources/font/unicode_page_6b.png b/backend/src/main/resources/font/unicode_page_6b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_6b.png rename to backend/src/main/resources/font/unicode_page_6b.png diff --git a/paper/src/main/resources/font/unicode_page_6c.png b/backend/src/main/resources/font/unicode_page_6c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_6c.png rename to backend/src/main/resources/font/unicode_page_6c.png diff --git a/paper/src/main/resources/font/unicode_page_6d.png b/backend/src/main/resources/font/unicode_page_6d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_6d.png rename to backend/src/main/resources/font/unicode_page_6d.png diff --git a/paper/src/main/resources/font/unicode_page_6e.png b/backend/src/main/resources/font/unicode_page_6e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_6e.png rename to backend/src/main/resources/font/unicode_page_6e.png diff --git a/paper/src/main/resources/font/unicode_page_6f.png b/backend/src/main/resources/font/unicode_page_6f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_6f.png rename to backend/src/main/resources/font/unicode_page_6f.png diff --git a/paper/src/main/resources/font/unicode_page_70.png b/backend/src/main/resources/font/unicode_page_70.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_70.png rename to backend/src/main/resources/font/unicode_page_70.png diff --git a/paper/src/main/resources/font/unicode_page_71.png b/backend/src/main/resources/font/unicode_page_71.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_71.png rename to backend/src/main/resources/font/unicode_page_71.png diff --git a/paper/src/main/resources/font/unicode_page_72.png b/backend/src/main/resources/font/unicode_page_72.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_72.png rename to backend/src/main/resources/font/unicode_page_72.png diff --git a/paper/src/main/resources/font/unicode_page_73.png b/backend/src/main/resources/font/unicode_page_73.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_73.png rename to backend/src/main/resources/font/unicode_page_73.png diff --git a/paper/src/main/resources/font/unicode_page_74.png b/backend/src/main/resources/font/unicode_page_74.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_74.png rename to backend/src/main/resources/font/unicode_page_74.png diff --git a/paper/src/main/resources/font/unicode_page_75.png b/backend/src/main/resources/font/unicode_page_75.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_75.png rename to backend/src/main/resources/font/unicode_page_75.png diff --git a/paper/src/main/resources/font/unicode_page_76.png b/backend/src/main/resources/font/unicode_page_76.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_76.png rename to backend/src/main/resources/font/unicode_page_76.png diff --git a/paper/src/main/resources/font/unicode_page_77.png b/backend/src/main/resources/font/unicode_page_77.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_77.png rename to backend/src/main/resources/font/unicode_page_77.png diff --git a/paper/src/main/resources/font/unicode_page_78.png b/backend/src/main/resources/font/unicode_page_78.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_78.png rename to backend/src/main/resources/font/unicode_page_78.png diff --git a/paper/src/main/resources/font/unicode_page_79.png b/backend/src/main/resources/font/unicode_page_79.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_79.png rename to backend/src/main/resources/font/unicode_page_79.png diff --git a/paper/src/main/resources/font/unicode_page_7a.png b/backend/src/main/resources/font/unicode_page_7a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_7a.png rename to backend/src/main/resources/font/unicode_page_7a.png diff --git a/paper/src/main/resources/font/unicode_page_7b.png b/backend/src/main/resources/font/unicode_page_7b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_7b.png rename to backend/src/main/resources/font/unicode_page_7b.png diff --git a/paper/src/main/resources/font/unicode_page_7c.png b/backend/src/main/resources/font/unicode_page_7c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_7c.png rename to backend/src/main/resources/font/unicode_page_7c.png diff --git a/paper/src/main/resources/font/unicode_page_7d.png b/backend/src/main/resources/font/unicode_page_7d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_7d.png rename to backend/src/main/resources/font/unicode_page_7d.png diff --git a/paper/src/main/resources/font/unicode_page_7e.png b/backend/src/main/resources/font/unicode_page_7e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_7e.png rename to backend/src/main/resources/font/unicode_page_7e.png diff --git a/paper/src/main/resources/font/unicode_page_7f.png b/backend/src/main/resources/font/unicode_page_7f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_7f.png rename to backend/src/main/resources/font/unicode_page_7f.png diff --git a/paper/src/main/resources/font/unicode_page_80.png b/backend/src/main/resources/font/unicode_page_80.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_80.png rename to backend/src/main/resources/font/unicode_page_80.png diff --git a/paper/src/main/resources/font/unicode_page_81.png b/backend/src/main/resources/font/unicode_page_81.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_81.png rename to backend/src/main/resources/font/unicode_page_81.png diff --git a/paper/src/main/resources/font/unicode_page_82.png b/backend/src/main/resources/font/unicode_page_82.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_82.png rename to backend/src/main/resources/font/unicode_page_82.png diff --git a/paper/src/main/resources/font/unicode_page_83.png b/backend/src/main/resources/font/unicode_page_83.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_83.png rename to backend/src/main/resources/font/unicode_page_83.png diff --git a/paper/src/main/resources/font/unicode_page_84.png b/backend/src/main/resources/font/unicode_page_84.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_84.png rename to backend/src/main/resources/font/unicode_page_84.png diff --git a/paper/src/main/resources/font/unicode_page_85.png b/backend/src/main/resources/font/unicode_page_85.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_85.png rename to backend/src/main/resources/font/unicode_page_85.png diff --git a/paper/src/main/resources/font/unicode_page_86.png b/backend/src/main/resources/font/unicode_page_86.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_86.png rename to backend/src/main/resources/font/unicode_page_86.png diff --git a/paper/src/main/resources/font/unicode_page_87.png b/backend/src/main/resources/font/unicode_page_87.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_87.png rename to backend/src/main/resources/font/unicode_page_87.png diff --git a/paper/src/main/resources/font/unicode_page_88.png b/backend/src/main/resources/font/unicode_page_88.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_88.png rename to backend/src/main/resources/font/unicode_page_88.png diff --git a/paper/src/main/resources/font/unicode_page_89.png b/backend/src/main/resources/font/unicode_page_89.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_89.png rename to backend/src/main/resources/font/unicode_page_89.png diff --git a/paper/src/main/resources/font/unicode_page_8a.png b/backend/src/main/resources/font/unicode_page_8a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_8a.png rename to backend/src/main/resources/font/unicode_page_8a.png diff --git a/paper/src/main/resources/font/unicode_page_8b.png b/backend/src/main/resources/font/unicode_page_8b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_8b.png rename to backend/src/main/resources/font/unicode_page_8b.png diff --git a/paper/src/main/resources/font/unicode_page_8c.png b/backend/src/main/resources/font/unicode_page_8c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_8c.png rename to backend/src/main/resources/font/unicode_page_8c.png diff --git a/paper/src/main/resources/font/unicode_page_8d.png b/backend/src/main/resources/font/unicode_page_8d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_8d.png rename to backend/src/main/resources/font/unicode_page_8d.png diff --git a/paper/src/main/resources/font/unicode_page_8e.png b/backend/src/main/resources/font/unicode_page_8e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_8e.png rename to backend/src/main/resources/font/unicode_page_8e.png diff --git a/paper/src/main/resources/font/unicode_page_8f.png b/backend/src/main/resources/font/unicode_page_8f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_8f.png rename to backend/src/main/resources/font/unicode_page_8f.png diff --git a/paper/src/main/resources/font/unicode_page_90.png b/backend/src/main/resources/font/unicode_page_90.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_90.png rename to backend/src/main/resources/font/unicode_page_90.png diff --git a/paper/src/main/resources/font/unicode_page_91.png b/backend/src/main/resources/font/unicode_page_91.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_91.png rename to backend/src/main/resources/font/unicode_page_91.png diff --git a/paper/src/main/resources/font/unicode_page_92.png b/backend/src/main/resources/font/unicode_page_92.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_92.png rename to backend/src/main/resources/font/unicode_page_92.png diff --git a/paper/src/main/resources/font/unicode_page_93.png b/backend/src/main/resources/font/unicode_page_93.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_93.png rename to backend/src/main/resources/font/unicode_page_93.png diff --git a/paper/src/main/resources/font/unicode_page_94.png b/backend/src/main/resources/font/unicode_page_94.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_94.png rename to backend/src/main/resources/font/unicode_page_94.png diff --git a/paper/src/main/resources/font/unicode_page_95.png b/backend/src/main/resources/font/unicode_page_95.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_95.png rename to backend/src/main/resources/font/unicode_page_95.png diff --git a/paper/src/main/resources/font/unicode_page_96.png b/backend/src/main/resources/font/unicode_page_96.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_96.png rename to backend/src/main/resources/font/unicode_page_96.png diff --git a/paper/src/main/resources/font/unicode_page_97.png b/backend/src/main/resources/font/unicode_page_97.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_97.png rename to backend/src/main/resources/font/unicode_page_97.png diff --git a/paper/src/main/resources/font/unicode_page_98.png b/backend/src/main/resources/font/unicode_page_98.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_98.png rename to backend/src/main/resources/font/unicode_page_98.png diff --git a/paper/src/main/resources/font/unicode_page_99.png b/backend/src/main/resources/font/unicode_page_99.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_99.png rename to backend/src/main/resources/font/unicode_page_99.png diff --git a/paper/src/main/resources/font/unicode_page_9a.png b/backend/src/main/resources/font/unicode_page_9a.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_9a.png rename to backend/src/main/resources/font/unicode_page_9a.png diff --git a/paper/src/main/resources/font/unicode_page_9b.png b/backend/src/main/resources/font/unicode_page_9b.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_9b.png rename to backend/src/main/resources/font/unicode_page_9b.png diff --git a/paper/src/main/resources/font/unicode_page_9c.png b/backend/src/main/resources/font/unicode_page_9c.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_9c.png rename to backend/src/main/resources/font/unicode_page_9c.png diff --git a/paper/src/main/resources/font/unicode_page_9d.png b/backend/src/main/resources/font/unicode_page_9d.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_9d.png rename to backend/src/main/resources/font/unicode_page_9d.png diff --git a/paper/src/main/resources/font/unicode_page_9e.png b/backend/src/main/resources/font/unicode_page_9e.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_9e.png rename to backend/src/main/resources/font/unicode_page_9e.png diff --git a/paper/src/main/resources/font/unicode_page_9f.png b/backend/src/main/resources/font/unicode_page_9f.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_9f.png rename to backend/src/main/resources/font/unicode_page_9f.png diff --git a/paper/src/main/resources/font/unicode_page_a0.png b/backend/src/main/resources/font/unicode_page_a0.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a0.png rename to backend/src/main/resources/font/unicode_page_a0.png diff --git a/paper/src/main/resources/font/unicode_page_a1.png b/backend/src/main/resources/font/unicode_page_a1.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a1.png rename to backend/src/main/resources/font/unicode_page_a1.png diff --git a/paper/src/main/resources/font/unicode_page_a2.png b/backend/src/main/resources/font/unicode_page_a2.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a2.png rename to backend/src/main/resources/font/unicode_page_a2.png diff --git a/paper/src/main/resources/font/unicode_page_a3.png b/backend/src/main/resources/font/unicode_page_a3.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a3.png rename to backend/src/main/resources/font/unicode_page_a3.png diff --git a/paper/src/main/resources/font/unicode_page_a4.png b/backend/src/main/resources/font/unicode_page_a4.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a4.png rename to backend/src/main/resources/font/unicode_page_a4.png diff --git a/paper/src/main/resources/font/unicode_page_a5.png b/backend/src/main/resources/font/unicode_page_a5.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a5.png rename to backend/src/main/resources/font/unicode_page_a5.png diff --git a/paper/src/main/resources/font/unicode_page_a6.png b/backend/src/main/resources/font/unicode_page_a6.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a6.png rename to backend/src/main/resources/font/unicode_page_a6.png diff --git a/paper/src/main/resources/font/unicode_page_a7.png b/backend/src/main/resources/font/unicode_page_a7.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a7.png rename to backend/src/main/resources/font/unicode_page_a7.png diff --git a/paper/src/main/resources/font/unicode_page_a8.png b/backend/src/main/resources/font/unicode_page_a8.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a8.png rename to backend/src/main/resources/font/unicode_page_a8.png diff --git a/paper/src/main/resources/font/unicode_page_a9.png b/backend/src/main/resources/font/unicode_page_a9.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_a9.png rename to backend/src/main/resources/font/unicode_page_a9.png diff --git a/paper/src/main/resources/font/unicode_page_aa.png b/backend/src/main/resources/font/unicode_page_aa.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_aa.png rename to backend/src/main/resources/font/unicode_page_aa.png diff --git a/paper/src/main/resources/font/unicode_page_ab.png b/backend/src/main/resources/font/unicode_page_ab.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ab.png rename to backend/src/main/resources/font/unicode_page_ab.png diff --git a/paper/src/main/resources/font/unicode_page_ac.png b/backend/src/main/resources/font/unicode_page_ac.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ac.png rename to backend/src/main/resources/font/unicode_page_ac.png diff --git a/paper/src/main/resources/font/unicode_page_ad.png b/backend/src/main/resources/font/unicode_page_ad.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ad.png rename to backend/src/main/resources/font/unicode_page_ad.png diff --git a/paper/src/main/resources/font/unicode_page_ae.png b/backend/src/main/resources/font/unicode_page_ae.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ae.png rename to backend/src/main/resources/font/unicode_page_ae.png diff --git a/paper/src/main/resources/font/unicode_page_af.png b/backend/src/main/resources/font/unicode_page_af.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_af.png rename to backend/src/main/resources/font/unicode_page_af.png diff --git a/paper/src/main/resources/font/unicode_page_b0.png b/backend/src/main/resources/font/unicode_page_b0.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b0.png rename to backend/src/main/resources/font/unicode_page_b0.png diff --git a/paper/src/main/resources/font/unicode_page_b1.png b/backend/src/main/resources/font/unicode_page_b1.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b1.png rename to backend/src/main/resources/font/unicode_page_b1.png diff --git a/paper/src/main/resources/font/unicode_page_b2.png b/backend/src/main/resources/font/unicode_page_b2.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b2.png rename to backend/src/main/resources/font/unicode_page_b2.png diff --git a/paper/src/main/resources/font/unicode_page_b3.png b/backend/src/main/resources/font/unicode_page_b3.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b3.png rename to backend/src/main/resources/font/unicode_page_b3.png diff --git a/paper/src/main/resources/font/unicode_page_b4.png b/backend/src/main/resources/font/unicode_page_b4.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b4.png rename to backend/src/main/resources/font/unicode_page_b4.png diff --git a/paper/src/main/resources/font/unicode_page_b5.png b/backend/src/main/resources/font/unicode_page_b5.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b5.png rename to backend/src/main/resources/font/unicode_page_b5.png diff --git a/paper/src/main/resources/font/unicode_page_b6.png b/backend/src/main/resources/font/unicode_page_b6.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b6.png rename to backend/src/main/resources/font/unicode_page_b6.png diff --git a/paper/src/main/resources/font/unicode_page_b7.png b/backend/src/main/resources/font/unicode_page_b7.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b7.png rename to backend/src/main/resources/font/unicode_page_b7.png diff --git a/paper/src/main/resources/font/unicode_page_b8.png b/backend/src/main/resources/font/unicode_page_b8.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b8.png rename to backend/src/main/resources/font/unicode_page_b8.png diff --git a/paper/src/main/resources/font/unicode_page_b9.png b/backend/src/main/resources/font/unicode_page_b9.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_b9.png rename to backend/src/main/resources/font/unicode_page_b9.png diff --git a/paper/src/main/resources/font/unicode_page_ba.png b/backend/src/main/resources/font/unicode_page_ba.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ba.png rename to backend/src/main/resources/font/unicode_page_ba.png diff --git a/paper/src/main/resources/font/unicode_page_bb.png b/backend/src/main/resources/font/unicode_page_bb.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_bb.png rename to backend/src/main/resources/font/unicode_page_bb.png diff --git a/paper/src/main/resources/font/unicode_page_bc.png b/backend/src/main/resources/font/unicode_page_bc.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_bc.png rename to backend/src/main/resources/font/unicode_page_bc.png diff --git a/paper/src/main/resources/font/unicode_page_bd.png b/backend/src/main/resources/font/unicode_page_bd.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_bd.png rename to backend/src/main/resources/font/unicode_page_bd.png diff --git a/paper/src/main/resources/font/unicode_page_be.png b/backend/src/main/resources/font/unicode_page_be.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_be.png rename to backend/src/main/resources/font/unicode_page_be.png diff --git a/paper/src/main/resources/font/unicode_page_bf.png b/backend/src/main/resources/font/unicode_page_bf.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_bf.png rename to backend/src/main/resources/font/unicode_page_bf.png diff --git a/paper/src/main/resources/font/unicode_page_c0.png b/backend/src/main/resources/font/unicode_page_c0.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c0.png rename to backend/src/main/resources/font/unicode_page_c0.png diff --git a/paper/src/main/resources/font/unicode_page_c1.png b/backend/src/main/resources/font/unicode_page_c1.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c1.png rename to backend/src/main/resources/font/unicode_page_c1.png diff --git a/paper/src/main/resources/font/unicode_page_c2.png b/backend/src/main/resources/font/unicode_page_c2.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c2.png rename to backend/src/main/resources/font/unicode_page_c2.png diff --git a/paper/src/main/resources/font/unicode_page_c3.png b/backend/src/main/resources/font/unicode_page_c3.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c3.png rename to backend/src/main/resources/font/unicode_page_c3.png diff --git a/paper/src/main/resources/font/unicode_page_c4.png b/backend/src/main/resources/font/unicode_page_c4.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c4.png rename to backend/src/main/resources/font/unicode_page_c4.png diff --git a/paper/src/main/resources/font/unicode_page_c5.png b/backend/src/main/resources/font/unicode_page_c5.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c5.png rename to backend/src/main/resources/font/unicode_page_c5.png diff --git a/paper/src/main/resources/font/unicode_page_c6.png b/backend/src/main/resources/font/unicode_page_c6.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c6.png rename to backend/src/main/resources/font/unicode_page_c6.png diff --git a/paper/src/main/resources/font/unicode_page_c7.png b/backend/src/main/resources/font/unicode_page_c7.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c7.png rename to backend/src/main/resources/font/unicode_page_c7.png diff --git a/paper/src/main/resources/font/unicode_page_c8.png b/backend/src/main/resources/font/unicode_page_c8.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c8.png rename to backend/src/main/resources/font/unicode_page_c8.png diff --git a/paper/src/main/resources/font/unicode_page_c9.png b/backend/src/main/resources/font/unicode_page_c9.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_c9.png rename to backend/src/main/resources/font/unicode_page_c9.png diff --git a/paper/src/main/resources/font/unicode_page_ca.png b/backend/src/main/resources/font/unicode_page_ca.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ca.png rename to backend/src/main/resources/font/unicode_page_ca.png diff --git a/paper/src/main/resources/font/unicode_page_cb.png b/backend/src/main/resources/font/unicode_page_cb.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_cb.png rename to backend/src/main/resources/font/unicode_page_cb.png diff --git a/paper/src/main/resources/font/unicode_page_cc.png b/backend/src/main/resources/font/unicode_page_cc.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_cc.png rename to backend/src/main/resources/font/unicode_page_cc.png diff --git a/paper/src/main/resources/font/unicode_page_cd.png b/backend/src/main/resources/font/unicode_page_cd.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_cd.png rename to backend/src/main/resources/font/unicode_page_cd.png diff --git a/paper/src/main/resources/font/unicode_page_ce.png b/backend/src/main/resources/font/unicode_page_ce.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ce.png rename to backend/src/main/resources/font/unicode_page_ce.png diff --git a/paper/src/main/resources/font/unicode_page_cf.png b/backend/src/main/resources/font/unicode_page_cf.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_cf.png rename to backend/src/main/resources/font/unicode_page_cf.png diff --git a/paper/src/main/resources/font/unicode_page_d0.png b/backend/src/main/resources/font/unicode_page_d0.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d0.png rename to backend/src/main/resources/font/unicode_page_d0.png diff --git a/paper/src/main/resources/font/unicode_page_d1.png b/backend/src/main/resources/font/unicode_page_d1.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d1.png rename to backend/src/main/resources/font/unicode_page_d1.png diff --git a/paper/src/main/resources/font/unicode_page_d2.png b/backend/src/main/resources/font/unicode_page_d2.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d2.png rename to backend/src/main/resources/font/unicode_page_d2.png diff --git a/paper/src/main/resources/font/unicode_page_d3.png b/backend/src/main/resources/font/unicode_page_d3.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d3.png rename to backend/src/main/resources/font/unicode_page_d3.png diff --git a/paper/src/main/resources/font/unicode_page_d4.png b/backend/src/main/resources/font/unicode_page_d4.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d4.png rename to backend/src/main/resources/font/unicode_page_d4.png diff --git a/paper/src/main/resources/font/unicode_page_d5.png b/backend/src/main/resources/font/unicode_page_d5.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d5.png rename to backend/src/main/resources/font/unicode_page_d5.png diff --git a/paper/src/main/resources/font/unicode_page_d6.png b/backend/src/main/resources/font/unicode_page_d6.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d6.png rename to backend/src/main/resources/font/unicode_page_d6.png diff --git a/paper/src/main/resources/font/unicode_page_d7.png b/backend/src/main/resources/font/unicode_page_d7.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_d7.png rename to backend/src/main/resources/font/unicode_page_d7.png diff --git a/paper/src/main/resources/font/unicode_page_f9.png b/backend/src/main/resources/font/unicode_page_f9.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_f9.png rename to backend/src/main/resources/font/unicode_page_f9.png diff --git a/paper/src/main/resources/font/unicode_page_fa.png b/backend/src/main/resources/font/unicode_page_fa.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_fa.png rename to backend/src/main/resources/font/unicode_page_fa.png diff --git a/paper/src/main/resources/font/unicode_page_fb.png b/backend/src/main/resources/font/unicode_page_fb.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_fb.png rename to backend/src/main/resources/font/unicode_page_fb.png diff --git a/paper/src/main/resources/font/unicode_page_fc.png b/backend/src/main/resources/font/unicode_page_fc.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_fc.png rename to backend/src/main/resources/font/unicode_page_fc.png diff --git a/paper/src/main/resources/font/unicode_page_fd.png b/backend/src/main/resources/font/unicode_page_fd.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_fd.png rename to backend/src/main/resources/font/unicode_page_fd.png diff --git a/paper/src/main/resources/font/unicode_page_fe.png b/backend/src/main/resources/font/unicode_page_fe.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_fe.png rename to backend/src/main/resources/font/unicode_page_fe.png diff --git a/paper/src/main/resources/font/unicode_page_ff.png b/backend/src/main/resources/font/unicode_page_ff.png similarity index 100% rename from paper/src/main/resources/font/unicode_page_ff.png rename to backend/src/main/resources/font/unicode_page_ff.png diff --git a/backend/src/main/resources/font/unifont.zip b/backend/src/main/resources/font/unifont.zip new file mode 100644 index 0000000..dbb251e Binary files /dev/null and b/backend/src/main/resources/font/unifont.zip differ diff --git a/backend/src/main/resources/font/unifont_jp.zip b/backend/src/main/resources/font/unifont_jp.zip new file mode 100644 index 0000000..21f5e90 Binary files /dev/null and b/backend/src/main/resources/font/unifont_jp.zip differ diff --git a/paper/src/main/resources/schema/h2.sql b/backend/src/main/resources/schema/h2.sql similarity index 100% rename from paper/src/main/resources/schema/h2.sql rename to backend/src/main/resources/schema/h2.sql diff --git a/paper/src/main/resources/schema/mariadb.sql b/backend/src/main/resources/schema/mariadb.sql similarity index 100% rename from paper/src/main/resources/schema/mariadb.sql rename to backend/src/main/resources/schema/mariadb.sql diff --git a/paper/src/main/resources/schema/mysql.sql b/backend/src/main/resources/schema/mysql.sql similarity index 100% rename from paper/src/main/resources/schema/mysql.sql rename to backend/src/main/resources/schema/mysql.sql diff --git a/paper/src/main/resources/schema/sqlite.sql b/backend/src/main/resources/schema/sqlite.sql similarity index 100% rename from paper/src/main/resources/schema/sqlite.sql rename to backend/src/main/resources/schema/sqlite.sql diff --git a/backend/src/main/resources/space_split.png b/backend/src/main/resources/space_split.png new file mode 100644 index 0000000..3ca3f1d Binary files /dev/null and b/backend/src/main/resources/space_split.png differ diff --git a/backend/src/main/resources/tmp/accented.json b/backend/src/main/resources/tmp/accented.json new file mode 100644 index 0000000..bfe0ff2 --- /dev/null +++ b/backend/src/main/resources/tmp/accented.json @@ -0,0 +1,84 @@ +{ + "providers": [ + { + "type": "bitmap", + "chars": [ + "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf", + "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3", + "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa", + "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b", + "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b", + "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129", + "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c", + "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d", + "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d", + "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d", + "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d", + "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f", + "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403", + "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f", + "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57", + "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb", + "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116", + "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc", + "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761", + "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda", + "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3", + "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf", + "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7", + "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e", + "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499", + "\u04a0\u04a1\u04aa\u04ab\u01f6\u26a0\u24ea\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468", + "\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u24b6\u24b7\u24b8\u24b9\u24ba", + "\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca", + "\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da", + "\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u0327", + "\u0282\u0290\u0276\u01cd\u01ce\u01de\u01df\u01fa\u01fb\u0202\u0203\u0226\u0227\u01e0\u01e1\u1e00", + "\u1e01\u0200\u0201\u1e06\u1e07\u1e04\u1e05\u1d6c\u1e08\u1e09\u1e10\u1e11\u1e12\u1e13\u1e0e\u1e0f", + "\u1e0c\u1d6d\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1c\u1e1d\u0228\u0229\u1e1a\u1e1b\u0204\u0205", + "\u0206\u1d6e\u01f4\u01f5\u01e6\u1e26\u1e27\u1e28\u1e29\u1e2a\u1e2b\u021e\u021f\u1e24\u1e96\u1e2e", + "\u1e2f\u020a\u020b\u01cf\u01d0\u0208\u0209\u1e2c\u1e2d\u01f0\u0237\u01e8\u01e9\u1e32\u1e33\u1e34", + "\u1e35\u1e3a\u1e3b\u1e3c\u1e3d\u1e36\u1e37\u1e38\u1e39\u2c62\u1e3e\u1e3f\u1e42\u1e43\u1d6f\u1e44", + "\u1e45\u1e46\u1e47\u1e4a\u1e4b\u01f8\u01f9\u1e48\u1e49\u1d70\u01ec\u01ed\u022c\u022d\u1e4c\u1e4d", + "\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u020e\u020f\u022a\u022b\u01d1\u01d2\u022e\u022f\u0230\u0231", + "\u020c\u020d\u01ea\u1e54\u1e55\u1d71\u0212\u0213\u1e58\u1e59\u1e5c\u1e5d\u1e5e\u1e5f\u0210\u0211", + "\u1e5a\u1d73\u1d72\u1e64\u1e65\u1e66\u1e67\u1e62\u1e63\u1e68\u1e69\u1d74\u1e70\u1e71\u1e6e\u1e6f", + "\u1e6c\u1e97\u1d75\u1e72\u1e73\u1e76\u1e77\u1e78\u1e79\u1e7a\u1e7b\u01d3\u01d4\u01d5\u01d6\u01d7", + "\u01d8\u01d9\u01da\u01db\u01dc\u1e74\u1e75\u0214\u0215\u0216\u1e7e\u1e7f\u1e7c\u1e7d\u1e86\u1e87", + "\u1e88\u1e89\u1e98\u1e8c\u1e8d\u1e8a\u1e8b\u0232\u0233\u1e8e\u1e8f\u1e99\u1e94\u1e95\u1e90\u1e91", + "\u1e93\u1d76\u01ee\u01ef\u1e9b\ua73e\ua73f\u01e2\u01e3\u1d7a\u1efb\u1d02\u1d14\uab63\u0238\u02a3", + "\u02a5\u02a4\u02a9\u02aa\u02ab\u0239\u02a8\u02a6\u02a7\uab50\uab51\u20a7\u1efa\ufb2e\ufb2f\u0180", + "\u0182\u0183\u0187\u0188\u018a\u018b\u018c\u0193\u01e4\u01e5\u0197\u0196\u0269\u0198\u0199\u019d", + "\u01a4\u01a5\u027d\u01a6\u01ac\u01ad\u01ab\u01ae\u0217\u01b1\u019c\u01b3\u01b4\u01b5\u01b6\u01a2", + "\u01a3\u0222\u0223\u02ad\u02ae\u02af\ufb14\ufb15\ufb17\ufb16\ufb13\u04d0\u04d1\u04d2\u04d3\u04f6", + "\u04f7\u0494\u0495\u04d6\u04d7\u04bc\u04bd\u04be\u04bf\u04da\u04db\u04dc\u04dd\u04c1\u04c2\u04de", + "\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04ea\u04eb\u04f0\u04f1\u04ee\u04ef\u04f2\u04f3\u04f4", + "\u04f5\u04f8\u04f9\u04ec\u04ed\u0476\u0477\u04d4\u04fa\u0502\ua682\ua680\ua688\u052a\u052c\ua684", + "\u0504\u0510\u04e0\u0506\u048a\u04c3\u049e\u049c\u051e\u051a\u04c5\u052e\u0512\u0520\u0508\u0514", + "\u04cd\u04c9\u0528\u04c7\u04a4\u0522\u050a\u04a8\u0524\u04a6\u048e\u0516\u050c\ua690\u04ac\ua68a", + "\ua68c\u050e\u04b2\u04fc\u04fe\u0526\ua694\u04b4\ua68e\u04b6\u04cb\u04b8\ua692\ua696\ua686\u048c", + "\u0518\u051c\u04d5\u04fb\u0503\ua683\ua681\ua689\u052b\u052d\ua685\u0505\u0511\u04e1\u0507\u048b", + "\u04c4\u049f\u049d\u051f\u051b\u04c6\u052f\u0513\u0521\u0509\u0515\u04ce\u04ca\u0529\u04c8\u04a5", + "\u0523\u050b\u04a9\u0525\u04a7\u048f\u0517\u050d\ua691\u04ad\ua68b\ua68d\u050f\u04b3\u04fd\u04ff", + "\u0527\ua695\u04b5\ua68f\u04b7\u04cc\u04b9\ua693\ua697\ua687\u048d\u0519\u051d\u1f08\u1f00\u1f09", + "\u1f01\u1f0a\u1f02\u1f0b\u1f03\u1f0c\u1f04\u1f0d\u1f05\u1f0e\u1f06\u1f0f\u1f07\u1fba\u1f70\u1fb8", + "\u1fb0\u1fb9\u1fb1\u1fbb\u1f71\u1f88\u1f80\u1f89\u1f81\u1f8a\u1f82\u1f8b\u1f83\u1f8c\u1f84\u1f8d", + "\u1f85\u1f8e\u1f86\u1f8f\u1f87\u1fbc\u1fb4\u1fb6\u1fb7\u1fb2\u1fb3\u1f18\u1f10\u1f19\u1f11\u1f1a", + "\u1f12\u1f1b\u1f13\u1f1c\u1f14\u1f1d\u1f15\u1fc8\u1fc9\u1f72\u1f73\u1f28\u1f20\u1fca\u1f74\u1f29", + "\u1f21\u1f2a\u1f22\u1f2b\u1f23\u1f2c\u1f24\u1f2d\u1f25\u1f2e\u1f26\u1f2f\u1f27\u1f98\u1f90\u1f99", + "\u1f91\u1f9a\u1f92\u1f9b\u1f93\u1f9c\u1f94\u1f9d\u1f95\u1f9e\u1f96\u1f9f\u1f97\u1fcb\u1f75\u1fcc", + "\u1fc3\u1fc2\u1fc4\u1fc6\u1fc7\u1fda\u1f76\u1fdb\u1f77\u1f38\u1f30\u1f39\u1f31\u1f3a\u1f32\u1f3b", + "\u1f33\u1f3c\u1f34\u1f3d\u1f35\u1f3e\u1f36\u1f3f\u1f37\u1fd8\u1fd0\u1fd9\u1fd1\u1fd2\u1fd3\u1fd6", + "\u1fd7\u1ff8\u1f78\u1ff9\u1f79\u1f48\u1f40\u1f49\u1f41\u1f4a\u1f42\u1f4b\u1f43\u1f4c\u1f44\u1f4d", + "\u1f45\u1fec\u1fe4\u1fe5\u1fea\u1f7a\u1feb\u1f7b\u1f59\u1f51\u1f5b\u1f53\u1f5d\u1f55\u1f5f\u1f57", + "\u1fe8\u1fe0\u1fe9\u1fe1\u03d3\u03d4\u1fe2\u1fe3\u1fe7\u1f50\u1f52\u1f54\u1fe6\u1f56\u1ffa\u1f7c", + "\u1ffb\u1f7d\u1f68\u1f60\u1f69\u1f61\u1f6a\u1f62\u1f6b\u1f63\u1f6c\u1f64\u1f6d\u1f65\u1f6e\u1f66", + "\u1f6f\u1f67\u1fa8\u1fa0\u1fa9\u1fa1\u1faa\u1fa2\u1fab\u1fa3\u1fac\u1fa4\u1fad\u1fa5\u1fae\u1fa6", + "\u1faf\u1fa7\u1ffc\u1ff3\u1ff2\u1ff4\u1ff6\u1ff7\u262f\u2610\u2611\u2612\u018d\u01ba\u2c7e\u023f", + "\u2c7f\u0240\u1d80\ua7c4\ua794\u1d81\u1d82\u1d83\ua795\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a", + "\u1d8b\u1d8c\u1d8d\ua7c6\u1d8e\u1d8f\u1d90\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a", + "\u1e9a\u2152\u2158\u20a8\u20af\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + ] + } + ] +} \ No newline at end of file diff --git a/backend/src/main/resources/tmp/ascii.json b/backend/src/main/resources/tmp/ascii.json new file mode 100644 index 0000000..2c83e40 --- /dev/null +++ b/backend/src/main/resources/tmp/ascii.json @@ -0,0 +1,25 @@ +{ + "providers": [ + { + "type": "bitmap", + "chars": [ + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f", + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f", + "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f", + "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f", + "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f", + "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00a3\u0000\u0000\u0192", + "\u0000\u0000\u0000\u0000\u0000\u0000\u00aa\u00ba\u0000\u0000\u00ac\u0000\u0000\u0000\u00ab\u00bb", + "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510", + "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567", + "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u2205\u2208\u0000", + "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u0000\u221a\u207f\u00b2\u25a0\u0000" + ] + } + ] +} \ No newline at end of file diff --git a/backend/src/main/resources/tmp/ascii_sga.json b/backend/src/main/resources/tmp/ascii_sga.json new file mode 100644 index 0000000..3980eb4 --- /dev/null +++ b/backend/src/main/resources/tmp/ascii_sga.json @@ -0,0 +1,25 @@ +{ + "providers": [ + { + "type": "bitmap", + "chars": [ + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004A\u004B\u004C\u004D\u004E\u004F", + "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005A\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C\u006D\u006E\u006F", + "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007A\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + ] + } + ] +} diff --git a/backend/src/main/resources/tmp/asciillager.json b/backend/src/main/resources/tmp/asciillager.json new file mode 100644 index 0000000..9403c7d --- /dev/null +++ b/backend/src/main/resources/tmp/asciillager.json @@ -0,0 +1,14 @@ +{ + "providers": [ + { + "type": "bitmap", + "chars": [ + "\u0021\u002C\u002D\u002E\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003F\u0061", + "\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C\u006D\u006E\u006F\u0070\u0071", + "\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007A\u0041\u0042\u0043\u0044\u0045\u0046\u0047", + "\u0048\u0049\u004A\u004B\u004C\u004D\u004E\u004F\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057", + "\u0058\u0059\u005A\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + ] + } + ] +} diff --git a/backend/src/main/resources/tmp/nonlatin_european.json b/backend/src/main/resources/tmp/nonlatin_european.json new file mode 100644 index 0000000..9093438 --- /dev/null +++ b/backend/src/main/resources/tmp/nonlatin_european.json @@ -0,0 +1,76 @@ +{ + "providers": [ + { + "type": "bitmap", + "chars": [ + "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392", + "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3", + "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba", + "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402", + "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a", + "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a", + "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b", + "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b", + "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d", + "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8", + "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04c0\u0472\u0473", + "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122", + "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2", + "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd", + "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5", + "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036", + "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117", + "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665", + "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u02ac\u26a1", + "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2e41\u2e4b\u295d\u1614\u0190\u07c8", + "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\ua7b0\ua780\u0500\ua779\u1d1a\u27d8\u2229", + "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c", + "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540", + "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551", + "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a", + "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a", + "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538", + "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c", + "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5", + "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8", + "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535", + "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af", + "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0", + "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0", + "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0", + "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0", + "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0", + "\u05f1\u05f2\u05be\u05c3\u05c6\u00b4\u00a8\u1d00\u0299\u1d04\u1d05\u1d07\ua730\u0262\u029c\u1d0a", + "\u1d0b\u029f\u1d0d\u0274\u1d0f\u1d18\ua7af\u0280\ua731\u1d1b\u1d1c\u1d20\u1d21\u028f\u1d22\u00a7", + "\u0271\u0273\u0272\u0288\u0256\u0261\u02a1\u0255\u0291\u0278\u029d\u02a2\u027b\u0281\u0266\u028b", + "\u0270\u026c\u026e\u0298\u01c0\u01c3\u01c2\u01c1\u0253\u0257\u1d91\u0284\u0260\u029b\u0267\u026b", + "\u0268\u0289\u028a\u0258\u0275\u0264\u025c\u025e\u0251\u0252\u025a\u025d\u0181\u0189\u0191\u01a9", + "\u01b2\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae", + "\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be", + "\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c7\u10cd\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6", + "\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6", + "\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6", + "\u10f7\u10f8\u10f9\u10fa\u10fb\u10fc\u10fd\u10fe\u10ff\ufb4a\ufb2b\ufb4e\ufb44\ufb3b\ufb1f\ufb1d", + "\ufb4b\ufb35\ufb4c\ufb31\ua727\ua726\u027a\u2c71\u02a0\u0297\u0296\u026d\u0277\u027f\u0285\u0286", + "\u0293\u029a\u20aa\u20be\u058a\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d21\u2d07\u2d08\u2d09", + "\u2d0a\u2d0b\u2d0c\u2d22\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d23\u2d13\u2d14\u2d15\u2d16\u2d17", + "\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d24\u2d1f\u2d20\u2d25\u215b\u215c\u215d\u215e\u2153", + "\u2154\u2709\u2602\u2614\u2604\u26c4\u2603\u231b\u231a\u2690\u270e\u2763\u2664\u2667\u2661\u2662", + "\u26c8\u2630\u2631\u2633\u2634\u2636\u2637\u2194\u21d2\u21cf\u21d4\u21f5\u2200\u2203\u2204\u2209", + "\u220b\u220c\u2282\u2283\u2284\u2285\u2227\u2228\u22bb\u22bc\u22bd\u2225\u2262\u22c6\u2211\u22a4", + "\u22a5\u22a2\u22a8\u2254\u2201\u2234\u2235\u221b\u221c\u2202\u22c3\u2286\u2287\u25a1\u25b3\u25b7", + "\u25bd\u25c1\u25c6\u25c7\u25cb\u25ce\u2606\u2605\u2718\u2080\u2081\u2082\u2083\u2084\u2085\u2086", + "\u2087\u2088\u2089\u208a\u208b\u208c\u208d\u208e\u222b\u222e\u221d\u2300\u2302\u2318\u3012\u027c", + "\u0184\u0185\u1e9f\u023d\u019a\u019b\u0220\u019e\u019f\u01a7\u01a8\u01aa\u01b8\u01b9\u01bb\u01bc", + "\u01bd\u01be\u0221\u0234\u0235\u0236\u023a\u2c65\u023b\u023c\u0246\u0247\u023e\u2c66\u0241\u0242", + "\u0243\u0244\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u1e9c\u1e9d\u1efc\u1efd\u1efe\u1eff", + "\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d", + "\ud800\udf3e\ud800\udf3f\ud800\udf40\ud800\udf41\ud800\udf42\ud800\udf43\ud800\udf44\ud800\udf45\ud800\udf46\ud800\udf47\ud800\udf48\ud800\udf49\ud800\udf4a\ud83c\udf27\ud83d\udd25\ud83c\udf0a", + "\u2150\u2151\u2155\u2156\u2157\u2159\u215a\u215f\u2189\ud83d\udde1\ud83c\udff9\ud83e\ude93\ud83d\udd31\ud83c\udfa3\ud83e\uddea\u2697", + "\u2bea\u2beb\u2c6d\ud83d\udee1\u2702\ud83c\udf56\ud83e\udea3\ud83d\udd14\u23f3\u2691\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5", + "\u20a6\u20a9\u20ab\u20ad\u20ae\u20b0\u20b1\u20b2\u20b3\u20b5\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb", + "\u20bc\u20bf\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + ] + } + ] +} \ No newline at end of file diff --git a/paper/src/main/resources/font/unicode.json b/backend/src/main/resources/tmp/unicode.json similarity index 91% rename from paper/src/main/resources/font/unicode.json rename to backend/src/main/resources/tmp/unicode.json index 2e72eb9..8e1816c 100644 --- a/paper/src/main/resources/font/unicode.json +++ b/backend/src/main/resources/tmp/unicode.json @@ -1,196 +1,8 @@ { "providers": [ - { - "type": "space", - "advances": { - " ": 4, - "\u200c": 0 - } - }, { "type": "bitmap", - "file": "minecraft:font/nonlatin_european.png", - "ascent": %ascent%, - "chars": [ - "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392", - "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3", - "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba", - "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402", - "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a", - "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a", - "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b", - "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b", - "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d", - "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8", - "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04c0\u0472\u0473", - "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122", - "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2", - "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd", - "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5", - "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036", - "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117", - "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665", - "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u02ac\u26a1", - "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2e41\u2e4b\u295d\u1614\u0190\u07c8", - "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\ua7b0\ua780\u0500\ua779\u1d1a\u27d8\u2229", - "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c", - "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540", - "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551", - "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a", - "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a", - "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538", - "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c", - "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5", - "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8", - "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535", - "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af", - "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0", - "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0", - "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0", - "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0", - "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0", - "\u05f1\u05f2\u05be\u05c3\u05c6\u00b4\u00a8\u1d00\u0299\u1d04\u1d05\u1d07\ua730\u0262\u029c\u1d0a", - "\u1d0b\u029f\u1d0d\u0274\u1d0f\u1d18\ua7af\u0280\ua731\u1d1b\u1d1c\u1d20\u1d21\u028f\u1d22\u00a7", - "\u0271\u0273\u0272\u0288\u0256\u0261\u02a1\u0255\u0291\u0278\u029d\u02a2\u027b\u0281\u0266\u028b", - "\u0270\u026c\u026e\u0298\u01c0\u01c3\u01c2\u01c1\u0253\u0257\u1d91\u0284\u0260\u029b\u0267\u026b", - "\u0268\u0289\u028a\u0258\u0275\u0264\u025c\u025e\u0251\u0252\u025a\u025d\u0181\u0189\u0191\u01a9", - "\u01b2\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae", - "\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be", - "\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c7\u10cd\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6", - "\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6", - "\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6", - "\u10f7\u10f8\u10f9\u10fa\u10fb\u10fc\u10fd\u10fe\u10ff\ufb4a\ufb2b\ufb4e\ufb44\ufb3b\ufb1f\ufb1d", - "\ufb4b\ufb35\ufb4c\ufb31\ua727\ua726\u027a\u2c71\u02a0\u0297\u0296\u026d\u0277\u027f\u0285\u0286", - "\u0293\u029a\u20aa\u20be\u058a\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d21\u2d07\u2d08\u2d09", - "\u2d0a\u2d0b\u2d0c\u2d22\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d23\u2d13\u2d14\u2d15\u2d16\u2d17", - "\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d24\u2d1f\u2d20\u2d25\u215b\u215c\u215d\u215e\u2153", - "\u2154\u2709\u2602\u2614\u2604\u26c4\u2603\u231b\u231a\u2690\u270e\u2763\u2664\u2667\u2661\u2662", - "\u26c8\u2630\u2631\u2633\u2634\u2636\u2637\u2194\u21d2\u21cf\u21d4\u21f5\u2200\u2203\u2204\u2209", - "\u220b\u220c\u2282\u2283\u2284\u2285\u2227\u2228\u22bb\u22bc\u22bd\u2225\u2262\u22c6\u2211\u22a4", - "\u22a5\u22a2\u22a8\u2254\u2201\u2234\u2235\u221b\u221c\u2202\u22c3\u2286\u2287\u25a1\u25b3\u25b7", - "\u25bd\u25c1\u25c6\u25c7\u25cb\u25ce\u2606\u2605\u2718\u2080\u2081\u2082\u2083\u2084\u2085\u2086", - "\u2087\u2088\u2089\u208a\u208b\u208c\u208d\u208e\u222b\u222e\u221d\u2300\u2302\u2318\u3012\u027c", - "\u0184\u0185\u1e9f\u023d\u019a\u019b\u0220\u019e\u019f\u01a7\u01a8\u01aa\u01b8\u01b9\u01bb\u01bc", - "\u01bd\u01be\u0221\u0234\u0235\u0236\u023a\u2c65\u023b\u023c\u0246\u0247\u023e\u2c66\u0241\u0242", - "\u0243\u0244\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u1e9c\u1e9d\u1efc\u1efd\u1efe\u1eff", - "\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d", - "\ud800\udf3e\ud800\udf3f\ud800\udf40\ud800\udf41\ud800\udf42\ud800\udf43\ud800\udf44\ud800\udf45\ud800\udf46\ud800\udf47\ud800\udf48\ud800\udf49\ud800\udf4a\ud83c\udf27\ud83d\udd25\ud83c\udf0a", - "\u2150\u2151\u2155\u2156\u2157\u2159\u215a\u215f\u2189\ud83d\udde1\ud83c\udff9\ud83e\ude93\ud83d\udd31\ud83c\udfa3\ud83e\uddea\u2697", - "\u2bea\u2beb\u2c6d\ud83d\udee1\u2702\ud83c\udf56\ud83e\udea3\ud83d\udd14\u23f3\u2691\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5", - "\u20a6\u20a9\u20ab\u20ad\u20ae\u20b0\u20b1\u20b2\u20b3\u20b5\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb", - "\u20bc\u20bf\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" - ] - }, - { - "type": "bitmap", - "file": "minecraft:font/accented.png", - "height": 12, - "ascent": %ASCENT%, - "chars": [ - "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf", - "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3", - "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa", - "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b", - "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b", - "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129", - "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c", - "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d", - "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d", - "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d", - "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d", - "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f", - "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403", - "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f", - "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57", - "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb", - "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116", - "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc", - "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761", - "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda", - "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3", - "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf", - "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7", - "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e", - "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499", - "\u04a0\u04a1\u04aa\u04ab\u01f6\u26a0\u24ea\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468", - "\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u24b6\u24b7\u24b8\u24b9\u24ba", - "\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca", - "\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da", - "\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u0327", - "\u0282\u0290\u0276\u01cd\u01ce\u01de\u01df\u01fa\u01fb\u0202\u0203\u0226\u0227\u01e0\u01e1\u1e00", - "\u1e01\u0200\u0201\u1e06\u1e07\u1e04\u1e05\u1d6c\u1e08\u1e09\u1e10\u1e11\u1e12\u1e13\u1e0e\u1e0f", - "\u1e0c\u1d6d\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1c\u1e1d\u0228\u0229\u1e1a\u1e1b\u0204\u0205", - "\u0206\u1d6e\u01f4\u01f5\u01e6\u1e26\u1e27\u1e28\u1e29\u1e2a\u1e2b\u021e\u021f\u1e24\u1e96\u1e2e", - "\u1e2f\u020a\u020b\u01cf\u01d0\u0208\u0209\u1e2c\u1e2d\u01f0\u0237\u01e8\u01e9\u1e32\u1e33\u1e34", - "\u1e35\u1e3a\u1e3b\u1e3c\u1e3d\u1e36\u1e37\u1e38\u1e39\u2c62\u1e3e\u1e3f\u1e42\u1e43\u1d6f\u1e44", - "\u1e45\u1e46\u1e47\u1e4a\u1e4b\u01f8\u01f9\u1e48\u1e49\u1d70\u01ec\u01ed\u022c\u022d\u1e4c\u1e4d", - "\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u020e\u020f\u022a\u022b\u01d1\u01d2\u022e\u022f\u0230\u0231", - "\u020c\u020d\u01ea\u1e54\u1e55\u1d71\u0212\u0213\u1e58\u1e59\u1e5c\u1e5d\u1e5e\u1e5f\u0210\u0211", - "\u1e5a\u1d73\u1d72\u1e64\u1e65\u1e66\u1e67\u1e62\u1e63\u1e68\u1e69\u1d74\u1e70\u1e71\u1e6e\u1e6f", - "\u1e6c\u1e97\u1d75\u1e72\u1e73\u1e76\u1e77\u1e78\u1e79\u1e7a\u1e7b\u01d3\u01d4\u01d5\u01d6\u01d7", - "\u01d8\u01d9\u01da\u01db\u01dc\u1e74\u1e75\u0214\u0215\u0216\u1e7e\u1e7f\u1e7c\u1e7d\u1e86\u1e87", - "\u1e88\u1e89\u1e98\u1e8c\u1e8d\u1e8a\u1e8b\u0232\u0233\u1e8e\u1e8f\u1e99\u1e94\u1e95\u1e90\u1e91", - "\u1e93\u1d76\u01ee\u01ef\u1e9b\ua73e\ua73f\u01e2\u01e3\u1d7a\u1efb\u1d02\u1d14\uab63\u0238\u02a3", - "\u02a5\u02a4\u02a9\u02aa\u02ab\u0239\u02a8\u02a6\u02a7\uab50\uab51\u20a7\u1efa\ufb2e\ufb2f\u0180", - "\u0182\u0183\u0187\u0188\u018a\u018b\u018c\u0193\u01e4\u01e5\u0197\u0196\u0269\u0198\u0199\u019d", - "\u01a4\u01a5\u027d\u01a6\u01ac\u01ad\u01ab\u01ae\u0217\u01b1\u019c\u01b3\u01b4\u01b5\u01b6\u01a2", - "\u01a3\u0222\u0223\u02ad\u02ae\u02af\ufb14\ufb15\ufb17\ufb16\ufb13\u04d0\u04d1\u04d2\u04d3\u04f6", - "\u04f7\u0494\u0495\u04d6\u04d7\u04bc\u04bd\u04be\u04bf\u04da\u04db\u04dc\u04dd\u04c1\u04c2\u04de", - "\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04ea\u04eb\u04f0\u04f1\u04ee\u04ef\u04f2\u04f3\u04f4", - "\u04f5\u04f8\u04f9\u04ec\u04ed\u0476\u0477\u04d4\u04fa\u0502\ua682\ua680\ua688\u052a\u052c\ua684", - "\u0504\u0510\u04e0\u0506\u048a\u04c3\u049e\u049c\u051e\u051a\u04c5\u052e\u0512\u0520\u0508\u0514", - "\u04cd\u04c9\u0528\u04c7\u04a4\u0522\u050a\u04a8\u0524\u04a6\u048e\u0516\u050c\ua690\u04ac\ua68a", - "\ua68c\u050e\u04b2\u04fc\u04fe\u0526\ua694\u04b4\ua68e\u04b6\u04cb\u04b8\ua692\ua696\ua686\u048c", - "\u0518\u051c\u04d5\u04fb\u0503\ua683\ua681\ua689\u052b\u052d\ua685\u0505\u0511\u04e1\u0507\u048b", - "\u04c4\u049f\u049d\u051f\u051b\u04c6\u052f\u0513\u0521\u0509\u0515\u04ce\u04ca\u0529\u04c8\u04a5", - "\u0523\u050b\u04a9\u0525\u04a7\u048f\u0517\u050d\ua691\u04ad\ua68b\ua68d\u050f\u04b3\u04fd\u04ff", - "\u0527\ua695\u04b5\ua68f\u04b7\u04cc\u04b9\ua693\ua697\ua687\u048d\u0519\u051d\u1f08\u1f00\u1f09", - "\u1f01\u1f0a\u1f02\u1f0b\u1f03\u1f0c\u1f04\u1f0d\u1f05\u1f0e\u1f06\u1f0f\u1f07\u1fba\u1f70\u1fb8", - "\u1fb0\u1fb9\u1fb1\u1fbb\u1f71\u1f88\u1f80\u1f89\u1f81\u1f8a\u1f82\u1f8b\u1f83\u1f8c\u1f84\u1f8d", - "\u1f85\u1f8e\u1f86\u1f8f\u1f87\u1fbc\u1fb4\u1fb6\u1fb7\u1fb2\u1fb3\u1f18\u1f10\u1f19\u1f11\u1f1a", - "\u1f12\u1f1b\u1f13\u1f1c\u1f14\u1f1d\u1f15\u1fc8\u1fc9\u1f72\u1f73\u1f28\u1f20\u1fca\u1f74\u1f29", - "\u1f21\u1f2a\u1f22\u1f2b\u1f23\u1f2c\u1f24\u1f2d\u1f25\u1f2e\u1f26\u1f2f\u1f27\u1f98\u1f90\u1f99", - "\u1f91\u1f9a\u1f92\u1f9b\u1f93\u1f9c\u1f94\u1f9d\u1f95\u1f9e\u1f96\u1f9f\u1f97\u1fcb\u1f75\u1fcc", - "\u1fc3\u1fc2\u1fc4\u1fc6\u1fc7\u1fda\u1f76\u1fdb\u1f77\u1f38\u1f30\u1f39\u1f31\u1f3a\u1f32\u1f3b", - "\u1f33\u1f3c\u1f34\u1f3d\u1f35\u1f3e\u1f36\u1f3f\u1f37\u1fd8\u1fd0\u1fd9\u1fd1\u1fd2\u1fd3\u1fd6", - "\u1fd7\u1ff8\u1f78\u1ff9\u1f79\u1f48\u1f40\u1f49\u1f41\u1f4a\u1f42\u1f4b\u1f43\u1f4c\u1f44\u1f4d", - "\u1f45\u1fec\u1fe4\u1fe5\u1fea\u1f7a\u1feb\u1f7b\u1f59\u1f51\u1f5b\u1f53\u1f5d\u1f55\u1f5f\u1f57", - "\u1fe8\u1fe0\u1fe9\u1fe1\u03d3\u03d4\u1fe2\u1fe3\u1fe7\u1f50\u1f52\u1f54\u1fe6\u1f56\u1ffa\u1f7c", - "\u1ffb\u1f7d\u1f68\u1f60\u1f69\u1f61\u1f6a\u1f62\u1f6b\u1f63\u1f6c\u1f64\u1f6d\u1f65\u1f6e\u1f66", - "\u1f6f\u1f67\u1fa8\u1fa0\u1fa9\u1fa1\u1faa\u1fa2\u1fab\u1fa3\u1fac\u1fa4\u1fad\u1fa5\u1fae\u1fa6", - "\u1faf\u1fa7\u1ffc\u1ff3\u1ff2\u1ff4\u1ff6\u1ff7\u262f\u2610\u2611\u2612\u018d\u01ba\u2c7e\u023f", - "\u2c7f\u0240\u1d80\ua7c4\ua794\u1d81\u1d82\u1d83\ua795\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a", - "\u1d8b\u1d8c\u1d8d\ua7c6\u1d8e\u1d8f\u1d90\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a", - "\u1e9a\u2152\u2158\u20a8\u20af\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" - ] - }, - { - "type": "bitmap", - "file": "minecraft:font/ascii.png", - "ascent": %ascent%, - "chars": [ - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f", - "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f", - "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f", - "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f", - "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f", - "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00a3\u0000\u0000\u0192", - "\u0000\u0000\u0000\u0000\u0000\u0000\u00aa\u00ba\u0000\u0000\u00ac\u0000\u0000\u0000\u00ab\u00bb", - "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510", - "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567", - "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u2205\u2208\u0000", - "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u0000\u221a\u207f\u00b2\u25a0\u0000" - ] - }, - { - "type": "bitmap", - "file": "minecraft:font/unicode_page_00.png", - "ascent": %ascent%, + "file": "00", "chars": [ "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f", "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f", @@ -212,8 +24,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_01.png", - "ascent": %ascent%, + "file": "01", "chars": [ "\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f", "\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u011e\u011f", @@ -235,8 +46,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_02.png", - "ascent": %ascent%, + "file": "02", "chars": [ "\u0200\u0201\u0202\u0203\u0204\u0205\u0206\u0207\u0208\u0209\u020a\u020b\u020c\u020d\u020e\u020f", "\u0210\u0211\u0212\u0213\u0214\u0215\u0216\u0217\u0218\u0219\u021a\u021b\u021c\u021d\u021e\u021f", @@ -258,8 +68,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_03.png", - "ascent": %ascent%, + "file": "03", "chars": [ "\u0300\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f", "\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031a\u031b\u031c\u031d\u031e\u031f", @@ -281,8 +90,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_04.png", - "ascent": %ascent%, + "file": "04", "chars": [ "\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f", "\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f", @@ -304,8 +112,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_05.png", - "ascent": %ascent%, + "file": "05", "chars": [ "\u0500\u0501\u0502\u0503\u0504\u0505\u0506\u0507\u0508\u0509\u050a\u050b\u050c\u050d\u050e\u050f", "\u0510\u0511\u0512\u0513\u0514\u0515\u0516\u0517\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f", @@ -327,8 +134,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_06.png", - "ascent": %ascent%, + "file": "06", "chars": [ "\u0600\u0601\u0602\u0603\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u060b\u060c\u060d\u060e\u060f", "\u0610\u0611\u0612\u0613\u0614\u0615\u0616\u0617\u0618\u0619\u061a\u061b\u061c\u061d\u061e\u061f", @@ -350,8 +156,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_07.png", - "ascent": %ascent%, + "file": "07", "chars": [ "\u0700\u0701\u0702\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u070a\u070b\u070c\u070d\u070e\u070f", "\u0710\u0711\u0712\u0713\u0714\u0715\u0716\u0717\u0718\u0719\u071a\u071b\u071c\u071d\u071e\u071f", @@ -373,8 +178,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_09.png", - "ascent": %ascent%, + "file": "09", "chars": [ "\u0900\u0901\u0902\u0903\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f", "\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c\u091d\u091e\u091f", @@ -396,8 +200,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_0a.png", - "ascent": %ascent%, + "file": "0a", "chars": [ "\u0a00\u0a01\u0a02\u0a03\u0a04\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a\u0a0b\u0a0c\u0a0d\u0a0e\u0a0f", "\u0a10\u0a11\u0a12\u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\u0a1b\u0a1c\u0a1d\u0a1e\u0a1f", @@ -419,8 +222,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_0b.png", - "ascent": %ascent%, + "file": "0b", "chars": [ "\u0b00\u0b01\u0b02\u0b03\u0b04\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0d\u0b0e\u0b0f", "\u0b10\u0b11\u0b12\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f", @@ -442,8 +244,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_0c.png", - "ascent": %ascent%, + "file": "0c", "chars": [ "\u0c00\u0c01\u0c02\u0c03\u0c04\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0d\u0c0e\u0c0f", "\u0c10\u0c11\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f", @@ -465,8 +266,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_0d.png", - "ascent": %ascent%, + "file": "0d", "chars": [ "\u0d00\u0d01\u0d02\u0d03\u0d04\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0d\u0d0e\u0d0f", "\u0d10\u0d11\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f", @@ -488,8 +288,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_0e.png", - "ascent": %ascent%, + "file": "0e", "chars": [ "\u0e00\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f", "\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f", @@ -511,8 +310,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_0f.png", - "ascent": %ascent%, + "file": "0f", "chars": [ "\u0f00\u0f01\u0f02\u0f03\u0f04\u0f05\u0f06\u0f07\u0f08\u0f09\u0f0a\u0f0b\u0f0c\u0f0d\u0f0e\u0f0f", "\u0f10\u0f11\u0f12\u0f13\u0f14\u0f15\u0f16\u0f17\u0f18\u0f19\u0f1a\u0f1b\u0f1c\u0f1d\u0f1e\u0f1f", @@ -534,8 +332,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_10.png", - "ascent": %ascent%, + "file": "10", "chars": [ "\u1000\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u100c\u100d\u100e\u100f", "\u1010\u1011\u1012\u1013\u1014\u1015\u1016\u1017\u1018\u1019\u101a\u101b\u101c\u101d\u101e\u101f", @@ -557,8 +354,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_11.png", - "ascent": %ascent%, + "file": "11", "chars": [ "\u1100\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110d\u110e\u110f", "\u1110\u1111\u1112\u1113\u1114\u1115\u1116\u1117\u1118\u1119\u111a\u111b\u111c\u111d\u111e\u111f", @@ -580,8 +376,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_12.png", - "ascent": %ascent%, + "file": "12", "chars": [ "\u1200\u1201\u1202\u1203\u1204\u1205\u1206\u1207\u1208\u1209\u120a\u120b\u120c\u120d\u120e\u120f", "\u1210\u1211\u1212\u1213\u1214\u1215\u1216\u1217\u1218\u1219\u121a\u121b\u121c\u121d\u121e\u121f", @@ -603,8 +398,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_13.png", - "ascent": %ascent%, + "file": "13", "chars": [ "\u1300\u1301\u1302\u1303\u1304\u1305\u1306\u1307\u1308\u1309\u130a\u130b\u130c\u130d\u130e\u130f", "\u1310\u1311\u1312\u1313\u1314\u1315\u1316\u1317\u1318\u1319\u131a\u131b\u131c\u131d\u131e\u131f", @@ -626,8 +420,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_14.png", - "ascent": %ascent%, + "file": "14", "chars": [ "\u1400\u1401\u1402\u1403\u1404\u1405\u1406\u1407\u1408\u1409\u140a\u140b\u140c\u140d\u140e\u140f", "\u1410\u1411\u1412\u1413\u1414\u1415\u1416\u1417\u1418\u1419\u141a\u141b\u141c\u141d\u141e\u141f", @@ -649,8 +442,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_15.png", - "ascent": %ascent%, + "file": "15", "chars": [ "\u1500\u1501\u1502\u1503\u1504\u1505\u1506\u1507\u1508\u1509\u150a\u150b\u150c\u150d\u150e\u150f", "\u1510\u1511\u1512\u1513\u1514\u1515\u1516\u1517\u1518\u1519\u151a\u151b\u151c\u151d\u151e\u151f", @@ -672,8 +464,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_16.png", - "ascent": %ascent%, + "file": "16", "chars": [ "\u1600\u1601\u1602\u1603\u1604\u1605\u1606\u1607\u1608\u1609\u160a\u160b\u160c\u160d\u160e\u160f", "\u1610\u1611\u1612\u1613\u1614\u1615\u1616\u1617\u1618\u1619\u161a\u161b\u161c\u161d\u161e\u161f", @@ -695,8 +486,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_17.png", - "ascent": %ascent%, + "file": "17", "chars": [ "\u1700\u1701\u1702\u1703\u1704\u1705\u1706\u1707\u1708\u1709\u170a\u170b\u170c\u170d\u170e\u170f", "\u1710\u1711\u1712\u1713\u1714\u1715\u1716\u1717\u1718\u1719\u171a\u171b\u171c\u171d\u171e\u171f", @@ -718,8 +508,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_18.png", - "ascent": %ascent%, + "file": "18", "chars": [ "\u1800\u1801\u1802\u1803\u1804\u1805\u1806\u1807\u1808\u1809\u180a\u180b\u180c\u180d\u180e\u180f", "\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u181a\u181b\u181c\u181d\u181e\u181f", @@ -741,8 +530,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_19.png", - "ascent": %ascent%, + "file": "19", "chars": [ "\u1900\u1901\u1902\u1903\u1904\u1905\u1906\u1907\u1908\u1909\u190a\u190b\u190c\u190d\u190e\u190f", "\u1910\u1911\u1912\u1913\u1914\u1915\u1916\u1917\u1918\u1919\u191a\u191b\u191c\u191d\u191e\u191f", @@ -764,8 +552,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_1a.png", - "ascent": %ascent%, + "file": "1a", "chars": [ "\u1a00\u1a01\u1a02\u1a03\u1a04\u1a05\u1a06\u1a07\u1a08\u1a09\u1a0a\u1a0b\u1a0c\u1a0d\u1a0e\u1a0f", "\u1a10\u1a11\u1a12\u1a13\u1a14\u1a15\u1a16\u1a17\u1a18\u1a19\u1a1a\u1a1b\u1a1c\u1a1d\u1a1e\u1a1f", @@ -787,8 +574,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_1b.png", - "ascent": %ascent%, + "file": "1b", "chars": [ "\u1b00\u1b01\u1b02\u1b03\u1b04\u1b05\u1b06\u1b07\u1b08\u1b09\u1b0a\u1b0b\u1b0c\u1b0d\u1b0e\u1b0f", "\u1b10\u1b11\u1b12\u1b13\u1b14\u1b15\u1b16\u1b17\u1b18\u1b19\u1b1a\u1b1b\u1b1c\u1b1d\u1b1e\u1b1f", @@ -810,8 +596,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_1c.png", - "ascent": %ascent%, + "file": "1c", "chars": [ "\u1c00\u1c01\u1c02\u1c03\u1c04\u1c05\u1c06\u1c07\u1c08\u1c09\u1c0a\u1c0b\u1c0c\u1c0d\u1c0e\u1c0f", "\u1c10\u1c11\u1c12\u1c13\u1c14\u1c15\u1c16\u1c17\u1c18\u1c19\u1c1a\u1c1b\u1c1c\u1c1d\u1c1e\u1c1f", @@ -833,8 +618,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_1d.png", - "ascent": %ascent%, + "file": "1d", "chars": [ "\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u1d06\u1d07\u1d08\u1d09\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u1d0f", "\u1d10\u1d11\u1d12\u1d13\u1d14\u1d15\u1d16\u1d17\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f", @@ -856,8 +640,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_1e.png", - "ascent": %ascent%, + "file": "1e", "chars": [ "\u1e00\u1e01\u1e02\u1e03\u1e04\u1e05\u1e06\u1e07\u1e08\u1e09\u1e0a\u1e0b\u1e0c\u1e0d\u1e0e\u1e0f", "\u1e10\u1e11\u1e12\u1e13\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1a\u1e1b\u1e1c\u1e1d\u1e1e\u1e1f", @@ -879,8 +662,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_1f.png", - "ascent": %ascent%, + "file": "1f", "chars": [ "\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f", "\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f16\u1f17\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f1e\u1f1f", @@ -902,8 +684,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_20.png", - "ascent": %ascent%, + "file": "20", "chars": [ "\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u200e\u200f", "\u2010\u2011\u2012\u2013\u2014\u2015\u2016\u2017\u2018\u2019\u201a\u201b\u201c\u201d\u201e\u201f", @@ -925,8 +706,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_21.png", - "ascent": %ascent%, + "file": "21", "chars": [ "\u2100\u2101\u2102\u2103\u2104\u2105\u2106\u2107\u2108\u2109\u210a\u210b\u210c\u210d\u210e\u210f", "\u2110\u2111\u2112\u2113\u2114\u2115\u2116\u2117\u2118\u2119\u211a\u211b\u211c\u211d\u211e\u211f", @@ -948,8 +728,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_22.png", - "ascent": %ascent%, + "file": "22", "chars": [ "\u2200\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220a\u220b\u220c\u220d\u220e\u220f", "\u2210\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221a\u221b\u221c\u221d\u221e\u221f", @@ -971,8 +750,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_23.png", - "ascent": %ascent%, + "file": "23", "chars": [ "\u2300\u2301\u2302\u2303\u2304\u2305\u2306\u2307\u2308\u2309\u230a\u230b\u230c\u230d\u230e\u230f", "\u2310\u2311\u2312\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231a\u231b\u231c\u231d\u231e\u231f", @@ -994,8 +772,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_24.png", - "ascent": %ascent%, + "file": "24", "chars": [ "\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240a\u240b\u240c\u240d\u240e\u240f", "\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241a\u241b\u241c\u241d\u241e\u241f", @@ -1017,8 +794,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_25.png", - "ascent": %ascent%, + "file": "25", "chars": [ "\u2500\u2501\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250a\u250b\u250c\u250d\u250e\u250f", "\u2510\u2511\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251a\u251b\u251c\u251d\u251e\u251f", @@ -1040,8 +816,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_26.png", - "ascent": %ascent%, + "file": "26", "chars": [ "\u2600\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609\u260a\u260b\u260c\u260d\u260e\u260f", "\u2610\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619\u261a\u261b\u261c\u261d\u261e\u261f", @@ -1063,8 +838,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_27.png", - "ascent": %ascent%, + "file": "27", "chars": [ "\u2700\u2701\u2702\u2703\u2704\u2705\u2706\u2707\u2708\u2709\u270a\u270b\u270c\u270d\u270e\u270f", "\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719\u271a\u271b\u271c\u271d\u271e\u271f", @@ -1086,8 +860,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_28.png", - "ascent": %ascent%, + "file": "28", "chars": [ "\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280a\u280b\u280c\u280d\u280e\u280f", "\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281a\u281b\u281c\u281d\u281e\u281f", @@ -1109,8 +882,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_29.png", - "ascent": %ascent%, + "file": "29", "chars": [ "\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f", "\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f", @@ -1132,8 +904,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_2a.png", - "ascent": %ascent%, + "file": "2a", "chars": [ "\u2a00\u2a01\u2a02\u2a03\u2a04\u2a05\u2a06\u2a07\u2a08\u2a09\u2a0a\u2a0b\u2a0c\u2a0d\u2a0e\u2a0f", "\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c\u2a1d\u2a1e\u2a1f", @@ -1155,8 +926,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_2b.png", - "ascent": %ascent%, + "file": "2b", "chars": [ "\u2b00\u2b01\u2b02\u2b03\u2b04\u2b05\u2b06\u2b07\u2b08\u2b09\u2b0a\u2b0b\u2b0c\u2b0d\u2b0e\u2b0f", "\u2b10\u2b11\u2b12\u2b13\u2b14\u2b15\u2b16\u2b17\u2b18\u2b19\u2b1a\u2b1b\u2b1c\u2b1d\u2b1e\u2b1f", @@ -1178,8 +948,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_2c.png", - "ascent": %ascent%, + "file": "2c", "chars": [ "\u2c00\u2c01\u2c02\u2c03\u2c04\u2c05\u2c06\u2c07\u2c08\u2c09\u2c0a\u2c0b\u2c0c\u2c0d\u2c0e\u2c0f", "\u2c10\u2c11\u2c12\u2c13\u2c14\u2c15\u2c16\u2c17\u2c18\u2c19\u2c1a\u2c1b\u2c1c\u2c1d\u2c1e\u2c1f", @@ -1201,8 +970,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_2d.png", - "ascent": %ascent%, + "file": "2d", "chars": [ "\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d07\u2d08\u2d09\u2d0a\u2d0b\u2d0c\u2d0d\u2d0e\u2d0f", "\u2d10\u2d11\u2d12\u2d13\u2d14\u2d15\u2d16\u2d17\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d1f", @@ -1224,8 +992,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_2e.png", - "ascent": %ascent%, + "file": "2e", "chars": [ "\u2e00\u2e01\u2e02\u2e03\u2e04\u2e05\u2e06\u2e07\u2e08\u2e09\u2e0a\u2e0b\u2e0c\u2e0d\u2e0e\u2e0f", "\u2e10\u2e11\u2e12\u2e13\u2e14\u2e15\u2e16\u2e17\u2e18\u2e19\u2e1a\u2e1b\u2e1c\u2e1d\u2e1e\u2e1f", @@ -1247,8 +1014,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_2f.png", - "ascent": %ascent%, + "file": "2f", "chars": [ "\u2f00\u2f01\u2f02\u2f03\u2f04\u2f05\u2f06\u2f07\u2f08\u2f09\u2f0a\u2f0b\u2f0c\u2f0d\u2f0e\u2f0f", "\u2f10\u2f11\u2f12\u2f13\u2f14\u2f15\u2f16\u2f17\u2f18\u2f19\u2f1a\u2f1b\u2f1c\u2f1d\u2f1e\u2f1f", @@ -1270,8 +1036,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_30.png", - "ascent": %ascent%, + "file": "30", "chars": [ "\u3000\u3001\u3002\u3003\u3004\u3005\u3006\u3007\u3008\u3009\u300a\u300b\u300c\u300d\u300e\u300f", "\u3010\u3011\u3012\u3013\u3014\u3015\u3016\u3017\u3018\u3019\u301a\u301b\u301c\u301d\u301e\u301f", @@ -1293,8 +1058,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_31.png", - "ascent": %ascent%, + "file": "31", "chars": [ "\u3100\u3101\u3102\u3103\u3104\u3105\u3106\u3107\u3108\u3109\u310a\u310b\u310c\u310d\u310e\u310f", "\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119\u311a\u311b\u311c\u311d\u311e\u311f", @@ -1316,8 +1080,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_32.png", - "ascent": %ascent%, + "file": "32", "chars": [ "\u3200\u3201\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209\u320a\u320b\u320c\u320d\u320e\u320f", "\u3210\u3211\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219\u321a\u321b\u321c\u321d\u321e\u321f", @@ -1339,8 +1102,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_33.png", - "ascent": %ascent%, + "file": "33", "chars": [ "\u3300\u3301\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309\u330a\u330b\u330c\u330d\u330e\u330f", "\u3310\u3311\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319\u331a\u331b\u331c\u331d\u331e\u331f", @@ -1362,8 +1124,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_34.png", - "ascent": %ascent%, + "file": "34", "chars": [ "\u3400\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409\u340a\u340b\u340c\u340d\u340e\u340f", "\u3410\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419\u341a\u341b\u341c\u341d\u341e\u341f", @@ -1385,8 +1146,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_35.png", - "ascent": %ascent%, + "file": "35", "chars": [ "\u3500\u3501\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350a\u350b\u350c\u350d\u350e\u350f", "\u3510\u3511\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351a\u351b\u351c\u351d\u351e\u351f", @@ -1408,8 +1168,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_36.png", - "ascent": %ascent%, + "file": "36", "chars": [ "\u3600\u3601\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609\u360a\u360b\u360c\u360d\u360e\u360f", "\u3610\u3611\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619\u361a\u361b\u361c\u361d\u361e\u361f", @@ -1431,8 +1190,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_37.png", - "ascent": %ascent%, + "file": "37", "chars": [ "\u3700\u3701\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709\u370a\u370b\u370c\u370d\u370e\u370f", "\u3710\u3711\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719\u371a\u371b\u371c\u371d\u371e\u371f", @@ -1454,8 +1212,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_38.png", - "ascent": %ascent%, + "file": "38", "chars": [ "\u3800\u3801\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809\u380a\u380b\u380c\u380d\u380e\u380f", "\u3810\u3811\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819\u381a\u381b\u381c\u381d\u381e\u381f", @@ -1477,8 +1234,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_39.png", - "ascent": %ascent%, + "file": "39", "chars": [ "\u3900\u3901\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909\u390a\u390b\u390c\u390d\u390e\u390f", "\u3910\u3911\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919\u391a\u391b\u391c\u391d\u391e\u391f", @@ -1500,8 +1256,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_3a.png", - "ascent": %ascent%, + "file": "3a", "chars": [ "\u3a00\u3a01\u3a02\u3a03\u3a04\u3a05\u3a06\u3a07\u3a08\u3a09\u3a0a\u3a0b\u3a0c\u3a0d\u3a0e\u3a0f", "\u3a10\u3a11\u3a12\u3a13\u3a14\u3a15\u3a16\u3a17\u3a18\u3a19\u3a1a\u3a1b\u3a1c\u3a1d\u3a1e\u3a1f", @@ -1523,8 +1278,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_3b.png", - "ascent": %ascent%, + "file": "3b", "chars": [ "\u3b00\u3b01\u3b02\u3b03\u3b04\u3b05\u3b06\u3b07\u3b08\u3b09\u3b0a\u3b0b\u3b0c\u3b0d\u3b0e\u3b0f", "\u3b10\u3b11\u3b12\u3b13\u3b14\u3b15\u3b16\u3b17\u3b18\u3b19\u3b1a\u3b1b\u3b1c\u3b1d\u3b1e\u3b1f", @@ -1546,8 +1300,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_3c.png", - "ascent": %ascent%, + "file": "3c", "chars": [ "\u3c00\u3c01\u3c02\u3c03\u3c04\u3c05\u3c06\u3c07\u3c08\u3c09\u3c0a\u3c0b\u3c0c\u3c0d\u3c0e\u3c0f", "\u3c10\u3c11\u3c12\u3c13\u3c14\u3c15\u3c16\u3c17\u3c18\u3c19\u3c1a\u3c1b\u3c1c\u3c1d\u3c1e\u3c1f", @@ -1569,8 +1322,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_3d.png", - "ascent": %ascent%, + "file": "3d", "chars": [ "\u3d00\u3d01\u3d02\u3d03\u3d04\u3d05\u3d06\u3d07\u3d08\u3d09\u3d0a\u3d0b\u3d0c\u3d0d\u3d0e\u3d0f", "\u3d10\u3d11\u3d12\u3d13\u3d14\u3d15\u3d16\u3d17\u3d18\u3d19\u3d1a\u3d1b\u3d1c\u3d1d\u3d1e\u3d1f", @@ -1592,8 +1344,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_3e.png", - "ascent": %ascent%, + "file": "3e", "chars": [ "\u3e00\u3e01\u3e02\u3e03\u3e04\u3e05\u3e06\u3e07\u3e08\u3e09\u3e0a\u3e0b\u3e0c\u3e0d\u3e0e\u3e0f", "\u3e10\u3e11\u3e12\u3e13\u3e14\u3e15\u3e16\u3e17\u3e18\u3e19\u3e1a\u3e1b\u3e1c\u3e1d\u3e1e\u3e1f", @@ -1615,8 +1366,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_3f.png", - "ascent": %ascent%, + "file": "3f", "chars": [ "\u3f00\u3f01\u3f02\u3f03\u3f04\u3f05\u3f06\u3f07\u3f08\u3f09\u3f0a\u3f0b\u3f0c\u3f0d\u3f0e\u3f0f", "\u3f10\u3f11\u3f12\u3f13\u3f14\u3f15\u3f16\u3f17\u3f18\u3f19\u3f1a\u3f1b\u3f1c\u3f1d\u3f1e\u3f1f", @@ -1638,8 +1388,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_40.png", - "ascent": %ascent%, + "file": "40", "chars": [ "\u4000\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u400a\u400b\u400c\u400d\u400e\u400f", "\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u4018\u4019\u401a\u401b\u401c\u401d\u401e\u401f", @@ -1661,8 +1410,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_41.png", - "ascent": %ascent%, + "file": "41", "chars": [ "\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107\u4108\u4109\u410a\u410b\u410c\u410d\u410e\u410f", "\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117\u4118\u4119\u411a\u411b\u411c\u411d\u411e\u411f", @@ -1684,8 +1432,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_42.png", - "ascent": %ascent%, + "file": "42", "chars": [ "\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208\u4209\u420a\u420b\u420c\u420d\u420e\u420f", "\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218\u4219\u421a\u421b\u421c\u421d\u421e\u421f", @@ -1707,8 +1454,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_43.png", - "ascent": %ascent%, + "file": "43", "chars": [ "\u4300\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308\u4309\u430a\u430b\u430c\u430d\u430e\u430f", "\u4310\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318\u4319\u431a\u431b\u431c\u431d\u431e\u431f", @@ -1730,8 +1476,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_44.png", - "ascent": %ascent%, + "file": "44", "chars": [ "\u4400\u4401\u4402\u4403\u4404\u4405\u4406\u4407\u4408\u4409\u440a\u440b\u440c\u440d\u440e\u440f", "\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417\u4418\u4419\u441a\u441b\u441c\u441d\u441e\u441f", @@ -1753,8 +1498,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_45.png", - "ascent": %ascent%, + "file": "45", "chars": [ "\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507\u4508\u4509\u450a\u450b\u450c\u450d\u450e\u450f", "\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517\u4518\u4519\u451a\u451b\u451c\u451d\u451e\u451f", @@ -1776,8 +1520,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_46.png", - "ascent": %ascent%, + "file": "46", "chars": [ "\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607\u4608\u4609\u460a\u460b\u460c\u460d\u460e\u460f", "\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617\u4618\u4619\u461a\u461b\u461c\u461d\u461e\u461f", @@ -1799,8 +1542,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_47.png", - "ascent": %ascent%, + "file": "47", "chars": [ "\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707\u4708\u4709\u470a\u470b\u470c\u470d\u470e\u470f", "\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717\u4718\u4719\u471a\u471b\u471c\u471d\u471e\u471f", @@ -1822,8 +1564,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_48.png", - "ascent": %ascent%, + "file": "48", "chars": [ "\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807\u4808\u4809\u480a\u480b\u480c\u480d\u480e\u480f", "\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817\u4818\u4819\u481a\u481b\u481c\u481d\u481e\u481f", @@ -1845,8 +1586,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_49.png", - "ascent": %ascent%, + "file": "49", "chars": [ "\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907\u4908\u4909\u490a\u490b\u490c\u490d\u490e\u490f", "\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917\u4918\u4919\u491a\u491b\u491c\u491d\u491e\u491f", @@ -1868,8 +1608,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_4a.png", - "ascent": %ascent%, + "file": "4a", "chars": [ "\u4a00\u4a01\u4a02\u4a03\u4a04\u4a05\u4a06\u4a07\u4a08\u4a09\u4a0a\u4a0b\u4a0c\u4a0d\u4a0e\u4a0f", "\u4a10\u4a11\u4a12\u4a13\u4a14\u4a15\u4a16\u4a17\u4a18\u4a19\u4a1a\u4a1b\u4a1c\u4a1d\u4a1e\u4a1f", @@ -1891,8 +1630,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_4b.png", - "ascent": %ascent%, + "file": "4b", "chars": [ "\u4b00\u4b01\u4b02\u4b03\u4b04\u4b05\u4b06\u4b07\u4b08\u4b09\u4b0a\u4b0b\u4b0c\u4b0d\u4b0e\u4b0f", "\u4b10\u4b11\u4b12\u4b13\u4b14\u4b15\u4b16\u4b17\u4b18\u4b19\u4b1a\u4b1b\u4b1c\u4b1d\u4b1e\u4b1f", @@ -1914,8 +1652,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_4c.png", - "ascent": %ascent%, + "file": "4c", "chars": [ "\u4c00\u4c01\u4c02\u4c03\u4c04\u4c05\u4c06\u4c07\u4c08\u4c09\u4c0a\u4c0b\u4c0c\u4c0d\u4c0e\u4c0f", "\u4c10\u4c11\u4c12\u4c13\u4c14\u4c15\u4c16\u4c17\u4c18\u4c19\u4c1a\u4c1b\u4c1c\u4c1d\u4c1e\u4c1f", @@ -1937,8 +1674,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_4d.png", - "ascent": %ascent%, + "file": "4d", "chars": [ "\u4d00\u4d01\u4d02\u4d03\u4d04\u4d05\u4d06\u4d07\u4d08\u4d09\u4d0a\u4d0b\u4d0c\u4d0d\u4d0e\u4d0f", "\u4d10\u4d11\u4d12\u4d13\u4d14\u4d15\u4d16\u4d17\u4d18\u4d19\u4d1a\u4d1b\u4d1c\u4d1d\u4d1e\u4d1f", @@ -1960,8 +1696,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_4e.png", - "ascent": %ascent%, + "file": "4e", "chars": [ "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d\u4e0e\u4e0f", "\u4e10\u4e11\u4e12\u4e13\u4e14\u4e15\u4e16\u4e17\u4e18\u4e19\u4e1a\u4e1b\u4e1c\u4e1d\u4e1e\u4e1f", @@ -1983,8 +1718,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_4f.png", - "ascent": %ascent%, + "file": "4f", "chars": [ "\u4f00\u4f01\u4f02\u4f03\u4f04\u4f05\u4f06\u4f07\u4f08\u4f09\u4f0a\u4f0b\u4f0c\u4f0d\u4f0e\u4f0f", "\u4f10\u4f11\u4f12\u4f13\u4f14\u4f15\u4f16\u4f17\u4f18\u4f19\u4f1a\u4f1b\u4f1c\u4f1d\u4f1e\u4f1f", @@ -2006,8 +1740,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_50.png", - "ascent": %ascent%, + "file": "50", "chars": [ "\u5000\u5001\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009\u500a\u500b\u500c\u500d\u500e\u500f", "\u5010\u5011\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019\u501a\u501b\u501c\u501d\u501e\u501f", @@ -2029,8 +1762,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_51.png", - "ascent": %ascent%, + "file": "51", "chars": [ "\u5100\u5101\u5102\u5103\u5104\u5105\u5106\u5107\u5108\u5109\u510a\u510b\u510c\u510d\u510e\u510f", "\u5110\u5111\u5112\u5113\u5114\u5115\u5116\u5117\u5118\u5119\u511a\u511b\u511c\u511d\u511e\u511f", @@ -2052,8 +1784,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_52.png", - "ascent": %ascent%, + "file": "52", "chars": [ "\u5200\u5201\u5202\u5203\u5204\u5205\u5206\u5207\u5208\u5209\u520a\u520b\u520c\u520d\u520e\u520f", "\u5210\u5211\u5212\u5213\u5214\u5215\u5216\u5217\u5218\u5219\u521a\u521b\u521c\u521d\u521e\u521f", @@ -2075,8 +1806,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_53.png", - "ascent": %ascent%, + "file": "53", "chars": [ "\u5300\u5301\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309\u530a\u530b\u530c\u530d\u530e\u530f", "\u5310\u5311\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319\u531a\u531b\u531c\u531d\u531e\u531f", @@ -2098,8 +1828,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_54.png", - "ascent": %ascent%, + "file": "54", "chars": [ "\u5400\u5401\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409\u540a\u540b\u540c\u540d\u540e\u540f", "\u5410\u5411\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419\u541a\u541b\u541c\u541d\u541e\u541f", @@ -2121,8 +1850,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_55.png", - "ascent": %ascent%, + "file": "55", "chars": [ "\u5500\u5501\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509\u550a\u550b\u550c\u550d\u550e\u550f", "\u5510\u5511\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519\u551a\u551b\u551c\u551d\u551e\u551f", @@ -2144,8 +1872,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_56.png", - "ascent": %ascent%, + "file": "56", "chars": [ "\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607\u5608\u5609\u560a\u560b\u560c\u560d\u560e\u560f", "\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617\u5618\u5619\u561a\u561b\u561c\u561d\u561e\u561f", @@ -2167,8 +1894,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_57.png", - "ascent": %ascent%, + "file": "57", "chars": [ "\u5700\u5701\u5702\u5703\u5704\u5705\u5706\u5707\u5708\u5709\u570a\u570b\u570c\u570d\u570e\u570f", "\u5710\u5711\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719\u571a\u571b\u571c\u571d\u571e\u571f", @@ -2190,8 +1916,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_58.png", - "ascent": %ascent%, + "file": "58", "chars": [ "\u5800\u5801\u5802\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580a\u580b\u580c\u580d\u580e\u580f", "\u5810\u5811\u5812\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581a\u581b\u581c\u581d\u581e\u581f", @@ -2213,8 +1938,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_59.png", - "ascent": %ascent%, + "file": "59", "chars": [ "\u5900\u5901\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909\u590a\u590b\u590c\u590d\u590e\u590f", "\u5910\u5911\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919\u591a\u591b\u591c\u591d\u591e\u591f", @@ -2236,8 +1960,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_5a.png", - "ascent": %ascent%, + "file": "5a", "chars": [ "\u5a00\u5a01\u5a02\u5a03\u5a04\u5a05\u5a06\u5a07\u5a08\u5a09\u5a0a\u5a0b\u5a0c\u5a0d\u5a0e\u5a0f", "\u5a10\u5a11\u5a12\u5a13\u5a14\u5a15\u5a16\u5a17\u5a18\u5a19\u5a1a\u5a1b\u5a1c\u5a1d\u5a1e\u5a1f", @@ -2259,8 +1982,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_5b.png", - "ascent": %ascent%, + "file": "5b", "chars": [ "\u5b00\u5b01\u5b02\u5b03\u5b04\u5b05\u5b06\u5b07\u5b08\u5b09\u5b0a\u5b0b\u5b0c\u5b0d\u5b0e\u5b0f", "\u5b10\u5b11\u5b12\u5b13\u5b14\u5b15\u5b16\u5b17\u5b18\u5b19\u5b1a\u5b1b\u5b1c\u5b1d\u5b1e\u5b1f", @@ -2282,8 +2004,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_5c.png", - "ascent": %ascent%, + "file": "5c", "chars": [ "\u5c00\u5c01\u5c02\u5c03\u5c04\u5c05\u5c06\u5c07\u5c08\u5c09\u5c0a\u5c0b\u5c0c\u5c0d\u5c0e\u5c0f", "\u5c10\u5c11\u5c12\u5c13\u5c14\u5c15\u5c16\u5c17\u5c18\u5c19\u5c1a\u5c1b\u5c1c\u5c1d\u5c1e\u5c1f", @@ -2305,8 +2026,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_5d.png", - "ascent": %ascent%, + "file": "5d", "chars": [ "\u5d00\u5d01\u5d02\u5d03\u5d04\u5d05\u5d06\u5d07\u5d08\u5d09\u5d0a\u5d0b\u5d0c\u5d0d\u5d0e\u5d0f", "\u5d10\u5d11\u5d12\u5d13\u5d14\u5d15\u5d16\u5d17\u5d18\u5d19\u5d1a\u5d1b\u5d1c\u5d1d\u5d1e\u5d1f", @@ -2328,8 +2048,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_5e.png", - "ascent": %ascent%, + "file": "5e", "chars": [ "\u5e00\u5e01\u5e02\u5e03\u5e04\u5e05\u5e06\u5e07\u5e08\u5e09\u5e0a\u5e0b\u5e0c\u5e0d\u5e0e\u5e0f", "\u5e10\u5e11\u5e12\u5e13\u5e14\u5e15\u5e16\u5e17\u5e18\u5e19\u5e1a\u5e1b\u5e1c\u5e1d\u5e1e\u5e1f", @@ -2351,8 +2070,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_5f.png", - "ascent": %ascent%, + "file": "5f", "chars": [ "\u5f00\u5f01\u5f02\u5f03\u5f04\u5f05\u5f06\u5f07\u5f08\u5f09\u5f0a\u5f0b\u5f0c\u5f0d\u5f0e\u5f0f", "\u5f10\u5f11\u5f12\u5f13\u5f14\u5f15\u5f16\u5f17\u5f18\u5f19\u5f1a\u5f1b\u5f1c\u5f1d\u5f1e\u5f1f", @@ -2374,8 +2092,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_60.png", - "ascent": %ascent%, + "file": "60", "chars": [ "\u6000\u6001\u6002\u6003\u6004\u6005\u6006\u6007\u6008\u6009\u600a\u600b\u600c\u600d\u600e\u600f", "\u6010\u6011\u6012\u6013\u6014\u6015\u6016\u6017\u6018\u6019\u601a\u601b\u601c\u601d\u601e\u601f", @@ -2397,8 +2114,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_61.png", - "ascent": %ascent%, + "file": "61", "chars": [ "\u6100\u6101\u6102\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610a\u610b\u610c\u610d\u610e\u610f", "\u6110\u6111\u6112\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611a\u611b\u611c\u611d\u611e\u611f", @@ -2420,8 +2136,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_62.png", - "ascent": %ascent%, + "file": "62", "chars": [ "\u6200\u6201\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209\u620a\u620b\u620c\u620d\u620e\u620f", "\u6210\u6211\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219\u621a\u621b\u621c\u621d\u621e\u621f", @@ -2443,8 +2158,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_63.png", - "ascent": %ascent%, + "file": "63", "chars": [ "\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307\u6308\u6309\u630a\u630b\u630c\u630d\u630e\u630f", "\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317\u6318\u6319\u631a\u631b\u631c\u631d\u631e\u631f", @@ -2466,8 +2180,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_64.png", - "ascent": %ascent%, + "file": "64", "chars": [ "\u6400\u6401\u6402\u6403\u6404\u6405\u6406\u6407\u6408\u6409\u640a\u640b\u640c\u640d\u640e\u640f", "\u6410\u6411\u6412\u6413\u6414\u6415\u6416\u6417\u6418\u6419\u641a\u641b\u641c\u641d\u641e\u641f", @@ -2489,8 +2202,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_65.png", - "ascent": %ascent%, + "file": "65", "chars": [ "\u6500\u6501\u6502\u6503\u6504\u6505\u6506\u6507\u6508\u6509\u650a\u650b\u650c\u650d\u650e\u650f", "\u6510\u6511\u6512\u6513\u6514\u6515\u6516\u6517\u6518\u6519\u651a\u651b\u651c\u651d\u651e\u651f", @@ -2512,8 +2224,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_66.png", - "ascent": %ascent%, + "file": "66", "chars": [ "\u6600\u6601\u6602\u6603\u6604\u6605\u6606\u6607\u6608\u6609\u660a\u660b\u660c\u660d\u660e\u660f", "\u6610\u6611\u6612\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661a\u661b\u661c\u661d\u661e\u661f", @@ -2535,8 +2246,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_67.png", - "ascent": %ascent%, + "file": "67", "chars": [ "\u6700\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708\u6709\u670a\u670b\u670c\u670d\u670e\u670f", "\u6710\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718\u6719\u671a\u671b\u671c\u671d\u671e\u671f", @@ -2558,8 +2268,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_68.png", - "ascent": %ascent%, + "file": "68", "chars": [ "\u6800\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808\u6809\u680a\u680b\u680c\u680d\u680e\u680f", "\u6810\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818\u6819\u681a\u681b\u681c\u681d\u681e\u681f", @@ -2581,8 +2290,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_69.png", - "ascent": %ascent%, + "file": "69", "chars": [ "\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907\u6908\u6909\u690a\u690b\u690c\u690d\u690e\u690f", "\u6910\u6911\u6912\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691a\u691b\u691c\u691d\u691e\u691f", @@ -2604,8 +2312,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_6a.png", - "ascent": %ascent%, + "file": "6a", "chars": [ "\u6a00\u6a01\u6a02\u6a03\u6a04\u6a05\u6a06\u6a07\u6a08\u6a09\u6a0a\u6a0b\u6a0c\u6a0d\u6a0e\u6a0f", "\u6a10\u6a11\u6a12\u6a13\u6a14\u6a15\u6a16\u6a17\u6a18\u6a19\u6a1a\u6a1b\u6a1c\u6a1d\u6a1e\u6a1f", @@ -2627,8 +2334,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_6b.png", - "ascent": %ascent%, + "file": "6b", "chars": [ "\u6b00\u6b01\u6b02\u6b03\u6b04\u6b05\u6b06\u6b07\u6b08\u6b09\u6b0a\u6b0b\u6b0c\u6b0d\u6b0e\u6b0f", "\u6b10\u6b11\u6b12\u6b13\u6b14\u6b15\u6b16\u6b17\u6b18\u6b19\u6b1a\u6b1b\u6b1c\u6b1d\u6b1e\u6b1f", @@ -2650,8 +2356,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_6c.png", - "ascent": %ascent%, + "file": "6c", "chars": [ "\u6c00\u6c01\u6c02\u6c03\u6c04\u6c05\u6c06\u6c07\u6c08\u6c09\u6c0a\u6c0b\u6c0c\u6c0d\u6c0e\u6c0f", "\u6c10\u6c11\u6c12\u6c13\u6c14\u6c15\u6c16\u6c17\u6c18\u6c19\u6c1a\u6c1b\u6c1c\u6c1d\u6c1e\u6c1f", @@ -2673,8 +2378,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_6d.png", - "ascent": %ascent%, + "file": "6d", "chars": [ "\u6d00\u6d01\u6d02\u6d03\u6d04\u6d05\u6d06\u6d07\u6d08\u6d09\u6d0a\u6d0b\u6d0c\u6d0d\u6d0e\u6d0f", "\u6d10\u6d11\u6d12\u6d13\u6d14\u6d15\u6d16\u6d17\u6d18\u6d19\u6d1a\u6d1b\u6d1c\u6d1d\u6d1e\u6d1f", @@ -2696,8 +2400,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_6e.png", - "ascent": %ascent%, + "file": "6e", "chars": [ "\u6e00\u6e01\u6e02\u6e03\u6e04\u6e05\u6e06\u6e07\u6e08\u6e09\u6e0a\u6e0b\u6e0c\u6e0d\u6e0e\u6e0f", "\u6e10\u6e11\u6e12\u6e13\u6e14\u6e15\u6e16\u6e17\u6e18\u6e19\u6e1a\u6e1b\u6e1c\u6e1d\u6e1e\u6e1f", @@ -2719,8 +2422,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_6f.png", - "ascent": %ascent%, + "file": "6f", "chars": [ "\u6f00\u6f01\u6f02\u6f03\u6f04\u6f05\u6f06\u6f07\u6f08\u6f09\u6f0a\u6f0b\u6f0c\u6f0d\u6f0e\u6f0f", "\u6f10\u6f11\u6f12\u6f13\u6f14\u6f15\u6f16\u6f17\u6f18\u6f19\u6f1a\u6f1b\u6f1c\u6f1d\u6f1e\u6f1f", @@ -2742,8 +2444,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_70.png", - "ascent": %ascent%, + "file": "70", "chars": [ "\u7000\u7001\u7002\u7003\u7004\u7005\u7006\u7007\u7008\u7009\u700a\u700b\u700c\u700d\u700e\u700f", "\u7010\u7011\u7012\u7013\u7014\u7015\u7016\u7017\u7018\u7019\u701a\u701b\u701c\u701d\u701e\u701f", @@ -2765,8 +2466,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_71.png", - "ascent": %ascent%, + "file": "71", "chars": [ "\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107\u7108\u7109\u710a\u710b\u710c\u710d\u710e\u710f", "\u7110\u7111\u7112\u7113\u7114\u7115\u7116\u7117\u7118\u7119\u711a\u711b\u711c\u711d\u711e\u711f", @@ -2788,8 +2488,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_72.png", - "ascent": %ascent%, + "file": "72", "chars": [ "\u7200\u7201\u7202\u7203\u7204\u7205\u7206\u7207\u7208\u7209\u720a\u720b\u720c\u720d\u720e\u720f", "\u7210\u7211\u7212\u7213\u7214\u7215\u7216\u7217\u7218\u7219\u721a\u721b\u721c\u721d\u721e\u721f", @@ -2811,8 +2510,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_73.png", - "ascent": %ascent%, + "file": "73", "chars": [ "\u7300\u7301\u7302\u7303\u7304\u7305\u7306\u7307\u7308\u7309\u730a\u730b\u730c\u730d\u730e\u730f", "\u7310\u7311\u7312\u7313\u7314\u7315\u7316\u7317\u7318\u7319\u731a\u731b\u731c\u731d\u731e\u731f", @@ -2834,8 +2532,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_74.png", - "ascent": %ascent%, + "file": "74", "chars": [ "\u7400\u7401\u7402\u7403\u7404\u7405\u7406\u7407\u7408\u7409\u740a\u740b\u740c\u740d\u740e\u740f", "\u7410\u7411\u7412\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741a\u741b\u741c\u741d\u741e\u741f", @@ -2857,8 +2554,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_75.png", - "ascent": %ascent%, + "file": "75", "chars": [ "\u7500\u7501\u7502\u7503\u7504\u7505\u7506\u7507\u7508\u7509\u750a\u750b\u750c\u750d\u750e\u750f", "\u7510\u7511\u7512\u7513\u7514\u7515\u7516\u7517\u7518\u7519\u751a\u751b\u751c\u751d\u751e\u751f", @@ -2880,8 +2576,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_76.png", - "ascent": %ascent%, + "file": "76", "chars": [ "\u7600\u7601\u7602\u7603\u7604\u7605\u7606\u7607\u7608\u7609\u760a\u760b\u760c\u760d\u760e\u760f", "\u7610\u7611\u7612\u7613\u7614\u7615\u7616\u7617\u7618\u7619\u761a\u761b\u761c\u761d\u761e\u761f", @@ -2903,8 +2598,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_77.png", - "ascent": %ascent%, + "file": "77", "chars": [ "\u7700\u7701\u7702\u7703\u7704\u7705\u7706\u7707\u7708\u7709\u770a\u770b\u770c\u770d\u770e\u770f", "\u7710\u7711\u7712\u7713\u7714\u7715\u7716\u7717\u7718\u7719\u771a\u771b\u771c\u771d\u771e\u771f", @@ -2926,8 +2620,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_78.png", - "ascent": %ascent%, + "file": "78", "chars": [ "\u7800\u7801\u7802\u7803\u7804\u7805\u7806\u7807\u7808\u7809\u780a\u780b\u780c\u780d\u780e\u780f", "\u7810\u7811\u7812\u7813\u7814\u7815\u7816\u7817\u7818\u7819\u781a\u781b\u781c\u781d\u781e\u781f", @@ -2949,8 +2642,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_79.png", - "ascent": %ascent%, + "file": "79", "chars": [ "\u7900\u7901\u7902\u7903\u7904\u7905\u7906\u7907\u7908\u7909\u790a\u790b\u790c\u790d\u790e\u790f", "\u7910\u7911\u7912\u7913\u7914\u7915\u7916\u7917\u7918\u7919\u791a\u791b\u791c\u791d\u791e\u791f", @@ -2972,8 +2664,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_7a.png", - "ascent": %ascent%, + "file": "7a", "chars": [ "\u7a00\u7a01\u7a02\u7a03\u7a04\u7a05\u7a06\u7a07\u7a08\u7a09\u7a0a\u7a0b\u7a0c\u7a0d\u7a0e\u7a0f", "\u7a10\u7a11\u7a12\u7a13\u7a14\u7a15\u7a16\u7a17\u7a18\u7a19\u7a1a\u7a1b\u7a1c\u7a1d\u7a1e\u7a1f", @@ -2995,8 +2686,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_7b.png", - "ascent": %ascent%, + "file": "7b", "chars": [ "\u7b00\u7b01\u7b02\u7b03\u7b04\u7b05\u7b06\u7b07\u7b08\u7b09\u7b0a\u7b0b\u7b0c\u7b0d\u7b0e\u7b0f", "\u7b10\u7b11\u7b12\u7b13\u7b14\u7b15\u7b16\u7b17\u7b18\u7b19\u7b1a\u7b1b\u7b1c\u7b1d\u7b1e\u7b1f", @@ -3018,8 +2708,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_7c.png", - "ascent": %ascent%, + "file": "7c", "chars": [ "\u7c00\u7c01\u7c02\u7c03\u7c04\u7c05\u7c06\u7c07\u7c08\u7c09\u7c0a\u7c0b\u7c0c\u7c0d\u7c0e\u7c0f", "\u7c10\u7c11\u7c12\u7c13\u7c14\u7c15\u7c16\u7c17\u7c18\u7c19\u7c1a\u7c1b\u7c1c\u7c1d\u7c1e\u7c1f", @@ -3041,8 +2730,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_7d.png", - "ascent": %ascent%, + "file": "7d", "chars": [ "\u7d00\u7d01\u7d02\u7d03\u7d04\u7d05\u7d06\u7d07\u7d08\u7d09\u7d0a\u7d0b\u7d0c\u7d0d\u7d0e\u7d0f", "\u7d10\u7d11\u7d12\u7d13\u7d14\u7d15\u7d16\u7d17\u7d18\u7d19\u7d1a\u7d1b\u7d1c\u7d1d\u7d1e\u7d1f", @@ -3064,8 +2752,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_7e.png", - "ascent": %ascent%, + "file": "7e", "chars": [ "\u7e00\u7e01\u7e02\u7e03\u7e04\u7e05\u7e06\u7e07\u7e08\u7e09\u7e0a\u7e0b\u7e0c\u7e0d\u7e0e\u7e0f", "\u7e10\u7e11\u7e12\u7e13\u7e14\u7e15\u7e16\u7e17\u7e18\u7e19\u7e1a\u7e1b\u7e1c\u7e1d\u7e1e\u7e1f", @@ -3087,8 +2774,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_7f.png", - "ascent": %ascent%, + "file": "7f", "chars": [ "\u7f00\u7f01\u7f02\u7f03\u7f04\u7f05\u7f06\u7f07\u7f08\u7f09\u7f0a\u7f0b\u7f0c\u7f0d\u7f0e\u7f0f", "\u7f10\u7f11\u7f12\u7f13\u7f14\u7f15\u7f16\u7f17\u7f18\u7f19\u7f1a\u7f1b\u7f1c\u7f1d\u7f1e\u7f1f", @@ -3110,8 +2796,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_80.png", - "ascent": %ascent%, + "file": "80", "chars": [ "\u8000\u8001\u8002\u8003\u8004\u8005\u8006\u8007\u8008\u8009\u800a\u800b\u800c\u800d\u800e\u800f", "\u8010\u8011\u8012\u8013\u8014\u8015\u8016\u8017\u8018\u8019\u801a\u801b\u801c\u801d\u801e\u801f", @@ -3133,8 +2818,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_81.png", - "ascent": %ascent%, + "file": "81", "chars": [ "\u8100\u8101\u8102\u8103\u8104\u8105\u8106\u8107\u8108\u8109\u810a\u810b\u810c\u810d\u810e\u810f", "\u8110\u8111\u8112\u8113\u8114\u8115\u8116\u8117\u8118\u8119\u811a\u811b\u811c\u811d\u811e\u811f", @@ -3156,8 +2840,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_82.png", - "ascent": %ascent%, + "file": "82", "chars": [ "\u8200\u8201\u8202\u8203\u8204\u8205\u8206\u8207\u8208\u8209\u820a\u820b\u820c\u820d\u820e\u820f", "\u8210\u8211\u8212\u8213\u8214\u8215\u8216\u8217\u8218\u8219\u821a\u821b\u821c\u821d\u821e\u821f", @@ -3179,8 +2862,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_83.png", - "ascent": %ascent%, + "file": "83", "chars": [ "\u8300\u8301\u8302\u8303\u8304\u8305\u8306\u8307\u8308\u8309\u830a\u830b\u830c\u830d\u830e\u830f", "\u8310\u8311\u8312\u8313\u8314\u8315\u8316\u8317\u8318\u8319\u831a\u831b\u831c\u831d\u831e\u831f", @@ -3202,8 +2884,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_84.png", - "ascent": %ascent%, + "file": "84", "chars": [ "\u8400\u8401\u8402\u8403\u8404\u8405\u8406\u8407\u8408\u8409\u840a\u840b\u840c\u840d\u840e\u840f", "\u8410\u8411\u8412\u8413\u8414\u8415\u8416\u8417\u8418\u8419\u841a\u841b\u841c\u841d\u841e\u841f", @@ -3225,8 +2906,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_85.png", - "ascent": %ascent%, + "file": "85", "chars": [ "\u8500\u8501\u8502\u8503\u8504\u8505\u8506\u8507\u8508\u8509\u850a\u850b\u850c\u850d\u850e\u850f", "\u8510\u8511\u8512\u8513\u8514\u8515\u8516\u8517\u8518\u8519\u851a\u851b\u851c\u851d\u851e\u851f", @@ -3248,8 +2928,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_86.png", - "ascent": %ascent%, + "file": "86", "chars": [ "\u8600\u8601\u8602\u8603\u8604\u8605\u8606\u8607\u8608\u8609\u860a\u860b\u860c\u860d\u860e\u860f", "\u8610\u8611\u8612\u8613\u8614\u8615\u8616\u8617\u8618\u8619\u861a\u861b\u861c\u861d\u861e\u861f", @@ -3271,8 +2950,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_87.png", - "ascent": %ascent%, + "file": "87", "chars": [ "\u8700\u8701\u8702\u8703\u8704\u8705\u8706\u8707\u8708\u8709\u870a\u870b\u870c\u870d\u870e\u870f", "\u8710\u8711\u8712\u8713\u8714\u8715\u8716\u8717\u8718\u8719\u871a\u871b\u871c\u871d\u871e\u871f", @@ -3294,8 +2972,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_88.png", - "ascent": %ascent%, + "file": "88", "chars": [ "\u8800\u8801\u8802\u8803\u8804\u8805\u8806\u8807\u8808\u8809\u880a\u880b\u880c\u880d\u880e\u880f", "\u8810\u8811\u8812\u8813\u8814\u8815\u8816\u8817\u8818\u8819\u881a\u881b\u881c\u881d\u881e\u881f", @@ -3317,8 +2994,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_89.png", - "ascent": %ascent%, + "file": "89", "chars": [ "\u8900\u8901\u8902\u8903\u8904\u8905\u8906\u8907\u8908\u8909\u890a\u890b\u890c\u890d\u890e\u890f", "\u8910\u8911\u8912\u8913\u8914\u8915\u8916\u8917\u8918\u8919\u891a\u891b\u891c\u891d\u891e\u891f", @@ -3340,8 +3016,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_8a.png", - "ascent": %ascent%, + "file": "8a", "chars": [ "\u8a00\u8a01\u8a02\u8a03\u8a04\u8a05\u8a06\u8a07\u8a08\u8a09\u8a0a\u8a0b\u8a0c\u8a0d\u8a0e\u8a0f", "\u8a10\u8a11\u8a12\u8a13\u8a14\u8a15\u8a16\u8a17\u8a18\u8a19\u8a1a\u8a1b\u8a1c\u8a1d\u8a1e\u8a1f", @@ -3363,8 +3038,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_8b.png", - "ascent": %ascent%, + "file": "8b", "chars": [ "\u8b00\u8b01\u8b02\u8b03\u8b04\u8b05\u8b06\u8b07\u8b08\u8b09\u8b0a\u8b0b\u8b0c\u8b0d\u8b0e\u8b0f", "\u8b10\u8b11\u8b12\u8b13\u8b14\u8b15\u8b16\u8b17\u8b18\u8b19\u8b1a\u8b1b\u8b1c\u8b1d\u8b1e\u8b1f", @@ -3386,8 +3060,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_8c.png", - "ascent": %ascent%, + "file": "8c", "chars": [ "\u8c00\u8c01\u8c02\u8c03\u8c04\u8c05\u8c06\u8c07\u8c08\u8c09\u8c0a\u8c0b\u8c0c\u8c0d\u8c0e\u8c0f", "\u8c10\u8c11\u8c12\u8c13\u8c14\u8c15\u8c16\u8c17\u8c18\u8c19\u8c1a\u8c1b\u8c1c\u8c1d\u8c1e\u8c1f", @@ -3409,8 +3082,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_8d.png", - "ascent": %ascent%, + "file": "8d", "chars": [ "\u8d00\u8d01\u8d02\u8d03\u8d04\u8d05\u8d06\u8d07\u8d08\u8d09\u8d0a\u8d0b\u8d0c\u8d0d\u8d0e\u8d0f", "\u8d10\u8d11\u8d12\u8d13\u8d14\u8d15\u8d16\u8d17\u8d18\u8d19\u8d1a\u8d1b\u8d1c\u8d1d\u8d1e\u8d1f", @@ -3432,8 +3104,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_8e.png", - "ascent": %ascent%, + "file": "8e", "chars": [ "\u8e00\u8e01\u8e02\u8e03\u8e04\u8e05\u8e06\u8e07\u8e08\u8e09\u8e0a\u8e0b\u8e0c\u8e0d\u8e0e\u8e0f", "\u8e10\u8e11\u8e12\u8e13\u8e14\u8e15\u8e16\u8e17\u8e18\u8e19\u8e1a\u8e1b\u8e1c\u8e1d\u8e1e\u8e1f", @@ -3455,8 +3126,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_8f.png", - "ascent": %ascent%, + "file": "8f", "chars": [ "\u8f00\u8f01\u8f02\u8f03\u8f04\u8f05\u8f06\u8f07\u8f08\u8f09\u8f0a\u8f0b\u8f0c\u8f0d\u8f0e\u8f0f", "\u8f10\u8f11\u8f12\u8f13\u8f14\u8f15\u8f16\u8f17\u8f18\u8f19\u8f1a\u8f1b\u8f1c\u8f1d\u8f1e\u8f1f", @@ -3478,8 +3148,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_90.png", - "ascent": %ascent%, + "file": "90", "chars": [ "\u9000\u9001\u9002\u9003\u9004\u9005\u9006\u9007\u9008\u9009\u900a\u900b\u900c\u900d\u900e\u900f", "\u9010\u9011\u9012\u9013\u9014\u9015\u9016\u9017\u9018\u9019\u901a\u901b\u901c\u901d\u901e\u901f", @@ -3501,8 +3170,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_91.png", - "ascent": %ascent%, + "file": "91", "chars": [ "\u9100\u9101\u9102\u9103\u9104\u9105\u9106\u9107\u9108\u9109\u910a\u910b\u910c\u910d\u910e\u910f", "\u9110\u9111\u9112\u9113\u9114\u9115\u9116\u9117\u9118\u9119\u911a\u911b\u911c\u911d\u911e\u911f", @@ -3524,8 +3192,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_92.png", - "ascent": %ascent%, + "file": "92", "chars": [ "\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207\u9208\u9209\u920a\u920b\u920c\u920d\u920e\u920f", "\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217\u9218\u9219\u921a\u921b\u921c\u921d\u921e\u921f", @@ -3547,8 +3214,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_93.png", - "ascent": %ascent%, + "file": "93", "chars": [ "\u9300\u9301\u9302\u9303\u9304\u9305\u9306\u9307\u9308\u9309\u930a\u930b\u930c\u930d\u930e\u930f", "\u9310\u9311\u9312\u9313\u9314\u9315\u9316\u9317\u9318\u9319\u931a\u931b\u931c\u931d\u931e\u931f", @@ -3570,8 +3236,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_94.png", - "ascent": %ascent%, + "file": "94", "chars": [ "\u9400\u9401\u9402\u9403\u9404\u9405\u9406\u9407\u9408\u9409\u940a\u940b\u940c\u940d\u940e\u940f", "\u9410\u9411\u9412\u9413\u9414\u9415\u9416\u9417\u9418\u9419\u941a\u941b\u941c\u941d\u941e\u941f", @@ -3593,8 +3258,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_95.png", - "ascent": %ascent%, + "file": "95", "chars": [ "\u9500\u9501\u9502\u9503\u9504\u9505\u9506\u9507\u9508\u9509\u950a\u950b\u950c\u950d\u950e\u950f", "\u9510\u9511\u9512\u9513\u9514\u9515\u9516\u9517\u9518\u9519\u951a\u951b\u951c\u951d\u951e\u951f", @@ -3616,8 +3280,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_96.png", - "ascent": %ascent%, + "file": "96", "chars": [ "\u9600\u9601\u9602\u9603\u9604\u9605\u9606\u9607\u9608\u9609\u960a\u960b\u960c\u960d\u960e\u960f", "\u9610\u9611\u9612\u9613\u9614\u9615\u9616\u9617\u9618\u9619\u961a\u961b\u961c\u961d\u961e\u961f", @@ -3639,8 +3302,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_97.png", - "ascent": %ascent%, + "file": "97", "chars": [ "\u9700\u9701\u9702\u9703\u9704\u9705\u9706\u9707\u9708\u9709\u970a\u970b\u970c\u970d\u970e\u970f", "\u9710\u9711\u9712\u9713\u9714\u9715\u9716\u9717\u9718\u9719\u971a\u971b\u971c\u971d\u971e\u971f", @@ -3662,8 +3324,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_98.png", - "ascent": %ascent%, + "file": "98", "chars": [ "\u9800\u9801\u9802\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980a\u980b\u980c\u980d\u980e\u980f", "\u9810\u9811\u9812\u9813\u9814\u9815\u9816\u9817\u9818\u9819\u981a\u981b\u981c\u981d\u981e\u981f", @@ -3685,8 +3346,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_99.png", - "ascent": %ascent%, + "file": "99", "chars": [ "\u9900\u9901\u9902\u9903\u9904\u9905\u9906\u9907\u9908\u9909\u990a\u990b\u990c\u990d\u990e\u990f", "\u9910\u9911\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919\u991a\u991b\u991c\u991d\u991e\u991f", @@ -3708,8 +3368,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_9a.png", - "ascent": %ascent%, + "file": "9a", "chars": [ "\u9a00\u9a01\u9a02\u9a03\u9a04\u9a05\u9a06\u9a07\u9a08\u9a09\u9a0a\u9a0b\u9a0c\u9a0d\u9a0e\u9a0f", "\u9a10\u9a11\u9a12\u9a13\u9a14\u9a15\u9a16\u9a17\u9a18\u9a19\u9a1a\u9a1b\u9a1c\u9a1d\u9a1e\u9a1f", @@ -3731,8 +3390,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_9b.png", - "ascent": %ascent%, + "file": "9b", "chars": [ "\u9b00\u9b01\u9b02\u9b03\u9b04\u9b05\u9b06\u9b07\u9b08\u9b09\u9b0a\u9b0b\u9b0c\u9b0d\u9b0e\u9b0f", "\u9b10\u9b11\u9b12\u9b13\u9b14\u9b15\u9b16\u9b17\u9b18\u9b19\u9b1a\u9b1b\u9b1c\u9b1d\u9b1e\u9b1f", @@ -3754,8 +3412,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_9c.png", - "ascent": %ascent%, + "file": "9c", "chars": [ "\u9c00\u9c01\u9c02\u9c03\u9c04\u9c05\u9c06\u9c07\u9c08\u9c09\u9c0a\u9c0b\u9c0c\u9c0d\u9c0e\u9c0f", "\u9c10\u9c11\u9c12\u9c13\u9c14\u9c15\u9c16\u9c17\u9c18\u9c19\u9c1a\u9c1b\u9c1c\u9c1d\u9c1e\u9c1f", @@ -3777,8 +3434,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_9d.png", - "ascent": %ascent%, + "file": "9d", "chars": [ "\u9d00\u9d01\u9d02\u9d03\u9d04\u9d05\u9d06\u9d07\u9d08\u9d09\u9d0a\u9d0b\u9d0c\u9d0d\u9d0e\u9d0f", "\u9d10\u9d11\u9d12\u9d13\u9d14\u9d15\u9d16\u9d17\u9d18\u9d19\u9d1a\u9d1b\u9d1c\u9d1d\u9d1e\u9d1f", @@ -3800,8 +3456,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_9e.png", - "ascent": %ascent%, + "file": "9e", "chars": [ "\u9e00\u9e01\u9e02\u9e03\u9e04\u9e05\u9e06\u9e07\u9e08\u9e09\u9e0a\u9e0b\u9e0c\u9e0d\u9e0e\u9e0f", "\u9e10\u9e11\u9e12\u9e13\u9e14\u9e15\u9e16\u9e17\u9e18\u9e19\u9e1a\u9e1b\u9e1c\u9e1d\u9e1e\u9e1f", @@ -3823,8 +3478,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_9f.png", - "ascent": %ascent%, + "file": "9f", "chars": [ "\u9f00\u9f01\u9f02\u9f03\u9f04\u9f05\u9f06\u9f07\u9f08\u9f09\u9f0a\u9f0b\u9f0c\u9f0d\u9f0e\u9f0f", "\u9f10\u9f11\u9f12\u9f13\u9f14\u9f15\u9f16\u9f17\u9f18\u9f19\u9f1a\u9f1b\u9f1c\u9f1d\u9f1e\u9f1f", @@ -3846,8 +3500,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a0.png", - "ascent": %ascent%, + "file": "a0", "chars": [ "\ua000\ua001\ua002\ua003\ua004\ua005\ua006\ua007\ua008\ua009\ua00a\ua00b\ua00c\ua00d\ua00e\ua00f", "\ua010\ua011\ua012\ua013\ua014\ua015\ua016\ua017\ua018\ua019\ua01a\ua01b\ua01c\ua01d\ua01e\ua01f", @@ -3869,8 +3522,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a1.png", - "ascent": %ascent%, + "file": "a1", "chars": [ "\ua100\ua101\ua102\ua103\ua104\ua105\ua106\ua107\ua108\ua109\ua10a\ua10b\ua10c\ua10d\ua10e\ua10f", "\ua110\ua111\ua112\ua113\ua114\ua115\ua116\ua117\ua118\ua119\ua11a\ua11b\ua11c\ua11d\ua11e\ua11f", @@ -3892,8 +3544,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a2.png", - "ascent": %ascent%, + "file": "a2", "chars": [ "\ua200\ua201\ua202\ua203\ua204\ua205\ua206\ua207\ua208\ua209\ua20a\ua20b\ua20c\ua20d\ua20e\ua20f", "\ua210\ua211\ua212\ua213\ua214\ua215\ua216\ua217\ua218\ua219\ua21a\ua21b\ua21c\ua21d\ua21e\ua21f", @@ -3915,8 +3566,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a3.png", - "ascent": %ascent%, + "file": "a3", "chars": [ "\ua300\ua301\ua302\ua303\ua304\ua305\ua306\ua307\ua308\ua309\ua30a\ua30b\ua30c\ua30d\ua30e\ua30f", "\ua310\ua311\ua312\ua313\ua314\ua315\ua316\ua317\ua318\ua319\ua31a\ua31b\ua31c\ua31d\ua31e\ua31f", @@ -3938,8 +3588,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a4.png", - "ascent": %ascent%, + "file": "a4", "chars": [ "\ua400\ua401\ua402\ua403\ua404\ua405\ua406\ua407\ua408\ua409\ua40a\ua40b\ua40c\ua40d\ua40e\ua40f", "\ua410\ua411\ua412\ua413\ua414\ua415\ua416\ua417\ua418\ua419\ua41a\ua41b\ua41c\ua41d\ua41e\ua41f", @@ -3961,8 +3610,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a5.png", - "ascent": %ascent%, + "file": "a5", "chars": [ "\ua500\ua501\ua502\ua503\ua504\ua505\ua506\ua507\ua508\ua509\ua50a\ua50b\ua50c\ua50d\ua50e\ua50f", "\ua510\ua511\ua512\ua513\ua514\ua515\ua516\ua517\ua518\ua519\ua51a\ua51b\ua51c\ua51d\ua51e\ua51f", @@ -3984,8 +3632,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a6.png", - "ascent": %ascent%, + "file": "a6", "chars": [ "\ua600\ua601\ua602\ua603\ua604\ua605\ua606\ua607\ua608\ua609\ua60a\ua60b\ua60c\ua60d\ua60e\ua60f", "\ua610\ua611\ua612\ua613\ua614\ua615\ua616\ua617\ua618\ua619\ua61a\ua61b\ua61c\ua61d\ua61e\ua61f", @@ -4007,8 +3654,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a7.png", - "ascent": %ascent%, + "file": "a7", "chars": [ "\ua700\ua701\ua702\ua703\ua704\ua705\ua706\ua707\ua708\ua709\ua70a\ua70b\ua70c\ua70d\ua70e\ua70f", "\ua710\ua711\ua712\ua713\ua714\ua715\ua716\ua717\ua718\ua719\ua71a\ua71b\ua71c\ua71d\ua71e\ua71f", @@ -4030,8 +3676,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a8.png", - "ascent": %ascent%, + "file": "a8", "chars": [ "\ua800\ua801\ua802\ua803\ua804\ua805\ua806\ua807\ua808\ua809\ua80a\ua80b\ua80c\ua80d\ua80e\ua80f", "\ua810\ua811\ua812\ua813\ua814\ua815\ua816\ua817\ua818\ua819\ua81a\ua81b\ua81c\ua81d\ua81e\ua81f", @@ -4053,8 +3698,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_a9.png", - "ascent": %ascent%, + "file": "a9", "chars": [ "\ua900\ua901\ua902\ua903\ua904\ua905\ua906\ua907\ua908\ua909\ua90a\ua90b\ua90c\ua90d\ua90e\ua90f", "\ua910\ua911\ua912\ua913\ua914\ua915\ua916\ua917\ua918\ua919\ua91a\ua91b\ua91c\ua91d\ua91e\ua91f", @@ -4076,8 +3720,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_aa.png", - "ascent": %ascent%, + "file": "aa", "chars": [ "\uaa00\uaa01\uaa02\uaa03\uaa04\uaa05\uaa06\uaa07\uaa08\uaa09\uaa0a\uaa0b\uaa0c\uaa0d\uaa0e\uaa0f", "\uaa10\uaa11\uaa12\uaa13\uaa14\uaa15\uaa16\uaa17\uaa18\uaa19\uaa1a\uaa1b\uaa1c\uaa1d\uaa1e\uaa1f", @@ -4099,8 +3742,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ab.png", - "ascent": %ascent%, + "file": "ab", "chars": [ "\uab00\uab01\uab02\uab03\uab04\uab05\uab06\uab07\uab08\uab09\uab0a\uab0b\uab0c\uab0d\uab0e\uab0f", "\uab10\uab11\uab12\uab13\uab14\uab15\uab16\uab17\uab18\uab19\uab1a\uab1b\uab1c\uab1d\uab1e\uab1f", @@ -4122,8 +3764,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ac.png", - "ascent": %ascent%, + "file": "ac", "chars": [ "\uac00\uac01\uac02\uac03\uac04\uac05\uac06\uac07\uac08\uac09\uac0a\uac0b\uac0c\uac0d\uac0e\uac0f", "\uac10\uac11\uac12\uac13\uac14\uac15\uac16\uac17\uac18\uac19\uac1a\uac1b\uac1c\uac1d\uac1e\uac1f", @@ -4145,8 +3786,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ad.png", - "ascent": %ascent%, + "file": "ad", "chars": [ "\uad00\uad01\uad02\uad03\uad04\uad05\uad06\uad07\uad08\uad09\uad0a\uad0b\uad0c\uad0d\uad0e\uad0f", "\uad10\uad11\uad12\uad13\uad14\uad15\uad16\uad17\uad18\uad19\uad1a\uad1b\uad1c\uad1d\uad1e\uad1f", @@ -4168,8 +3808,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ae.png", - "ascent": %ascent%, + "file": "ae", "chars": [ "\uae00\uae01\uae02\uae03\uae04\uae05\uae06\uae07\uae08\uae09\uae0a\uae0b\uae0c\uae0d\uae0e\uae0f", "\uae10\uae11\uae12\uae13\uae14\uae15\uae16\uae17\uae18\uae19\uae1a\uae1b\uae1c\uae1d\uae1e\uae1f", @@ -4191,8 +3830,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_af.png", - "ascent": %ascent%, + "file": "af", "chars": [ "\uaf00\uaf01\uaf02\uaf03\uaf04\uaf05\uaf06\uaf07\uaf08\uaf09\uaf0a\uaf0b\uaf0c\uaf0d\uaf0e\uaf0f", "\uaf10\uaf11\uaf12\uaf13\uaf14\uaf15\uaf16\uaf17\uaf18\uaf19\uaf1a\uaf1b\uaf1c\uaf1d\uaf1e\uaf1f", @@ -4214,8 +3852,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b0.png", - "ascent": %ascent%, + "file": "b0", "chars": [ "\ub000\ub001\ub002\ub003\ub004\ub005\ub006\ub007\ub008\ub009\ub00a\ub00b\ub00c\ub00d\ub00e\ub00f", "\ub010\ub011\ub012\ub013\ub014\ub015\ub016\ub017\ub018\ub019\ub01a\ub01b\ub01c\ub01d\ub01e\ub01f", @@ -4237,8 +3874,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b1.png", - "ascent": %ascent%, + "file": "b1", "chars": [ "\ub100\ub101\ub102\ub103\ub104\ub105\ub106\ub107\ub108\ub109\ub10a\ub10b\ub10c\ub10d\ub10e\ub10f", "\ub110\ub111\ub112\ub113\ub114\ub115\ub116\ub117\ub118\ub119\ub11a\ub11b\ub11c\ub11d\ub11e\ub11f", @@ -4260,8 +3896,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b2.png", - "ascent": %ascent%, + "file": "b2", "chars": [ "\ub200\ub201\ub202\ub203\ub204\ub205\ub206\ub207\ub208\ub209\ub20a\ub20b\ub20c\ub20d\ub20e\ub20f", "\ub210\ub211\ub212\ub213\ub214\ub215\ub216\ub217\ub218\ub219\ub21a\ub21b\ub21c\ub21d\ub21e\ub21f", @@ -4283,8 +3918,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b3.png", - "ascent": %ascent%, + "file": "b3", "chars": [ "\ub300\ub301\ub302\ub303\ub304\ub305\ub306\ub307\ub308\ub309\ub30a\ub30b\ub30c\ub30d\ub30e\ub30f", "\ub310\ub311\ub312\ub313\ub314\ub315\ub316\ub317\ub318\ub319\ub31a\ub31b\ub31c\ub31d\ub31e\ub31f", @@ -4306,8 +3940,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b4.png", - "ascent": %ascent%, + "file": "b4", "chars": [ "\ub400\ub401\ub402\ub403\ub404\ub405\ub406\ub407\ub408\ub409\ub40a\ub40b\ub40c\ub40d\ub40e\ub40f", "\ub410\ub411\ub412\ub413\ub414\ub415\ub416\ub417\ub418\ub419\ub41a\ub41b\ub41c\ub41d\ub41e\ub41f", @@ -4329,8 +3962,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b5.png", - "ascent": %ascent%, + "file": "b5", "chars": [ "\ub500\ub501\ub502\ub503\ub504\ub505\ub506\ub507\ub508\ub509\ub50a\ub50b\ub50c\ub50d\ub50e\ub50f", "\ub510\ub511\ub512\ub513\ub514\ub515\ub516\ub517\ub518\ub519\ub51a\ub51b\ub51c\ub51d\ub51e\ub51f", @@ -4352,8 +3984,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b6.png", - "ascent": %ascent%, + "file": "b6", "chars": [ "\ub600\ub601\ub602\ub603\ub604\ub605\ub606\ub607\ub608\ub609\ub60a\ub60b\ub60c\ub60d\ub60e\ub60f", "\ub610\ub611\ub612\ub613\ub614\ub615\ub616\ub617\ub618\ub619\ub61a\ub61b\ub61c\ub61d\ub61e\ub61f", @@ -4375,8 +4006,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b7.png", - "ascent": %ascent%, + "file": "b7", "chars": [ "\ub700\ub701\ub702\ub703\ub704\ub705\ub706\ub707\ub708\ub709\ub70a\ub70b\ub70c\ub70d\ub70e\ub70f", "\ub710\ub711\ub712\ub713\ub714\ub715\ub716\ub717\ub718\ub719\ub71a\ub71b\ub71c\ub71d\ub71e\ub71f", @@ -4398,8 +4028,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b8.png", - "ascent": %ascent%, + "file": "b8", "chars": [ "\ub800\ub801\ub802\ub803\ub804\ub805\ub806\ub807\ub808\ub809\ub80a\ub80b\ub80c\ub80d\ub80e\ub80f", "\ub810\ub811\ub812\ub813\ub814\ub815\ub816\ub817\ub818\ub819\ub81a\ub81b\ub81c\ub81d\ub81e\ub81f", @@ -4421,8 +4050,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_b9.png", - "ascent": %ascent%, + "file": "b9", "chars": [ "\ub900\ub901\ub902\ub903\ub904\ub905\ub906\ub907\ub908\ub909\ub90a\ub90b\ub90c\ub90d\ub90e\ub90f", "\ub910\ub911\ub912\ub913\ub914\ub915\ub916\ub917\ub918\ub919\ub91a\ub91b\ub91c\ub91d\ub91e\ub91f", @@ -4444,8 +4072,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ba.png", - "ascent": %ascent%, + "file": "ba", "chars": [ "\uba00\uba01\uba02\uba03\uba04\uba05\uba06\uba07\uba08\uba09\uba0a\uba0b\uba0c\uba0d\uba0e\uba0f", "\uba10\uba11\uba12\uba13\uba14\uba15\uba16\uba17\uba18\uba19\uba1a\uba1b\uba1c\uba1d\uba1e\uba1f", @@ -4467,8 +4094,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_bb.png", - "ascent": %ascent%, + "file": "bb", "chars": [ "\ubb00\ubb01\ubb02\ubb03\ubb04\ubb05\ubb06\ubb07\ubb08\ubb09\ubb0a\ubb0b\ubb0c\ubb0d\ubb0e\ubb0f", "\ubb10\ubb11\ubb12\ubb13\ubb14\ubb15\ubb16\ubb17\ubb18\ubb19\ubb1a\ubb1b\ubb1c\ubb1d\ubb1e\ubb1f", @@ -4490,8 +4116,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_bc.png", - "ascent": %ascent%, + "file": "bc", "chars": [ "\ubc00\ubc01\ubc02\ubc03\ubc04\ubc05\ubc06\ubc07\ubc08\ubc09\ubc0a\ubc0b\ubc0c\ubc0d\ubc0e\ubc0f", "\ubc10\ubc11\ubc12\ubc13\ubc14\ubc15\ubc16\ubc17\ubc18\ubc19\ubc1a\ubc1b\ubc1c\ubc1d\ubc1e\ubc1f", @@ -4513,8 +4138,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_bd.png", - "ascent": %ascent%, + "file": "bd", "chars": [ "\ubd00\ubd01\ubd02\ubd03\ubd04\ubd05\ubd06\ubd07\ubd08\ubd09\ubd0a\ubd0b\ubd0c\ubd0d\ubd0e\ubd0f", "\ubd10\ubd11\ubd12\ubd13\ubd14\ubd15\ubd16\ubd17\ubd18\ubd19\ubd1a\ubd1b\ubd1c\ubd1d\ubd1e\ubd1f", @@ -4536,8 +4160,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_be.png", - "ascent": %ascent%, + "file": "be", "chars": [ "\ube00\ube01\ube02\ube03\ube04\ube05\ube06\ube07\ube08\ube09\ube0a\ube0b\ube0c\ube0d\ube0e\ube0f", "\ube10\ube11\ube12\ube13\ube14\ube15\ube16\ube17\ube18\ube19\ube1a\ube1b\ube1c\ube1d\ube1e\ube1f", @@ -4559,8 +4182,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_bf.png", - "ascent": %ascent%, + "file": "bf", "chars": [ "\ubf00\ubf01\ubf02\ubf03\ubf04\ubf05\ubf06\ubf07\ubf08\ubf09\ubf0a\ubf0b\ubf0c\ubf0d\ubf0e\ubf0f", "\ubf10\ubf11\ubf12\ubf13\ubf14\ubf15\ubf16\ubf17\ubf18\ubf19\ubf1a\ubf1b\ubf1c\ubf1d\ubf1e\ubf1f", @@ -4582,8 +4204,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c0.png", - "ascent": %ascent%, + "file": "c0", "chars": [ "\uc000\uc001\uc002\uc003\uc004\uc005\uc006\uc007\uc008\uc009\uc00a\uc00b\uc00c\uc00d\uc00e\uc00f", "\uc010\uc011\uc012\uc013\uc014\uc015\uc016\uc017\uc018\uc019\uc01a\uc01b\uc01c\uc01d\uc01e\uc01f", @@ -4605,8 +4226,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c1.png", - "ascent": %ascent%, + "file": "c1", "chars": [ "\uc100\uc101\uc102\uc103\uc104\uc105\uc106\uc107\uc108\uc109\uc10a\uc10b\uc10c\uc10d\uc10e\uc10f", "\uc110\uc111\uc112\uc113\uc114\uc115\uc116\uc117\uc118\uc119\uc11a\uc11b\uc11c\uc11d\uc11e\uc11f", @@ -4628,8 +4248,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c2.png", - "ascent": %ascent%, + "file": "c2", "chars": [ "\uc200\uc201\uc202\uc203\uc204\uc205\uc206\uc207\uc208\uc209\uc20a\uc20b\uc20c\uc20d\uc20e\uc20f", "\uc210\uc211\uc212\uc213\uc214\uc215\uc216\uc217\uc218\uc219\uc21a\uc21b\uc21c\uc21d\uc21e\uc21f", @@ -4651,8 +4270,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c3.png", - "ascent": %ascent%, + "file": "c3", "chars": [ "\uc300\uc301\uc302\uc303\uc304\uc305\uc306\uc307\uc308\uc309\uc30a\uc30b\uc30c\uc30d\uc30e\uc30f", "\uc310\uc311\uc312\uc313\uc314\uc315\uc316\uc317\uc318\uc319\uc31a\uc31b\uc31c\uc31d\uc31e\uc31f", @@ -4674,8 +4292,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c4.png", - "ascent": %ascent%, + "file": "c4", "chars": [ "\uc400\uc401\uc402\uc403\uc404\uc405\uc406\uc407\uc408\uc409\uc40a\uc40b\uc40c\uc40d\uc40e\uc40f", "\uc410\uc411\uc412\uc413\uc414\uc415\uc416\uc417\uc418\uc419\uc41a\uc41b\uc41c\uc41d\uc41e\uc41f", @@ -4697,8 +4314,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c5.png", - "ascent": %ascent%, + "file": "c5", "chars": [ "\uc500\uc501\uc502\uc503\uc504\uc505\uc506\uc507\uc508\uc509\uc50a\uc50b\uc50c\uc50d\uc50e\uc50f", "\uc510\uc511\uc512\uc513\uc514\uc515\uc516\uc517\uc518\uc519\uc51a\uc51b\uc51c\uc51d\uc51e\uc51f", @@ -4720,8 +4336,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c6.png", - "ascent": %ascent%, + "file": "c6", "chars": [ "\uc600\uc601\uc602\uc603\uc604\uc605\uc606\uc607\uc608\uc609\uc60a\uc60b\uc60c\uc60d\uc60e\uc60f", "\uc610\uc611\uc612\uc613\uc614\uc615\uc616\uc617\uc618\uc619\uc61a\uc61b\uc61c\uc61d\uc61e\uc61f", @@ -4743,8 +4358,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c7.png", - "ascent": %ascent%, + "file": "c7", "chars": [ "\uc700\uc701\uc702\uc703\uc704\uc705\uc706\uc707\uc708\uc709\uc70a\uc70b\uc70c\uc70d\uc70e\uc70f", "\uc710\uc711\uc712\uc713\uc714\uc715\uc716\uc717\uc718\uc719\uc71a\uc71b\uc71c\uc71d\uc71e\uc71f", @@ -4766,8 +4380,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c8.png", - "ascent": %ascent%, + "file": "c8", "chars": [ "\uc800\uc801\uc802\uc803\uc804\uc805\uc806\uc807\uc808\uc809\uc80a\uc80b\uc80c\uc80d\uc80e\uc80f", "\uc810\uc811\uc812\uc813\uc814\uc815\uc816\uc817\uc818\uc819\uc81a\uc81b\uc81c\uc81d\uc81e\uc81f", @@ -4789,8 +4402,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_c9.png", - "ascent": %ascent%, + "file": "c9", "chars": [ "\uc900\uc901\uc902\uc903\uc904\uc905\uc906\uc907\uc908\uc909\uc90a\uc90b\uc90c\uc90d\uc90e\uc90f", "\uc910\uc911\uc912\uc913\uc914\uc915\uc916\uc917\uc918\uc919\uc91a\uc91b\uc91c\uc91d\uc91e\uc91f", @@ -4812,8 +4424,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ca.png", - "ascent": %ascent%, + "file": "ca", "chars": [ "\uca00\uca01\uca02\uca03\uca04\uca05\uca06\uca07\uca08\uca09\uca0a\uca0b\uca0c\uca0d\uca0e\uca0f", "\uca10\uca11\uca12\uca13\uca14\uca15\uca16\uca17\uca18\uca19\uca1a\uca1b\uca1c\uca1d\uca1e\uca1f", @@ -4835,8 +4446,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_cb.png", - "ascent": %ascent%, + "file": "cb", "chars": [ "\ucb00\ucb01\ucb02\ucb03\ucb04\ucb05\ucb06\ucb07\ucb08\ucb09\ucb0a\ucb0b\ucb0c\ucb0d\ucb0e\ucb0f", "\ucb10\ucb11\ucb12\ucb13\ucb14\ucb15\ucb16\ucb17\ucb18\ucb19\ucb1a\ucb1b\ucb1c\ucb1d\ucb1e\ucb1f", @@ -4858,8 +4468,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_cc.png", - "ascent": %ascent%, + "file": "cc", "chars": [ "\ucc00\ucc01\ucc02\ucc03\ucc04\ucc05\ucc06\ucc07\ucc08\ucc09\ucc0a\ucc0b\ucc0c\ucc0d\ucc0e\ucc0f", "\ucc10\ucc11\ucc12\ucc13\ucc14\ucc15\ucc16\ucc17\ucc18\ucc19\ucc1a\ucc1b\ucc1c\ucc1d\ucc1e\ucc1f", @@ -4881,8 +4490,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_cd.png", - "ascent": %ascent%, + "file": "cd", "chars": [ "\ucd00\ucd01\ucd02\ucd03\ucd04\ucd05\ucd06\ucd07\ucd08\ucd09\ucd0a\ucd0b\ucd0c\ucd0d\ucd0e\ucd0f", "\ucd10\ucd11\ucd12\ucd13\ucd14\ucd15\ucd16\ucd17\ucd18\ucd19\ucd1a\ucd1b\ucd1c\ucd1d\ucd1e\ucd1f", @@ -4904,8 +4512,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ce.png", - "ascent": %ascent%, + "file": "ce", "chars": [ "\uce00\uce01\uce02\uce03\uce04\uce05\uce06\uce07\uce08\uce09\uce0a\uce0b\uce0c\uce0d\uce0e\uce0f", "\uce10\uce11\uce12\uce13\uce14\uce15\uce16\uce17\uce18\uce19\uce1a\uce1b\uce1c\uce1d\uce1e\uce1f", @@ -4927,8 +4534,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_cf.png", - "ascent": %ascent%, + "file": "cf", "chars": [ "\ucf00\ucf01\ucf02\ucf03\ucf04\ucf05\ucf06\ucf07\ucf08\ucf09\ucf0a\ucf0b\ucf0c\ucf0d\ucf0e\ucf0f", "\ucf10\ucf11\ucf12\ucf13\ucf14\ucf15\ucf16\ucf17\ucf18\ucf19\ucf1a\ucf1b\ucf1c\ucf1d\ucf1e\ucf1f", @@ -4950,8 +4556,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d0.png", - "ascent": %ascent%, + "file": "d0", "chars": [ "\ud000\ud001\ud002\ud003\ud004\ud005\ud006\ud007\ud008\ud009\ud00a\ud00b\ud00c\ud00d\ud00e\ud00f", "\ud010\ud011\ud012\ud013\ud014\ud015\ud016\ud017\ud018\ud019\ud01a\ud01b\ud01c\ud01d\ud01e\ud01f", @@ -4973,8 +4578,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d1.png", - "ascent": %ascent%, + "file": "d1", "chars": [ "\ud100\ud101\ud102\ud103\ud104\ud105\ud106\ud107\ud108\ud109\ud10a\ud10b\ud10c\ud10d\ud10e\ud10f", "\ud110\ud111\ud112\ud113\ud114\ud115\ud116\ud117\ud118\ud119\ud11a\ud11b\ud11c\ud11d\ud11e\ud11f", @@ -4996,8 +4600,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d2.png", - "ascent": %ascent%, + "file": "d2", "chars": [ "\ud200\ud201\ud202\ud203\ud204\ud205\ud206\ud207\ud208\ud209\ud20a\ud20b\ud20c\ud20d\ud20e\ud20f", "\ud210\ud211\ud212\ud213\ud214\ud215\ud216\ud217\ud218\ud219\ud21a\ud21b\ud21c\ud21d\ud21e\ud21f", @@ -5019,8 +4622,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d3.png", - "ascent": %ascent%, + "file": "d3", "chars": [ "\ud300\ud301\ud302\ud303\ud304\ud305\ud306\ud307\ud308\ud309\ud30a\ud30b\ud30c\ud30d\ud30e\ud30f", "\ud310\ud311\ud312\ud313\ud314\ud315\ud316\ud317\ud318\ud319\ud31a\ud31b\ud31c\ud31d\ud31e\ud31f", @@ -5042,8 +4644,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d4.png", - "ascent": %ascent%, + "file": "d4", "chars": [ "\ud400\ud401\ud402\ud403\ud404\ud405\ud406\ud407\ud408\ud409\ud40a\ud40b\ud40c\ud40d\ud40e\ud40f", "\ud410\ud411\ud412\ud413\ud414\ud415\ud416\ud417\ud418\ud419\ud41a\ud41b\ud41c\ud41d\ud41e\ud41f", @@ -5065,8 +4666,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d5.png", - "ascent": %ascent%, + "file": "d5", "chars": [ "\ud500\ud501\ud502\ud503\ud504\ud505\ud506\ud507\ud508\ud509\ud50a\ud50b\ud50c\ud50d\ud50e\ud50f", "\ud510\ud511\ud512\ud513\ud514\ud515\ud516\ud517\ud518\ud519\ud51a\ud51b\ud51c\ud51d\ud51e\ud51f", @@ -5088,8 +4688,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d6.png", - "ascent": %ascent%, + "file": "d6", "chars": [ "\ud600\ud601\ud602\ud603\ud604\ud605\ud606\ud607\ud608\ud609\ud60a\ud60b\ud60c\ud60d\ud60e\ud60f", "\ud610\ud611\ud612\ud613\ud614\ud615\ud616\ud617\ud618\ud619\ud61a\ud61b\ud61c\ud61d\ud61e\ud61f", @@ -5111,8 +4710,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_d7.png", - "ascent": %ascent%, + "file": "d7", "chars": [ "\ud700\ud701\ud702\ud703\ud704\ud705\ud706\ud707\ud708\ud709\ud70a\ud70b\ud70c\ud70d\ud70e\ud70f", "\ud710\ud711\ud712\ud713\ud714\ud715\ud716\ud717\ud718\ud719\ud71a\ud71b\ud71c\ud71d\ud71e\ud71f", @@ -5134,8 +4732,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_f9.png", - "ascent": %ascent%, + "file": "f9", "chars": [ "\uf900\uf901\uf902\uf903\uf904\uf905\uf906\uf907\uf908\uf909\uf90a\uf90b\uf90c\uf90d\uf90e\uf90f", "\uf910\uf911\uf912\uf913\uf914\uf915\uf916\uf917\uf918\uf919\uf91a\uf91b\uf91c\uf91d\uf91e\uf91f", @@ -5157,8 +4754,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_fa.png", - "ascent": %ascent%, + "file": "fa", "chars": [ "\ufa00\ufa01\ufa02\ufa03\ufa04\ufa05\ufa06\ufa07\ufa08\ufa09\ufa0a\ufa0b\ufa0c\ufa0d\ufa0e\ufa0f", "\ufa10\ufa11\ufa12\ufa13\ufa14\ufa15\ufa16\ufa17\ufa18\ufa19\ufa1a\ufa1b\ufa1c\ufa1d\ufa1e\ufa1f", @@ -5180,8 +4776,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_fb.png", - "ascent": %ascent%, + "file": "fb", "chars": [ "\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb07\ufb08\ufb09\ufb0a\ufb0b\ufb0c\ufb0d\ufb0e\ufb0f", "\ufb10\ufb11\ufb12\ufb13\ufb14\ufb15\ufb16\ufb17\ufb18\ufb19\ufb1a\ufb1b\ufb1c\ufb1d\ufb1e\ufb1f", @@ -5203,8 +4798,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_fc.png", - "ascent": %ascent%, + "file": "fc", "chars": [ "\ufc00\ufc01\ufc02\ufc03\ufc04\ufc05\ufc06\ufc07\ufc08\ufc09\ufc0a\ufc0b\ufc0c\ufc0d\ufc0e\ufc0f", "\ufc10\ufc11\ufc12\ufc13\ufc14\ufc15\ufc16\ufc17\ufc18\ufc19\ufc1a\ufc1b\ufc1c\ufc1d\ufc1e\ufc1f", @@ -5226,8 +4820,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_fd.png", - "ascent": %ascent%, + "file": "fd", "chars": [ "\ufd00\ufd01\ufd02\ufd03\ufd04\ufd05\ufd06\ufd07\ufd08\ufd09\ufd0a\ufd0b\ufd0c\ufd0d\ufd0e\ufd0f", "\ufd10\ufd11\ufd12\ufd13\ufd14\ufd15\ufd16\ufd17\ufd18\ufd19\ufd1a\ufd1b\ufd1c\ufd1d\ufd1e\ufd1f", @@ -5249,8 +4842,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_fe.png", - "ascent": %ascent%, + "file": "fe", "chars": [ "\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f", "\ufe10\ufe11\ufe12\ufe13\ufe14\ufe15\ufe16\ufe17\ufe18\ufe19\ufe1a\ufe1b\ufe1c\ufe1d\ufe1e\ufe1f", @@ -5272,8 +4864,7 @@ }, { "type": "bitmap", - "file": "minecraft:font/unicode_page_ff.png", - "ascent": %ascent%, + "file": "ff", "chars": [ "\uff00\uff01\uff02\uff03\uff04\uff05\uff06\uff07\uff08\uff09\uff0a\uff0b\uff0c\uff0d\uff0e\uff0f", "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19\uff1a\uff1b\uff1c\uff1d\uff1e\uff1f", diff --git a/build.gradle.kts b/build.gradle.kts index cdab2f8..e0e22cd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,87 +1,62 @@ +import org.gradle.process.internal.ExecException +import java.io.ByteArrayOutputStream + plugins { id("java") - id("application") - id("maven-publish") - id("io.github.goooler.shadow") version "8.1.7" } -allprojects { +val git : String = versionBanner() +val builder : String = builder() +ext["git_version"] = git +ext["builder"] = builder - version = "2.4.8.2" +subprojects { - apply() apply(plugin = "java") - apply(plugin = "application") - apply(plugin = "io.github.goooler.shadow") - apply(plugin = "org.gradle.maven-publish") - - application { - mainClass.set("") - } + apply(plugin = "java-library") repositories { mavenCentral() - maven("https://maven.aliyun.com/repository/public/") - maven("https://papermc.io/repo/repository/maven-public/") - maven("https://oss.sonatype.org/content/groups/public/") - maven("https://repo.dmulloy2.net/repository/public/") - maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") - maven("https://repo.codemc.org/repository/maven-public/") - maven("https://maven.enginehub.org/repo/") - maven("https://jitpack.io/") - maven("https://mvn.lumine.io/repository/maven-public/") - maven("https://repo.rapture.pw/repository/maven-releases/") - maven("https://nexus.phoenixdevt.fr/repository/maven-public/") - maven("https://r.irepo.space/maven/") - maven("https://repo.auxilor.io/repository/maven-public/") - maven("https://betonquest.org/nexus/repository/betonquest/") - maven("https://repo.william278.net/releases/") - maven("https://repo.william278.net/velocity/") - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") - maven("https://repo.minebench.de/") - maven("https://repo.xenondevs.xyz/releases/") - maven("https://repo.kryptonmc.org/releases") - maven("https://repo.oraxen.com/releases/") - maven("https://nexus.codecrafter47.de/content/repositories/public/") - maven("https://repo.opencollab.dev/main/") - maven("https://repo.md-5.net/content/groups/public/") + } + + tasks.processResources { + filteringCharset = "UTF-8" + + filesMatching(arrayListOf("custom-nameplates.properties")) { + expand(rootProject.properties) + } + + filesMatching(arrayListOf("*.yml", "*/*.yml")) { + expand( + Pair("project_version", rootProject.properties["project_version"]), + Pair("config_version", rootProject.properties["config_version"]) + ) + } } } -subprojects { - tasks.processResources { - val props = mapOf("version" to version) - inputs.properties(props) - filteringCharset = "UTF-8" - filesMatching("*plugin.yml") { - expand(props) +fun versionBanner(): String { + val os = ByteArrayOutputStream() + try { + project.exec { + commandLine = "git rev-parse --short=8 HEAD".split(" ") + standardOutput = os } - filesMatching("bungee.yml") { - expand(props) - } - } - - tasks.withType { - options.encoding = "UTF-8" - options.release.set(17) - } - - tasks.shadowJar { - destinationDirectory.set(file("$rootDir/target")) - archiveClassifier.set("") - archiveFileName.set("CustomNameplates-" + project.name + "-" + project.version + ".jar") - } - - if ("api" == project.name) { - publishing { - publications { - create("mavenJava") { - groupId = "net.momirealms" - artifactId = "CustomNameplates" - version = rootProject.version.toString() - artifact(tasks.shadowJar) - } - } + } catch (e: ExecException) { + return "Unknown" + } + return String(os.toByteArray()).trim() +} + +fun builder(): String { + val os = ByteArrayOutputStream() + try { + project.exec { + commandLine = "git config user.name".split(" ") + standardOutput = os } + } catch (e: ExecException) { + return "Unknown" } + return String(os.toByteArray()).trim() } \ No newline at end of file diff --git a/bungeecord/.gitignore b/bukkit/.gitignore similarity index 100% rename from bungeecord/.gitignore rename to bukkit/.gitignore diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts new file mode 100644 index 0000000..b77d15c --- /dev/null +++ b/bukkit/build.gradle.kts @@ -0,0 +1,87 @@ +plugins { + id("io.github.goooler.shadow") version "8.1.8" +} + +repositories { + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi + maven("https://libraries.minecraft.net") // brigadier + maven("https://jitpack.io/") + maven("https://papermc.io/repo/repository/maven-public/") // paper + maven("https://oss.sonatype.org/content/repositories/snapshots") + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // spigot +} + +dependencies { + implementation(project(":api")) { + exclude("dev.dejvokep", "boosted-yaml") + } + implementation(project(":common")) + implementation(project(":backend")) + implementation(project(":compatibility")) + + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + + // YAML + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + + // Adventure + implementation("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") + implementation("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}") + implementation("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") { + exclude("com.google.code.gson", "gson") + } + + // BStats + compileOnly("org.bstats:bstats-bukkit:${rootProject.properties["bstats_version"]}") + + // Cloud + compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") + compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}") + compileOnly("org.incendo:cloud-paper:${rootProject.properties["cloud_paper_version"]}") + // Netty + compileOnly("io.netty:netty-all:4.1.113.Final") +} + +tasks { + shadowJar { + archiveFileName = "CustomNameplates-bukkit-${rootProject.properties["project_version"]}.jar" + destinationDirectory.set(file("$rootDir/target")) + relocate("net.kyori", "net.momirealms.customnameplates.libraries") + relocate("org.incendo", "net.momirealms.customnameplates.libraries") + relocate("dev.dejvokep", "net.momirealms.customnameplates.libraries") + relocate("net.bytebuddy", "net.momirealms.customnameplates.libraries.bytebuddy") + relocate("org.apache.commons.pool2", "net.momirealms.customnameplates.libraries.commonspool2") + relocate("com.mysql", "net.momirealms.customnameplates.libraries.mysql") + relocate("org.mariadb", "net.momirealms.customnameplates.libraries.mariadb") + relocate("com.zaxxer.hikari", "net.momirealms.customnameplates.libraries.hikari") + relocate("com.mongodb", "net.momirealms.customnameplates.libraries.mongodb") + relocate("org.bson", "net.momirealms.customnameplates.libraries.bson") + relocate("org.bstats", "net.momirealms.customnameplates.libraries.bstats") + relocate("com.github.benmanes.caffeine", "net.momirealms.customnameplates.libraries.caffeine") + relocate("net.objecthunter.exp4j", "net.momirealms.customnameplates.libraries.exp4j") + relocate("redis.clients.jedis", "net.momirealms.customnameplates.libraries.jedis") + relocate("org.apache.fontbox", "net.momirealms.customnameplates.libraries.fontbox") + relocate("org.apache.pdfbox", "net.momirealms.customnameplates.libraries.pdfbox") + relocate("org.apache.commons.io", "net.momirealms.customnameplates.libraries.commons.io") + } +} + +artifacts { + archives(tasks.shadowJar) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitBootstrap.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitBootstrap.java new file mode 100644 index 0000000..994d9eb --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitBootstrap.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class BukkitBootstrap extends JavaPlugin { + + private CustomNameplates nameplates; + private NameplatesPlugin plugin; + + @Override + public void onLoad() { + this.plugin = new BukkitCustomNameplates(this); + this.plugin.load(); + } + + @Override + public void onEnable() { + this.plugin.enable(); + } + + @Override + public void onDisable() { + this.plugin.disable(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCNPlayer.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCNPlayer.java new file mode 100644 index 0000000..e71925e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCNPlayer.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +import net.momirealms.customnameplates.api.AbstractCNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.util.Vector3; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class BukkitCNPlayer extends AbstractCNPlayer { + + private static final Attribute scaleAttribute = Registry.ATTRIBUTE.get(NamespacedKey.minecraft("generic.scale")); + + private final Player player; + + public BukkitCNPlayer(CustomNameplates plugin, Player player) { + super(plugin, player); + this.player = player; + } + + @Override + public String name() { + return player.getName(); + } + + @Override + public UUID uuid() { + return player.getUniqueId(); + } + + @Override + public int entityID() { + return player.getEntityId(); + } + + @Override + public Vector3 position() { + Location location = player.getLocation(); + return new Vector3(location.x(), location.y(), location.z()); + } + + @Override + public String world() { + return player.getWorld().getName(); + } + + @Override + public double scale() { + if (scaleAttribute == null) return 1; + return Optional.ofNullable(player.getAttribute(scaleAttribute)).map(AttributeInstance::getValue).orElse(1d); + } + + @Override + public boolean isCrouching() { + return player.isSneaking(); + } + + @Override + public boolean hasPermission(String permission) { + return player.hasPermission(permission); + } + + @Override + public long playerTime() { + return player.getPlayerTime(); + } + + @Override + public boolean isOnline() { + return player.isOnline(); + } + + @Override + public Set passengers() { + return player.getPassengers().stream().map(Entity::getEntityId).collect(Collectors.toSet()); + } + + @Override + public boolean isFlying() { + return player.isFlying(); + } + + @Override + public int remainingAir() { + return player.getRemainingAir(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitChatManager.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitChatManager.java new file mode 100644 index 0000000..5d7acff --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitChatManager.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatManager; +import net.momirealms.customnameplates.bukkit.compatibility.chat.*; +import net.momirealms.customnameplates.bukkit.compatibility.emoji.ItemsAdderEmojiProvider; +import net.momirealms.customnameplates.bukkit.compatibility.emoji.OraxenEmojiProvider; +import org.bukkit.Bukkit; + +public class BukkitChatManager extends AbstractChatManager { + + public BukkitChatManager(CustomNameplates plugin) { + super(plugin); + } + + @Override + protected void setUpPlatformProvider() { + if (ConfigManager.chatTrChat() && Bukkit.getPluginManager().isPluginEnabled("TrChat")) { + this.chatProvider = new TrChatProvider(plugin, this); + plugin.getPluginLogger().info("TrChat hooked"); + } else if (ConfigManager.chatVenture() && Bukkit.getPluginManager().isPluginEnabled("VentureChat")) { + this.chatProvider = new VentureChatProvider(plugin, this); + plugin.getPluginLogger().info("VentureChat hooked"); + } else if (ConfigManager.chatHusk() && Bukkit.getPluginManager().isPluginEnabled("HuskChat")) { + this.chatProvider = new HuskChatProvider(plugin, this); + plugin.getPluginLogger().info("HuskChat hooked"); + } else if (ConfigManager.chatCarbon() && Bukkit.getPluginManager().isPluginEnabled("CarbonChat")) { + this.chatProvider = new CarbonChatProvider(plugin, this); + plugin.getPluginLogger().info("CarbonChat hooked"); + } else if (ConfigManager.chatAdvanced() && Bukkit.getPluginManager().isPluginEnabled("AdvancedChat")) { + this.chatProvider = new AdvancedChatProvider(plugin, this); + plugin.getPluginLogger().info("AdvancedChat hooked"); + } else if (ConfigManager.chatEss() && Bukkit.getPluginManager().isPluginEnabled("EssentialsChat")) { + this.chatProvider = new EssentialsChatProvider(plugin, this); + plugin.getPluginLogger().info("EssentialsChat hooked"); + } else { + this.chatProvider = new AsyncChatProvider(plugin, this); + } + } + + @Override + protected void setUpPlatformEmojiProviders() { + if (Bukkit.getPluginManager().isPluginEnabled("ItemsAdder")) { + this.emojiProviders.add(new ItemsAdderEmojiProvider()); + } + if (Bukkit.getPluginManager().isPluginEnabled("Oraxen")) { + try { + this.emojiProviders.add(new OraxenEmojiProvider()); + } catch (Exception ignore) { + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitConfigManager.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitConfigManager.java new file mode 100644 index 0000000..49bbdc1 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitConfigManager.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +import net.momirealms.customnameplates.api.ConfigManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.net.URL; +import java.net.URLConnection; + +public class BukkitConfigManager extends ConfigManager { + + private final BukkitCustomNameplates plugin; + + public BukkitConfigManager(BukkitCustomNameplates plugin) { + super(plugin); + this.plugin = plugin; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public void saveResource(@NotNull String resourcePath) { + if (resourcePath.isEmpty()) { + throw new IllegalArgumentException("ResourcePath cannot be null or empty"); + } + + resourcePath = resourcePath.replace('\\', '/'); + InputStream in = getResource(resourcePath); + if (in == null) { + return; + } + + File outFile = new File(BukkitCustomNameplates.getInstance().getBootstrap().getDataFolder(), resourcePath); + int lastIndex = resourcePath.lastIndexOf('/'); + File outDir = new File(BukkitCustomNameplates.getInstance().getBootstrap().getDataFolder(), resourcePath.substring(0, Math.max(lastIndex, 0))); + + if (!outDir.exists()) { + outDir.mkdirs(); + } + + try { + if (!outFile.exists()) { + OutputStream out = new FileOutputStream(outFile); + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.close(); + in.close(); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + @Nullable + public static InputStream getResource(@NotNull String filename) { + try { + URL url = BukkitCustomNameplates.getInstance().getBootstrap().getClass().getClassLoader().getResource(filename); + if (url == null) { + return null; + } + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException ex) { + return null; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java new file mode 100644 index 0000000..08dcd56 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java @@ -0,0 +1,351 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +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.event.NameplatesReloadEvent; +import net.momirealms.customnameplates.api.feature.ChatListener; +import net.momirealms.customnameplates.api.feature.JoinQuitListener; +import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManagerImpl; +import net.momirealms.customnameplates.api.feature.advance.AdvanceManagerImpl; +import net.momirealms.customnameplates.api.feature.background.BackgroundManagerImpl; +import net.momirealms.customnameplates.api.feature.bossbar.BossBarManagerImpl; +import net.momirealms.customnameplates.api.feature.bubble.BubbleManagerImpl; +import net.momirealms.customnameplates.api.feature.image.ImageManagerImpl; +import net.momirealms.customnameplates.api.feature.nameplate.NameplateManagerImpl; +import net.momirealms.customnameplates.api.feature.pack.ResourcePackManagerImpl; +import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManagerImpl; +import net.momirealms.customnameplates.api.helper.VersionHelper; +import net.momirealms.customnameplates.api.placeholder.PlaceholderManagerImpl; +import net.momirealms.customnameplates.backend.storage.StorageManagerImpl; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandManager; +import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExpansion; +import net.momirealms.customnameplates.bukkit.requirement.BukkitRequirementManager; +import net.momirealms.customnameplates.bukkit.scheduler.BukkitSchedulerAdapter; +import net.momirealms.customnameplates.common.dependency.Dependency; +import net.momirealms.customnameplates.common.dependency.DependencyManagerImpl; +import net.momirealms.customnameplates.common.event.EventManager; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import net.momirealms.customnameplates.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customnameplates.common.plugin.classpath.ReflectionClassPathAppender; +import net.momirealms.customnameplates.common.plugin.logging.JavaPluginLogger; +import net.momirealms.customnameplates.common.plugin.logging.PluginLogger; +import net.momirealms.customnameplates.common.plugin.scheduler.AbstractJavaScheduler; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerAdapter; +import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class BukkitCustomNameplates extends CustomNameplates implements Listener { + + private static BukkitCustomNameplates instance; + + private final ClassPathAppender classPathAppender; + private final PluginLogger logger; + private final AbstractJavaScheduler scheduler; + + private BukkitSenderFactory senderFactory; + private BukkitCommandManager commandManager; + + private final JavaPlugin bootstrap; + + private final List joinQuitListeners = new ArrayList<>(); + + private boolean loaded = false; + + private String buildByBit = "%%__BUILTBYBIT__%%"; + private String polymart = "%%__POLYMART__%%"; + private String time = "%%__TIMESTAMP__%%"; + private String user = "%%__USER__%%"; + private String username = "%%__USERNAME__%%"; + + private boolean isLatest = false; + + public BukkitCustomNameplates(JavaPlugin bootstrap) { + this.bootstrap = bootstrap; + VersionHelper.init(getServerVersion()); + this.scheduler = new BukkitSchedulerAdapter(this); + this.classPathAppender = new ReflectionClassPathAppender(this); + this.logger = new JavaPluginLogger(getBootstrap().getLogger()); + this.dependencyManager = new DependencyManagerImpl(this); + instance = this; + } + + @Override + public void load() { + this.dependencyManager.loadDependencies( + List.of( + Dependency.BOOSTED_YAML, + Dependency.BSTATS_BASE, Dependency.BSTATS_BUKKIT, + Dependency.CAFFEINE, + Dependency.GEANTY_REF, + Dependency.CLOUD_CORE, Dependency.CLOUD_SERVICES, Dependency.CLOUD_BUKKIT, Dependency.CLOUD_PAPER, Dependency.CLOUD_BRIGADIER, Dependency.CLOUD_MINECRAFT_EXTRAS, + Dependency.GSON, + Dependency.COMMONS_POOL_2, + Dependency.JEDIS, + Dependency.MYSQL_DRIVER, Dependency.MARIADB_DRIVER, + Dependency.SQLITE_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, + Dependency.H2_DRIVER, + Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON, + Dependency.HIKARI_CP, + Dependency.FONT_BOX, Dependency.PDF_BOX, + Dependency.BYTE_BUDDY, + Dependency.COMMONS_IO + ) + ); + } + + @Override + public void enable() { + if (!VersionHelper.isVersionNewerThan1_19_4()) { + getPluginLogger().severe("CustomNameplates only supports 1.19.4+ servers"); + Bukkit.getPluginManager().disablePlugin(this.getBootstrap()); + return; + } + + BukkitNetworkManager networkManager = new BukkitNetworkManager(this); + this.packetSender = networkManager; + this.pipelineInjector = networkManager; + this.commandManager = new BukkitCommandManager(this); + this.senderFactory = new BukkitSenderFactory(this); + this.platform = new BukkitPlatform(this); + this.senderFactory = new BukkitSenderFactory(this); + this.configManager = new BukkitConfigManager(this); + this.translationManager = new TranslationManager(this); + this.placeholderManager = new PlaceholderManagerImpl(this); + this.actionBarManager = new ActionBarManagerImpl(this); + this.bossBarManager = new BossBarManagerImpl(this); + this.advanceManager = new AdvanceManagerImpl(this); + this.backgroundManager = new BackgroundManagerImpl(this); + this.bubbleManager = new BubbleManagerImpl(this); + this.nameplateManager = new NameplateManagerImpl(this); + this.imageManager = new ImageManagerImpl(this); + this.unlimitedTagManager = new UnlimitedTagManagerImpl(this); + this.requirementManager = new BukkitRequirementManager(this); + this.storageManager = new StorageManagerImpl(this); + this.chatManager = new BukkitChatManager(this); + this.resourcePackManager = new ResourcePackManagerImpl(this); + this.eventManager = EventManager.create(this); + + this.registerJoinQuitListener((JoinQuitListener) storageManager); + this.registerJoinQuitListener((JoinQuitListener) actionBarManager); + this.registerJoinQuitListener((JoinQuitListener) bossBarManager); + this.registerJoinQuitListener((JoinQuitListener) unlimitedTagManager); + this.chatManager.registerListener((ChatListener) bubbleManager); + + Bukkit.getPluginManager().registerEvents(this, getBootstrap()); + + this.commandManager.registerDefaultFeatures(); + this.reload(); + + this.loaded = true; + + if (ConfigManager.metrics()) new Metrics(getBootstrap(), 16649); + if (ConfigManager.generateOnStart()) { + this.resourcePackManager.generate(); + } + if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { + new NameplatesExpansion(this).register(); + } + + boolean downloadFromPolymart = polymart.equals("1"); + boolean downloadFromBBB = buildByBit.equals("true"); + + if (ConfigManager.checkUpdate()) { + VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> { + String link; + if (downloadFromPolymart) { + link = "https://polymart.org/resource/2543/"; + } else if (downloadFromBBB) { + link = "https://builtbybit.com/resources/36359/"; + } else { + link = "https://github.com/Xiao-MoMi/Custom-Nameplates/"; + } + if (!result) { + this.getPluginLogger().info("You are using the latest version."); + isLatest = true; + } else { + this.getPluginLogger().warn("Update is available: " + link); + isLatest = false; + } + }); + } + } + + @Override + public void disable() { + if (!this.loaded) return; + super.disable(); + + this.configManager.disable(); + this.actionBarManager.disable(); + this.bossBarManager.disable(); + this.unlimitedTagManager.disable(); + this.advanceManager.disable(); + this.backgroundManager.disable(); + this.requirementManager.disable(); + this.placeholderManager.disable(); + this.storageManager.disable(); + this.bubbleManager.disable(); + this.nameplateManager.disable(); + this.imageManager.disable(); + this.chatManager.disable(); + + this.commandManager.unregisterFeatures(); + this.joinQuitListeners.clear(); + HandlerList.unregisterAll(this); + + this.loaded = false; + } + + @Override + public void reload() { + super.reload(); + for (CNPlayer player : getOnlinePlayers()) { + ((AbstractCNPlayer) player).reload(); + } + // load basics + this.configManager.reload(); + this.storageManager.reload(); + this.translationManager.reload(); + // prepare resources + this.backgroundManager.reload(); + this.bubbleManager.reload(); + this.nameplateManager.reload(); + this.imageManager.reload(); + this.advanceManager.reload(); + // set up placeholders + this.placeholderManager.reload(); + // set up requirements + this.requirementManager.reload(); + // use the placeholders + this.actionBarManager.reload(); + this.bossBarManager.reload(); + this.unlimitedTagManager.reload(); + this.chatManager.reload(); + // dispatch the event + this.eventManager.dispatch(NameplatesReloadEvent.class); + } + + @Override + public boolean isUpToDate() { + return isLatest; + } + + @Override + public InputStream getResourceStream(String filePath) { + return getBootstrap().getResource(filePath.replace("\\", "/")); + } + + @Override + public PluginLogger getPluginLogger() { + return logger; + } + + @Override + public ClassPathAppender getClassPathAppender() { + return classPathAppender; + } + + @Override + public SchedulerAdapter getScheduler() { + return scheduler; + } + + @Override + public Path getDataDirectory() { + return getBootstrap().getDataFolder().toPath().toAbsolutePath(); + } + + @Override + public String getServerVersion() { + return Bukkit.getServer().getBukkitVersion().split("-")[0]; + } + + @SuppressWarnings("deprecation") + @Override + public String getPluginVersion() { + return getBootstrap().getDescription().getVersion(); + } + + public JavaPlugin getBootstrap() { + return bootstrap; + } + + public static BukkitCustomNameplates getInstance() { + return instance; + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + CNPlayer cnPlayer = new BukkitCNPlayer(this, event.getPlayer()); + CNPlayer previous = onlinePlayerMap.put(cnPlayer.uuid(), cnPlayer); + if (previous != null) { + getPluginLogger().severe("Player " + event.getPlayer().getName() + " is duplicated"); + } + + entityIDFastLookup.put(cnPlayer.entityID(), cnPlayer); + pipelineInjector.inject(cnPlayer); + + for (JoinQuitListener listener : joinQuitListeners) { + listener.onPlayerJoin(cnPlayer); + } + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + CNPlayer cnPlayer = onlinePlayerMap.remove(event.getPlayer().getUniqueId()); + if (cnPlayer == null) { + getPluginLogger().severe("Player " + event.getPlayer().getName() + " is not recorded by CustomNameplates"); + return; + } + + entityIDFastLookup.remove(cnPlayer.entityID()); + pipelineInjector.uninject(cnPlayer); + + for (JoinQuitListener listener : joinQuitListeners) { + listener.onPlayerQuit(cnPlayer); + } + } + + public BukkitSenderFactory getSenderFactory() { + return senderFactory; + } + + public void registerJoinQuitListener(JoinQuitListener listener) { + this.joinQuitListeners.add(listener); + } + + public void unregisterJoinQuitListener(JoinQuitListener listener) { + this.joinQuitListeners.remove(listener); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitNetworkManager.java new file mode 100644 index 0000000..482c383 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitNetworkManager.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +import io.netty.channel.*; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.network.PacketEvent; +import net.momirealms.customnameplates.api.network.PacketSender; +import net.momirealms.customnameplates.api.network.PipelineInjector; +import net.momirealms.customnameplates.bukkit.util.Reflections; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.function.BiConsumer; + +public class BukkitNetworkManager implements PacketSender, PipelineInjector { + + private final BiConsumer> packetsConsumer; + private final CustomNameplates plugin; + + public BukkitNetworkManager(CustomNameplates plugin) { + this.plugin = plugin; + this.packetsConsumer = ((player, objects) -> { + try { + Object bundle = Reflections.constructor$ClientboundBundlePacket.newInstance(objects); + sendPacket(player, bundle); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }); + } + + @Override + public void sendPacket(@NotNull CNPlayer player, final Object packet) { + if (!player.isOnline()) return; + try { + Reflections.method$SendPacket.invoke( + Reflections.field$PlayerConnection.get( + Reflections.method$CraftPlayer$getHandle.invoke(player.player())), packet); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public void sendPacket(@NotNull CNPlayer player, final List packet) { + if (!player.isOnline()) return; + packetsConsumer.accept(player, packet); + } + + @Override + public Channel getChannel(CNPlayer player) { + try { + return (Channel) Reflections.field$Channel.get( + Reflections.field$NetworkManager.get( + Reflections.field$PlayerConnection.get( + Reflections.method$CraftPlayer$getHandle.invoke(player.player()) + ) + ) + ); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public ChannelDuplexHandler createHandler(CNPlayer player) { + return new CNChannelHandler(player); + } + + @Override + public void inject(CNPlayer player) { + Channel channel = getChannel(player); + try { + ChannelPipeline pipeline = channel.pipeline(); + for (Map.Entry entry : pipeline.toMap().entrySet()) { + if (Reflections.clazz$NetworkManager.isAssignableFrom(entry.getValue().getClass())) { + pipeline.addBefore(entry.getKey(), "nameplates_packet_handler", createHandler(player)); + break; + } + } + } catch (NoSuchElementException | IllegalArgumentException e) { + throw new RuntimeException(e); + } + } + + @Override + public void uninject(CNPlayer player) { + Channel channel = getChannel(player); + channel.eventLoop().submit(() -> { + channel.pipeline().remove("nameplates_packet_handler"); + return null; + }); + } + + public class CNChannelHandler extends ChannelDuplexHandler { + + private final CNPlayer player; + + public CNChannelHandler(CNPlayer player) { + this.player = player; + } + + @Override + public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) throws Exception { + try { + PacketEvent event = new PacketEvent(packet); + plugin.getPlatform().onPacketSend(player, event); + if (event.cancelled()) return; + super.write(context, packet, channelPromise); + channelPromise.addListener((p) -> { + for (Runnable task : event.getDelayedTasks()) { + task.run(); + } + }); + } catch (Throwable e) { + plugin.getPluginLogger().severe("An error occurred when reading packets", e); + super.write(context, packet, channelPromise); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java new file mode 100644 index 0000000..9e19bd6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java @@ -0,0 +1,503 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit; + +import it.unimi.dsi.fastutil.ints.IntList; +import me.clip.placeholderapi.PlaceholderAPI; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.Platform; +import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManagerImpl; +import net.momirealms.customnameplates.api.feature.bossbar.BossBar; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.api.helper.VersionHelper; +import net.momirealms.customnameplates.api.network.PacketEvent; +import net.momirealms.customnameplates.api.placeholder.DummyPlaceholder; +import net.momirealms.customnameplates.api.placeholder.Placeholder; +import net.momirealms.customnameplates.api.util.Alignment; +import net.momirealms.customnameplates.api.util.Vector3; +import net.momirealms.customnameplates.bukkit.util.EntityData; +import net.momirealms.customnameplates.bukkit.util.Reflections; +import net.momirealms.customnameplates.common.util.TriConsumer; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.function.Consumer; + +public class BukkitPlatform implements Platform { + + private final CustomNameplates plugin; + private final boolean placeholderAPI; + private final boolean geyser; + private final boolean floodGate; + private final boolean libsDisguises; + + private static final HashMap> packetFunctions = new HashMap<>(); + + private static void registerPacketConsumer(final TriConsumer functions, String... packet) { + for (String s : packet) { + packetFunctions.put(s, functions); + } + } + + public boolean hasPlaceholderAPI() { + return placeholderAPI; + } + + public boolean hasGeyser() { + return geyser; + } + + public boolean hasFloodGate() { + return floodGate; + } + + public boolean hasLibsDisguises() { + return libsDisguises; + } + + public BukkitPlatform(CustomNameplates plugin) { + this.plugin = plugin; + this.placeholderAPI = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null; + this.geyser = Bukkit.getPluginManager().getPlugin("Geyser-Spigot") != null; + this.floodGate = Bukkit.getPluginManager().getPlugin("floodgate") != null; + this.libsDisguises = Bukkit.getPluginManager().getPlugin("LibsDisguises") != null; + } + + static { + registerPacketConsumer((player, event, packet) -> { + if (!ConfigManager.actionbarModule()) return; + if (!ConfigManager.catchOtherActionBar()) return; + try { + Object component = Reflections.field$ClientboundSetActionBarTextPacket$text.get(packet); + Object contents = Reflections.method$Component$getContents.invoke(component); + if (Reflections.clazz$ScoreContents.isAssignableFrom(contents.getClass())) { + String name = (String) Reflections.field$ScoreContents$name.get(contents); + String objective = (String) Reflections.field$ScoreContents$objective.get(contents); + if (name.equals("np") && objective.equals("ab")) return; + } + CustomNameplates.getInstance().getScheduler().async().execute(() -> { + ((ActionBarManagerImpl) CustomNameplates.getInstance().getActionBarManager()).handleActionBarPacket(player, AdventureHelper.minecraftComponentToMiniMessage(component)); + }); + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundSetActionBarTextPacket", e); + } + event.cancelled(true); + }, "ClientboundSetActionBarTextPacket"); + + registerPacketConsumer((player, event, packet) -> { + if (!ConfigManager.actionbarModule()) return; + if (!ConfigManager.catchOtherActionBar()) return; + try { + boolean actionBar = (boolean) Reflections.field$ClientboundSystemChatPacket$overlay.get(packet); + if (actionBar) { + CustomNameplates.getInstance().getScheduler().async().execute(() -> { + try { + if (VersionHelper.isVersionNewerThan1_20_4()) { + // 1.20.4+ + Object component = Reflections.field$ClientboundSystemChatPacket$component.get(packet); + if (component == null) return; + ((ActionBarManagerImpl) CustomNameplates.getInstance().getActionBarManager()).handleActionBarPacket(player, AdventureHelper.minecraftComponentToMiniMessage(component)); + } else { + // 1.20.4- + String json = (String) Reflections.field$ClientboundSystemChatPacket$text.get(packet); + if (json == null) return; + ((ActionBarManagerImpl) CustomNameplates.getInstance().getActionBarManager()).handleActionBarPacket(player, AdventureHelper.jsonToMiniMessage(json)); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }); + event.cancelled(true); + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundSystemChatPacket", e); + } + }, "ClientboundSystemChatPacket"); + + // 1.20.2+ + registerPacketConsumer((player, event, packet) -> { + if (!VersionHelper.isVersionNewerThan1_20_2()) return; + try { + int entityID = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet); + CNPlayer added = CustomNameplates.getInstance().getPlayer(entityID); + if (added != null) { + added.addPlayerToTracker(player); + CustomNameplates.getInstance().getUnlimitedTagManager().onAddPlayer(added, player); + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundAddEntityPacket", e); + } + }, "ClientboundAddEntityPacket", "PacketPlayOutSpawnEntity"); + + // 1.19.4-1.20.1 + registerPacketConsumer((player, event, packet) -> { + if (VersionHelper.isVersionNewerThan1_20_2()) return; + try { + int entityID = (int) Reflections.field$PacketPlayOutNamedEntitySpawn$entityId.get(packet); + CNPlayer added = CustomNameplates.getInstance().getPlayer(entityID); + if (added != null) { + added.addPlayerToTracker(player); + CustomNameplates.getInstance().getUnlimitedTagManager().onAddPlayer(added, player); + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle PacketPlayOutNamedEntitySpawn", e); + } + }, "PacketPlayOutNamedEntitySpawn"); + + registerPacketConsumer((player, event, packet) -> { + try { + IntList intList = (IntList) Reflections.field$ClientboundRemoveEntitiesPacket$entityIds.get(packet); + for (int i : intList) { + CNPlayer removed = CustomNameplates.getInstance().getPlayer(i); + if (removed != null) { + removed.removePlayerFromTracker(player); + CustomNameplates.getInstance().getUnlimitedTagManager().onRemovePlayer(removed, player); + } + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundRemoveEntitiesPacket", e); + } + }, "PacketPlayOutEntityDestroy", "ClientboundRemoveEntitiesPacket"); + + // for cosmetic plugin compatibility + registerPacketConsumer((player, event, packet) -> { + try { + int[] passengers = (int[]) Reflections.field$ClientboundSetPassengersPacket$passengers.get(packet); + int vehicle = (int) Reflections.field$ClientboundSetPassengersPacket$vehicle.get(packet); + CNPlayer another = CustomNameplates.getInstance().getPlayer(vehicle); + if (another != null) { + Set otherEntities = player.getTrackedPassengerIds(another); + for (int passenger : passengers) { + otherEntities.add(passenger); + } + int[] merged = new int[otherEntities.size()]; + int index = 0; + for (Integer element : otherEntities) { + merged[index++] = element; + } + Reflections.field$ClientboundSetPassengersPacket$passengers.set(packet, merged); + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundSetPassengersPacket", e); + } + }, "PacketPlayOutMount", "ClientboundSetPassengersPacket"); + + registerPacketConsumer((player, event, packet) -> { + if (!VersionHelper.isVersionNewerThan1_20_5()) return; + try { + int entityID = (int) Reflections.field$ClientboundUpdateAttributesPacket$id.get(packet); + CNPlayer another = CustomNameplates.getInstance().getPlayer(entityID); + if (another != null) { + @SuppressWarnings("unchecked") + List attributes = (List) Reflections.field$ClientboundUpdateAttributesPacket$attributes.get(packet); + for (Object attributeSnapshot : attributes) { + Object attributeHolder = Reflections.field$ClientboundUpdateAttributesPacket$AttributeSnapshot$attribute.get(attributeSnapshot); + Object attribute = Reflections.method$Holder$value.invoke(attributeHolder); + String id = (String) Reflections.field$Attribute$id.get(attribute); + if (id.equals("attribute.name.generic.scale")) { + double baseValue = (double) Reflections.field$ClientboundUpdateAttributesPacket$AttributeSnapshot$base.get(attributeSnapshot); + @SuppressWarnings("unchecked") + Collection modifiers = (Collection) Reflections.field$ClientboundUpdateAttributesPacket$AttributeSnapshot$modifiers.get(attributeSnapshot); + for (Object modifier : modifiers) { + double amount = (double) Reflections.field$AttributeModifier$amount.get(modifier); + baseValue += amount; + } + CustomNameplates.getInstance().getUnlimitedTagManager().onPlayerAttributeSet(another, player, baseValue); + return; + } + } + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundUpdateAttributesPacket", e); + } + }, "ClientboundUpdateAttributesPacket", "PacketPlayOutUpdateAttributes"); + + registerPacketConsumer((player, event, packet) -> { + try { + int entityID = (int) Reflections.field$ClientboundSetEntityDataPacket$id.get(packet); + CNPlayer another = CustomNameplates.getInstance().getPlayer(entityID); + if (another != null) { + @SuppressWarnings("unchecked") + List dataValues = (List) Reflections.field$ClientboundSetEntityDataPacket$packedItems.get(packet); + for (Object dataValue : dataValues) { + int id = (int) Reflections.field$SynchedEntityData$DataValue$id.get(dataValue); + if (id == 0) { + byte value = (byte) Reflections.field$SynchedEntityData$DataValue$value.get(dataValue); + boolean isCrouching = EntityData.isCrouching(value); + CustomNameplates.getInstance().getUnlimitedTagManager().onPlayerDataSet(another, player, isCrouching); + return; + } + } + } + } catch (ReflectiveOperationException e) { + CustomNameplates.getInstance().getPluginLogger().severe("Failed to handle ClientboundSetEntityDataPacket", e); + } + }, "ClientboundSetEntityDataPacket", "PacketPlayOutEntityMetadata"); + } + + @Override + public Object jsonToMinecraftComponent(String json) { + if (VersionHelper.isVersionNewerThan1_20_5()) { + try { + return Reflections.method$Component$Serializer$fromJson.invoke(null, json, Reflections.instance$MinecraftRegistry); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } else { + try { + return Reflections.method$CraftChatMessage$fromJSON.invoke(null, json); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public String minecraftComponentToJson(Object component) { + if (VersionHelper.isVersionNewerThan1_20_5()) { + try { + return (String) Reflections.method$Component$Serializer$toJson.invoke(null, component, Reflections.instance$MinecraftRegistry); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } else { + try { + return (String) Reflections.method$CraftChatMessage$toJSON.invoke(null, component); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public Placeholder registerPlatformPlaceholder(String id) { + if (!placeholderAPI) { + return new DummyPlaceholder(id); + } + Placeholder placeholder; + if (id.startsWith("%rel_")) { + placeholder = plugin.getPlaceholderManager().registerRelationalPlaceholder(id, + // viewer // owner + (p1, p2) -> PlaceholderAPI.setRelationalPlaceholders((Player) p2.player(), (Player) p1.player(), id)); + } else if (id.startsWith("%shared_")) { + String sub = "%" + id.substring("%shared_".length()); + placeholder =plugin.getPlaceholderManager().registerSharedPlaceholder(id, + () -> PlaceholderAPI.setPlaceholders(null, sub)); + } else { + placeholder = plugin.getPlaceholderManager().registerPlayerPlaceholder(id, + (p) -> p == null ? PlaceholderAPI.setPlaceholders(null, id) : PlaceholderAPI.setPlaceholders((OfflinePlayer) p.player(), id)); + } + return placeholder; + } + + @Override + public Object setActionBarTextPacket(Object component) { + try { + return Reflections.constructor$ClientboundSetActionBarTextPacket.newInstance(component); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object createBossBarPacket(UUID uuid, Object component, float progress, BossBar.Overlay overlay, BossBar.Color color) { + try { + Object barColor = Reflections.method$BossEvent$BossBarColor$valueOf.invoke(null, color.name()); + Object barOverlay = Reflections.method$BossEvent$BossBarOverlay$valueOf.invoke(null, overlay.name()); + Object operationInstance = Reflections.allocateAddOperationInstance(); + Reflections.field$ClientboundBossEventPacket$AddOperation$name.set(operationInstance, component); + Reflections.field$ClientboundBossEventPacket$AddOperation$progress.set(operationInstance, progress); + Reflections.field$ClientboundBossEventPacket$AddOperation$color.set(operationInstance, barColor); + Reflections.field$ClientboundBossEventPacket$AddOperation$overlay.set(operationInstance, barOverlay); + Reflections.field$ClientboundBossEventPacket$AddOperation$darkenScreen.set(operationInstance, false); + Reflections.field$ClientboundBossEventPacket$AddOperation$playMusic.set(operationInstance, false); + Reflections.field$ClientboundBossEventPacket$AddOperation$createWorldFog.set(operationInstance, false); + return Reflections.constructor$ClientboundBossEventPacket.newInstance(uuid, operationInstance); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object removeBossBarPacket(UUID uuid) { + try { + return Reflections.constructor$ClientboundBossEventPacket.newInstance(uuid, Reflections.instance$ClientboundBossEventPacket$REMOVE_OPERATION); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object updateBossBarNamePacket(UUID uuid, Object component) { + try { + Object operation = Reflections.constructor$ClientboundBossEventPacket$UpdateNameOperation.newInstance(component); + return Reflections.constructor$ClientboundBossEventPacket.newInstance(uuid, operation); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public List 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 + ) { + try { + Object addEntityPacket = Reflections.constructor$ClientboundAddEntityPacket.newInstance( + entityID, uuid, position.x(), position.y(), position.z(), pitch, yaw, + Reflections.instance$EntityType$TEXT_DISPLAY, 0, Reflections.instance$Vec3$Zero, headYaw + ); + + // It's shit code + ArrayList values = new ArrayList<>(); + EntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(interpolationDelay, values); + if (VersionHelper.isVersionNewerThan1_20_2()) { + EntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(positionRotationInterpolationDuration, values); + EntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(transformationInterpolationDuration, values); + } else { + EntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(transformationInterpolationDuration, values); + } + EntityData.BillboardConstraints.addEntityDataIfNotDefaultValue((byte) 3, values); + EntityData.BackgroundColor.addEntityDataIfNotDefaultValue( backgroundColor, values); + EntityData.Text.addEntityDataIfNotDefaultValue( component, values); + EntityData.TextOpacity.addEntityDataIfNotDefaultValue( isCrouching ? 64 : opacity, values); + EntityData.ViewRange.addEntityDataIfNotDefaultValue( viewRange, values); + EntityData.ShadowRadius.addEntityDataIfNotDefaultValue( shadowRadius, values); + EntityData.ShadowStrength.addEntityDataIfNotDefaultValue( shadowStrength, values); + EntityData.LineWidth.addEntityDataIfNotDefaultValue( lineWidth, values); + EntityData.Scale.addEntityDataIfNotDefaultValue( scale.toVec3(), values); + EntityData.Translation.addEntityDataIfNotDefaultValue( translation.toVec3(), values); + EntityData.TextDisplayMasks.addEntityDataIfNotDefaultValue(EntityData.encodeMask(hasShadow, isSeeThrough, useDefaultBackgroundColor, alignment.getId()), values); + + Object setDataPacket = Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityID, values); + + return List.of(addEntityPacket, setDataPacket); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Consumer> createInterpolationDelayModifier(int delay) { + return (values) -> EntityData.InterpolationDelay.addEntityData(delay, values); + } + + @Override + public Consumer> createTransformationInterpolationDurationModifier(int duration) { + if (VersionHelper.isVersionNewerThan1_20_2()) { + return (values) -> EntityData.TransformationInterpolationDuration.addEntityData(duration, values); + } else { + return (values) -> EntityData.InterpolationDuration.addEntityData(duration, values); + } + } + + @Override + public Consumer> createTextComponentModifier(Object component) { + return (values) -> EntityData.Text.addEntityData(component, values); + } + + @Override + public Consumer> createScaleModifier(Vector3 scale) { + return (values) -> EntityData.Scale.addEntityData(scale.toVec3(), values); + } + + @Override + public Consumer> createTranslationModifier(Vector3 translation) { + return (values) -> EntityData.Translation.addEntityData(translation.toVec3(), values); + } + + @Override + public Consumer> createOpacityModifier(byte opacity) { + return (values) -> EntityData.TextOpacity.addEntityData(opacity, values); + } + + @Override + public Object updateTextDisplayPacket(int entityID, List>> modifiers) { + try { + ArrayList values = new ArrayList<>(); + for (Consumer> modifier : modifiers) { + modifier.accept(values); + } + return Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityID, values); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object setPassengersPacket(int vehicle, int[] passengers) { + try { + Object packet = Reflections.allocateClientboundSetPassengersPacketInstance(); + Reflections.field$ClientboundSetPassengersPacket$passengers.set(packet, passengers); + Reflections.field$ClientboundSetPassengersPacket$vehicle.set(packet, vehicle); + return packet; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object removeEntityPacket(int... entityID) { + try { + return Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) entityID); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object vec3(double x, double y, double z) { + try { + return Reflections.constructor$Vector3f.newInstance((float) x, (float) y, (float) z); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + @Override + public void onPacketSend(CNPlayer player, PacketEvent event) { + try { + Object packet = event.getPacket(); + if (Reflections.clazz$ClientboundBundlePacket.isInstance(packet)) { + Iterable packets = (Iterable) Reflections.field$BundlePacket$packets.get(packet); + for (Object p : packets) { + handlePacket(player, event, p); + } + } else { + handlePacket(player, event, packet); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + private void handlePacket(CNPlayer player, PacketEvent event, Object packet) throws ReflectiveOperationException { + Optional.ofNullable(packetFunctions.get(packet.getClass().getSimpleName())) + .ifPresent(function -> function.accept(player, event, packet)); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitSenderFactory.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitSenderFactory.java new file mode 100644 index 0000000..214b51d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitSenderFactory.java @@ -0,0 +1,111 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.bukkit; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.common.sender.Sender; +import net.momirealms.customnameplates.common.sender.SenderFactory; +import net.momirealms.customnameplates.common.util.Tristate; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.RemoteConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.util.UUID; + +public class BukkitSenderFactory extends SenderFactory { + private final BukkitAudiences audiences; + + public BukkitSenderFactory(BukkitCustomNameplates plugin) { + super(plugin); + this.audiences = BukkitAudiences.create(plugin.getBootstrap()); + } + + @Override + protected String getName(CommandSender sender) { + if (sender instanceof Player) { + return sender.getName(); + } + return Sender.CONSOLE_NAME; + } + + @Override + protected UUID getUniqueId(CommandSender sender) { + if (sender instanceof Player) { + return ((Player) sender).getUniqueId(); + } + return Sender.CONSOLE_UUID; + } + + @Override + public Audience getAudience(CommandSender sender) { + return this.audiences.sender(sender); + } + + @Override + protected void sendMessage(CommandSender sender, Component message) { + // we can safely send async for players and the console - otherwise, send it sync + if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) { + getAudience(sender).sendMessage(message); + } else { + getPlugin().getScheduler().executeSync(() -> getAudience(sender).sendMessage(message)); + } + } + + @Override + protected Tristate getPermissionValue(CommandSender sender, String node) { + if (sender.hasPermission(node)) { + return Tristate.TRUE; + } else if (sender.isPermissionSet(node)) { + return Tristate.FALSE; + } else { + return Tristate.UNDEFINED; + } + } + + @Override + protected boolean hasPermission(CommandSender sender, String node) { + return sender.hasPermission(node); + } + + @Override + protected void performCommand(CommandSender sender, String command) { + getPlugin().getBootstrap().getServer().dispatchCommand(sender, command); + } + + @Override + protected boolean isConsole(CommandSender sender) { + return sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender; + } + + @Override + public void close() { + super.close(); + this.audiences.close(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/BukkitCommandFeature.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/BukkitCommandFeature.java new file mode 100644 index 0000000..37432b0 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/BukkitCommandFeature.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.common.command.AbstractCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.sender.SenderFactory; +import net.momirealms.customnameplates.common.util.Pair; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.incendo.cloud.bukkit.data.Selector; + +import java.util.Collection; + +public abstract class BukkitCommandFeature extends AbstractCommandFeature { + + protected BukkitCustomNameplates plugin; + + public BukkitCommandFeature(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager); + this.plugin = plugin; + } + + @Override + @SuppressWarnings("unchecked") + protected SenderFactory getSenderFactory() { + return (SenderFactory) BukkitCustomNameplates.getInstance().getSenderFactory(); + } + + public Pair resolveSelector(Selector selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) { + Collection entities = selector.values(); + if (entities.size() == 1) { + return Pair.of(single, Component.text(entities.iterator().next().getName())); + } else { + return Pair.of(multiple, Component.text(entities.size())); + } + } + + public Pair resolveSelector(Collection selector, TranslatableComponent.Builder single, TranslatableComponent.Builder multiple) { + if (selector.size() == 1) { + return Pair.of(single, Component.text(selector.iterator().next().getName())); + } else { + return Pair.of(multiple, Component.text(selector.size())); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/BukkitCommandManager.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/BukkitCommandManager.java new file mode 100644 index 0000000..d971e47 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/BukkitCommandManager.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command; + +import net.kyori.adventure.util.Index; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.feature.*; +import net.momirealms.customnameplates.common.command.AbstractCommandManager; +import net.momirealms.customnameplates.common.command.CommandFeature; +import net.momirealms.customnameplates.common.sender.Sender; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.bukkit.CloudBukkitCapabilities; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.LegacyPaperCommandManager; +import org.incendo.cloud.setting.ManagerSetting; + +import java.util.List; + +public class BukkitCommandManager extends AbstractCommandManager { + + private final BukkitCustomNameplates plugin; + private final Index> INDEX; + + public BukkitCommandManager(BukkitCustomNameplates plugin) { + super(plugin, new LegacyPaperCommandManager<>( + plugin.getBootstrap(), + ExecutionCoordinator.simpleCoordinator(), + SenderMapper.identity() + )); + this.plugin = plugin; + List> FEATURES = List.of( + new ReloadCommand(this, plugin), + new DebugPerformanceCommand(this, plugin), + new DebugWidthCommand(this, plugin), + new DebugLinesCommand(this, plugin), + new NameplatesEquipCommand(this, plugin), + new NameplatesUnEquipCommand(this, plugin), + new NameplatesListCommand(this, plugin), + new NameplatesPreviewCommand(this, plugin), + new NameplatesForceUnEquipCommand(this, plugin), + new NameplatesForcePreviewCommand(this, plugin), + new NameplatesForceEquipCommand(this, plugin), + new BubblesForceUnEquipCommand(this, plugin), + new BubblesListCommand(this, plugin), + new BubblesUnEquipCommand(this, plugin), + new BubblesForceEquipCommand(this, plugin), + new BubblesEquipCommand(this, plugin) + ); + this.INDEX = Index.create(CommandFeature::getFeatureID, FEATURES); + final LegacyPaperCommandManager manager = (LegacyPaperCommandManager) getCommandManager(); + manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true); + if (manager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) { + manager.registerBrigadier(); + manager.brigadierManager().setNativeNumberSuggestions(true); + } else if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + manager.registerAsynchronousCompletions(); + } + } + + @Override + protected Sender wrapSender(CommandSender sender) { + return plugin.getSenderFactory().wrap(sender); + } + + @Override + public Index> getFeatures() { + return INDEX; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesEquipCommand.java new file mode 100644 index 0000000..11f8ccf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesEquipCommand.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public class BubblesEquipCommand extends BukkitCommandFeature { + + public BubblesEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("bubble", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + Player player = (Player) context.sender(); + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) { + return CompletableFuture.completedFuture(Collections.emptySet()); + } + return CompletableFuture.completedFuture(plugin.getBubbleManager().availableBubbles(cnPlayer).stream().map(it -> Suggestion.suggestion(it.id())).toList()); + } + })) + .handler(context -> { + if (!ConfigManager.bubbleModule()) return; + String bubbleId = context.get("bubble"); + BubbleConfig bubble = plugin.getBubbleManager().getBubbleConfig(bubbleId); + if (bubble == null) { + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_EQUIP_FAILURE_NOT_EXISTS, Component.text(bubbleId)); + return; + } + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to equip a bubble when data not loaded"); + return; + } + if (!plugin.getBubbleManager().hasBubble(player, bubbleId)) { + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_EQUIP_FAILURE_PERMISSION); + return; + } + if (player.equippedBubble().equals(bubbleId)) { + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_EQUIP_FAILURE_NO_CHANGE); + return; + } + player.equippedBubble(bubbleId); + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_EQUIP_SUCCESS, Component.text(bubbleId), AdventureHelper.miniMessage(bubble.displayName())); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "bubbles_equip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesForceEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesForceEquipCommand.java new file mode 100644 index 0000000..d7e1dad --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesForceEquipCommand.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.concurrent.CompletableFuture; + +public class BubblesForceEquipCommand extends BukkitCommandFeature { + + public BubblesForceEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .required("bubble", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(plugin.getBubbleManager().getBubbles().stream().map(it -> Suggestion.suggestion(it.id())).toList()); + } + })) + .handler(context -> { + if (!ConfigManager.bubbleModule()) return; + String bubbleId = context.get("bubble"); + Player bukkitPlayer = context.get("player"); + BubbleConfig bubble = plugin.getBubbleManager().getBubbleConfig(bubbleId); + if (bubble == null) { + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_FORCE_EQUIP_FAILURE_NOT_EXISTS, Component.text(bukkitPlayer.getName()), Component.text(bubbleId)); + return; + } + CNPlayer player = plugin.getPlayer(bukkitPlayer.getUniqueId()); + if (player == null) { + return; + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to equip a bubble when data not loaded"); + return; + } + player.equippedBubble(bubbleId); + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_FORCE_EQUIP_SUCCESS, Component.text(bukkitPlayer.getName()), Component.text(bubbleId), AdventureHelper.miniMessage(bubble.displayName())); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "bubbles_force_equip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesForceUnEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesForceUnEquipCommand.java new file mode 100644 index 0000000..84570c3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesForceUnEquipCommand.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; + +public class BubblesForceUnEquipCommand extends BukkitCommandFeature { + + public BubblesForceUnEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .handler(context -> { + if (!ConfigManager.bubbleModule()) return; + Player bukkitPlayer = context.get("player"); + CNPlayer player = plugin.getPlayer(bukkitPlayer.getUniqueId()); + if (player == null) { + return; + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to equip a bubble when data not loaded"); + return; + } + player.equippedBubble("none"); + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_FORCE_UNEQUIP_SUCCESS, Component.text(bukkitPlayer.getName())); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "bubbles_force_unequip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesListCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesListCommand.java new file mode 100644 index 0000000..2a4f662 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesListCommand.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +import java.util.Collection; +import java.util.StringJoiner; + +public class BubblesListCommand extends BukkitCommandFeature { + + public BubblesListCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + if (!ConfigManager.bubbleModule()) return; + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + Collection available = plugin.getBubbleManager().availableBubbles(player); + if (available.isEmpty()) { + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_LIST_FAILURE_NONE); + } else { + StringJoiner sj1 = new StringJoiner(TranslationManager.miniMessageTranslation(MessageConstants.COMMAND_BUBBLES_LIST_DELIMITER.build().key())); + StringJoiner sj2 = new StringJoiner(TranslationManager.miniMessageTranslation(MessageConstants.COMMAND_BUBBLES_LIST_DELIMITER.build().key())); + for (BubbleConfig bubble : available) { + sj1.add(bubble.id()); + sj2.add(bubble.displayName()); + } + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_LIST_SUCCESS, AdventureHelper.miniMessage(sj1.toString()), AdventureHelper.miniMessage(sj2.toString())); + } + }); + } + + @Override + public String getFeatureID() { + return "bubbles_list"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesUnEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesUnEquipCommand.java new file mode 100644 index 0000000..9665f89 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/BubblesUnEquipCommand.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class BubblesUnEquipCommand extends BukkitCommandFeature { + + public BubblesUnEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + if (!ConfigManager.bubbleModule()) return; + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to unequip a bubble when data not loaded"); + return; + } + if (player.equippedBubble().equals("none")) { + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_UNEQUIP_FAILURE_NOT_EQUIP); + return; + } + player.equippedBubble("none"); + handleFeedback(context, MessageConstants.COMMAND_BUBBLES_UNEQUIP_SUCCESS); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "bubbles_unequip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugLinesCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugLinesCommand.java new file mode 100644 index 0000000..ff598c2 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugLinesCommand.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.parser.standard.StringParser; + +public class DebugLinesCommand extends BukkitCommandFeature { + + public DebugLinesCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("width", IntegerParser.integerParser()) + .required("text", StringParser.greedyStringParser()) + .handler(context -> { + String text = context.get("text"); + int width = context.get("width"); + context.sender().sendMessage(String.valueOf(plugin.getAdvanceManager().getLines(text, width))); + }); + } + + @Override + public String getFeatureID() { + return "debug_lines"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugPerformanceCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugPerformanceCommand.java new file mode 100644 index 0000000..e887a6e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugPerformanceCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.MainTask; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class DebugPerformanceCommand extends BukkitCommandFeature { + + public DebugPerformanceCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .handler(context -> { + MainTask.HealthyProfile profile = MainTask.getHealthyProfile(); + handleFeedback(context, MessageConstants.COMMAND_DEBUG_PERFORMANCE, + Component.text(String.format("%.3f", profile.getLoad() * 100)), + Component.text(String.format("%.3f", (double) profile.getTotalTimeNS() / 1_000_000)), + Component.text(String.format("%.3f", (double) profile.getActionBarConditionNS() / 1_000_000)), + Component.text(String.format("%.3f", (double) profile.getRefreshPlaceholderNS() / 1_000_000)) + ); + }); + } + + @Override + public String getFeatureID() { + return "debug_performance"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugWidthCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugWidthCommand.java new file mode 100644 index 0000000..4397306 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/DebugWidthCommand.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.StringParser; + +public class DebugWidthCommand extends BukkitCommandFeature { + + public DebugWidthCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("text", StringParser.greedyStringParser()) + .handler(context -> { + String text = context.get("text"); + context.sender().sendMessage(String.valueOf(plugin.getAdvanceManager().getLineAdvance(text))); + }); + } + + @Override + public String getFeatureID() { + return "debug_width"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesEquipCommand.java new file mode 100644 index 0000000..0323642 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesEquipCommand.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.nameplate.Nameplate; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public class NameplatesEquipCommand extends BukkitCommandFeature { + + public NameplatesEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .required("nameplate", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + Player player = (Player) context.sender(); + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) { + return CompletableFuture.completedFuture(Collections.emptySet()); + } + return CompletableFuture.completedFuture(plugin.getNameplateManager().availableNameplates(cnPlayer).stream().map(it -> Suggestion.suggestion(it.id())).toList()); + } + })) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + String nameplateId = context.get("nameplate"); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(nameplateId); + if (nameplate == null) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_EQUIP_FAILURE_NOT_EXISTS, Component.text(nameplateId)); + return; + } + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to equip a nameplate when data not loaded"); + return; + } + if (!plugin.getNameplateManager().hasNameplate(player, nameplateId)) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_EQUIP_FAILURE_PERMISSION); + return; + } + if (player.equippedNameplate().equals(nameplateId)) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_EQUIP_FAILURE_NO_CHANGE); + return; + } + player.equippedNameplate(nameplateId); + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_EQUIP_SUCCESS, Component.text(nameplateId), AdventureHelper.miniMessage(nameplate.displayName())); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "nameplates_equip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForceEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForceEquipCommand.java new file mode 100644 index 0000000..e268766 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForceEquipCommand.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.nameplate.Nameplate; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.concurrent.CompletableFuture; + +public class NameplatesForceEquipCommand extends BukkitCommandFeature { + + public NameplatesForceEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .required("nameplate", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(plugin.getNameplateManager().getNameplates().stream().map(it -> Suggestion.suggestion(it.id())).toList()); + } + })) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + String nameplateId = context.get("nameplate"); + Player bukkitPlayer = context.get("player"); + Nameplate nameplate = plugin.getNameplateManager().getNameplate(nameplateId); + if (nameplate == null) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_FORCE_EQUIP_FAILURE_NOT_EXISTS, Component.text(bukkitPlayer.getName()), Component.text(nameplateId)); + return; + } + CNPlayer player = plugin.getPlayer(bukkitPlayer.getUniqueId()); + if (player == null) { + return; + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to equip a nameplate when data not loaded"); + return; + } + player.equippedNameplate(nameplateId); + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_FORCE_EQUIP_SUCCESS, Component.text(bukkitPlayer.getName()), Component.text(nameplateId), AdventureHelper.miniMessage(nameplate.displayName())); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "nameplates_force_equip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForcePreviewCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForcePreviewCommand.java new file mode 100644 index 0000000..85ecec7 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForcePreviewCommand.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.JoinQuitListener; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.bukkit.command.misc.PreviewTasks; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public class NameplatesForcePreviewCommand extends BukkitCommandFeature implements JoinQuitListener { + + public NameplatesForcePreviewCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .optional("nameplate", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(plugin.getNameplateManager().getNameplates().stream().map(it -> Suggestion.suggestion(it.id())).toList()); + } + })) + .optional("time", IntegerParser.integerParser(0)) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + Player bukkitPlayer = context.get("player"); + CNPlayer player = plugin.getPlayer(bukkitPlayer.getUniqueId()); + if (player == null || !player.isLoaded()) { + return; + } + + Optional optionalNameplate = context.optional("nameplate"); + boolean specified = optionalNameplate.isPresent(); + + int time = (int) context.optional("time").orElse(plugin.getUnlimitedTagManager().previewDuration()); + + String previousNameplate = player.equippedNameplate(); + if (specified) { + player.equippedNameplate(optionalNameplate.get()); + } + + if (!player.isPreviewing()) + plugin.getUnlimitedTagManager().setPreviewing(player, true); + + SchedulerTask task = plugin.getScheduler().asyncLater(() -> { + if (!player.isOnline()) { + return; + } + + if (!plugin.getUnlimitedTagManager().isAlwaysShow()) { + plugin.getUnlimitedTagManager().setPreviewing(player, false); + } + + if (specified) { + player.equippedNameplate(previousNameplate); + } + }, time, TimeUnit.SECONDS); + + SchedulerTask previous = PreviewTasks.delayedTasks.put(player.uuid(), task); + if (previous != null && !previous.cancelled()) { + previous.cancel(); + } + + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_FORCE_PREVIEW_SUCCESS, Component.text(player.name())); + }); + } + + @Override + public String getFeatureID() { + return "nameplates_force_preview"; + } + + @Override + public void registerRelatedFunctions() { + plugin.registerJoinQuitListener(this); + } + + @Override + public void unregisterRelatedFunctions() { + for (SchedulerTask task : PreviewTasks.delayedTasks.values()) { + if (!task.cancelled()) + task.cancel(); + } + PreviewTasks.delayedTasks.clear(); + plugin.unregisterJoinQuitListener(this); + } + + @Override + public void onPlayerJoin(CNPlayer player) { + SchedulerTask previous = PreviewTasks.delayedTasks.remove(player.uuid()); + if (previous != null && !previous.cancelled()) { + previous.cancel(); + } + } + + @Override + public void onPlayerQuit(CNPlayer player) { + SchedulerTask previous = PreviewTasks.delayedTasks.remove(player.uuid()); + if (previous != null && !previous.cancelled()) { + previous.cancel(); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForceUnEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForceUnEquipCommand.java new file mode 100644 index 0000000..fbaeaa0 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesForceUnEquipCommand.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.parser.PlayerParser; + +public class NameplatesForceUnEquipCommand extends BukkitCommandFeature { + + public NameplatesForceUnEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .required("player", PlayerParser.playerParser()) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + Player bukkitPlayer = context.get("player"); + CNPlayer player = plugin.getPlayer(bukkitPlayer.getUniqueId()); + if (player == null) { + return; + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to equip a nameplate when data not loaded"); + return; + } + player.equippedNameplate("none"); + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_FORCE_UNEQUIP_SUCCESS, Component.text(bukkitPlayer.getName())); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "nameplates_force_unequip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesListCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesListCommand.java new file mode 100644 index 0000000..a0b640d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesListCommand.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.nameplate.Nameplate; +import net.momirealms.customnameplates.api.helper.AdventureHelper; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +import java.util.Collection; +import java.util.StringJoiner; + +public class NameplatesListCommand extends BukkitCommandFeature { + + public NameplatesListCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + Collection available = plugin.getNameplateManager().availableNameplates(player); + if (available.isEmpty()) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_LIST_FAILURE_NONE); + } else { + StringJoiner sj1 = new StringJoiner(TranslationManager.miniMessageTranslation(MessageConstants.COMMAND_NAMEPLATES_LIST_DELIMITER.build().key())); + StringJoiner sj2 = new StringJoiner(TranslationManager.miniMessageTranslation(MessageConstants.COMMAND_NAMEPLATES_LIST_DELIMITER.build().key())); + for (Nameplate nameplate : available) { + sj1.add(nameplate.id()); + sj2.add(nameplate.displayName()); + } + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_LIST_SUCCESS, AdventureHelper.miniMessage(sj1.toString()), AdventureHelper.miniMessage(sj2.toString())); + } + }); + } + + @Override + public String getFeatureID() { + return "nameplates_list"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesPreviewCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesPreviewCommand.java new file mode 100644 index 0000000..d3b4bc8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesPreviewCommand.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.feature.JoinQuitListener; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.bukkit.command.misc.PreviewTasks; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +import java.util.concurrent.TimeUnit; + +public class NameplatesPreviewCommand extends BukkitCommandFeature implements JoinQuitListener { + + public NameplatesPreviewCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + if (plugin.getUnlimitedTagManager().isAlwaysShow()) { + return; + } + if (player.isPreviewing()) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_PREVIEW_FAILURE_COOLDOWN); + return; + } + plugin.getUnlimitedTagManager().setPreviewing(player, true); + + SchedulerTask task = plugin.getScheduler().asyncLater(() -> { + if (!player.isOnline()) { + return; + } + plugin.getUnlimitedTagManager().setPreviewing(player, false); + }, plugin.getUnlimitedTagManager().previewDuration(), TimeUnit.SECONDS); + + PreviewTasks.delayedTasks.put(player.uuid(), task); + + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_PREVIEW_SUCCESS); + }); + } + + @Override + public String getFeatureID() { + return "nameplates_preview"; + } + + @Override + public void registerRelatedFunctions() { + plugin.registerJoinQuitListener(this); + } + + @Override + public void unregisterRelatedFunctions() { + for (SchedulerTask task : PreviewTasks.delayedTasks.values()) { + if (!task.cancelled()) + task.cancel(); + } + PreviewTasks.delayedTasks.clear(); + plugin.unregisterJoinQuitListener(this); + } + + @Override + public void onPlayerJoin(CNPlayer player) { + SchedulerTask previous = PreviewTasks.delayedTasks.remove(player.uuid()); + if (previous != null && !previous.cancelled()) { + previous.cancel(); + } + } + + @Override + public void onPlayerQuit(CNPlayer player) { + SchedulerTask previous = PreviewTasks.delayedTasks.remove(player.uuid()); + if (previous != null && !previous.cancelled()) { + previous.cancel(); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesUnEquipCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesUnEquipCommand.java new file mode 100644 index 0000000..28fa5f0 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/NameplatesUnEquipCommand.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.storage.data.PlayerData; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public class NameplatesUnEquipCommand extends BukkitCommandFeature { + + public NameplatesUnEquipCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .senderType(Player.class) + .handler(context -> { + if (!ConfigManager.nameplateModule()) return; + CNPlayer player = plugin.getPlayer(context.sender().getUniqueId()); + if (player == null) { + throw new RuntimeException("Player should not be null"); + } + if (!player.isLoaded()) { + plugin.getPluginLogger().warn("Player " + player.name() + " tried to unequip a nameplate when data not loaded"); + return; + } + if (player.equippedNameplate().equals("none")) { + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_UNEQUIP_FAILURE_NOT_EQUIP); + return; + } + player.equippedNameplate("none"); + handleFeedback(context, MessageConstants.COMMAND_NAMEPLATES_UNEQUIP_SUCCESS); + + plugin.getStorageManager().getDataSource().updatePlayerData(PlayerData.builder() + .uuid(player.uuid()) + .nameplate(player.equippedNameplate()) + .bubble(player.equippedBubble()) + .build(), plugin.getScheduler().async()); + }); + } + + @Override + public String getFeatureID() { + return "nameplates_unequip"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/ReloadCommand.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/ReloadCommand.java new file mode 100644 index 0000000..d251c33 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/feature/ReloadCommand.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.command.BukkitCommandFeature; +import net.momirealms.customnameplates.common.command.CustomNameplatesCommandManager; +import net.momirealms.customnameplates.common.locale.MessageConstants; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.parser.standard.EnumParser; + +import java.util.Optional; + +public class ReloadCommand extends BukkitCommandFeature { + + public ReloadCommand(CustomNameplatesCommandManager commandManager, BukkitCustomNameplates plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(manager.flagBuilder("silent").withAliases("s")) + .optional("content", EnumParser.enumParser(ReloadArgument.class)) + .handler(context -> { + Optional optional = context.optional("content"); + ReloadArgument argument = ReloadArgument.ALL; + if (optional.isPresent()) { + argument = optional.get(); + } + if (argument == ReloadArgument.ALL || argument == ReloadArgument.CONFIG) { + long time1 = System.currentTimeMillis(); + plugin.reload(); + handleFeedback(context, MessageConstants.COMMAND_RELOAD_SUCCESS, Component.text(System.currentTimeMillis() - time1)); + } + if (argument == ReloadArgument.ALL || argument == ReloadArgument.PACK) { + long time1 = System.currentTimeMillis(); + plugin.getScheduler().async().execute(() -> { + handleFeedback(context, MessageConstants.COMMAND_RELOAD_GENERATING); + plugin.getResourcePackManager().generate(); + handleFeedback(context, MessageConstants.COMMAND_RELOAD_GENERATED, Component.text(System.currentTimeMillis() - time1)); + }); + } + }); + } + + @Override + public String getFeatureID() { + return "reload"; + } + + public enum ReloadArgument { + + ALL, + CONFIG, + PACK + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/misc/PreviewTasks.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/misc/PreviewTasks.java new file mode 100644 index 0000000..1310c7a --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/command/misc/PreviewTasks.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.command.misc; + +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class PreviewTasks { + + public final static Map delayedTasks = Collections.synchronizedMap(new HashMap<>()); +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/BukkitRequirementManager.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/BukkitRequirementManager.java new file mode 100644 index 0000000..2601e19 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/BukkitRequirementManager.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement; + +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirementManager; +import net.momirealms.customnameplates.api.requirement.Requirement; +import net.momirealms.customnameplates.api.util.ConfigUtils; +import net.momirealms.customnameplates.bukkit.requirement.builtin.*; +import net.momirealms.customnameplates.common.util.ListUtils; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashSet; +import java.util.List; + +public class BukkitRequirementManager extends AbstractRequirementManager { + + public BukkitRequirementManager(CustomNameplates plugin) { + super(plugin); + } + + @Override + protected void registerPlatformRequirements() { + this.registerPermission(); + this.registerBiome(); + this.registerTime(); + this.registerY(); + this.registerWorld(); + this.registerWeather(); + this.registerDate(); + this.registerLevel(); + this.registerRandom(); + this.registerCooldown(); + this.registerEnvironment(); + this.registerPotionEffect(); + this.registerGameMode(); + this.registerBedrock(); + this.registerDisguise(); + } + + private void registerDisguise() { + this.registerRequirement((args, interval) -> { + boolean is = (boolean) args; + return new DisguiseRequirement(interval, is); + }, "self-disguised"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new DisguiseTypeRequirement(interval, new HashSet<>(list)); + }, "disguised-type"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new NotDisguiseTypeRequirement(interval, new HashSet<>(list)); + }, "!disguised-type"); + } + + private void registerBedrock() { + this.registerRequirement((args, interval) -> { + boolean is = (boolean) args; + return new BedrockPlayerRequirement(interval, is); + }, "is-bedrock-player"); + this.registerRequirement((args, interval) -> { + boolean is = (boolean) args; + return new GeyserRequirement(interval, is); + }, "geyser"); + this.registerRequirement((args, interval) -> { + boolean is = (boolean) args; + return new FloodGateRequirement(interval, is); + }, "floodgate"); + } + + private void registerPermission() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new PermissionRequirement(interval, list); + }, "permission"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new NotPermissionRequirement(interval, list); + }, "!permission"); + } + + private void registerBiome() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new BiomeRequirement(interval, list); + }, "biome"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new NotBiomeRequirement(interval, list); + }, "!biome"); + } + + private void registerTime() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new TimeRequirement(interval, list); + }, "time"); + } + + private void registerY() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new YRequirement(interval, list); + }, "ypos"); + } + + private void registerWorld() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new WorldRequirement(interval, list); + }, "world"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new NotWorldRequirement(interval, list); + }, "!world"); + } + + private void registerWeather() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new WeatherRequirement(interval, list); + }, "weather"); + } + + private void registerDate() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new DateRequirement(interval, list); + }, "date"); + } + + private void registerLevel() { + this.registerRequirement((args, interval) -> { + int level = (int) args; + return new LevelRequirement(interval, level); + }, "level"); + } + + private void registerRandom() { + this.registerRequirement((args, interval) -> { + double random = (double) args; + return new RandomRequirement(interval, random); + }, "random"); + } + + private void registerCooldown() { + this.registerRequirement((args, interval) -> { + Section section = ConfigUtils.safeCast(args, Section.class); + if (section == null) return Requirement.empty(); + String key = section.getString("key"); + int time = section.getInt("time"); + return new CooldownRequirement(interval, key, time); + }, "cooldown"); + } + + private void registerEnvironment() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new EnvironmentRequirement(interval, list); + }, "environment"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new NotEnvironmentRequirement(interval, list); + }, "!environment"); + } + + private void registerPotionEffect() { + this.registerRequirement((args, interval) -> { + String potions = (String) args; + String[] split = potions.split("(<=|>=|<|>|==)", 2); + PotionEffectType type = PotionEffectType.getByName(split[0]); + if (type == null) { + plugin.getPluginLogger().warn("Potion effect doesn't exist: " + split[0]); + return Requirement.empty(); + } + int required = Integer.parseInt(split[1]); + String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); + return new PotionEffectRequirement(interval, type, operator, required); + }, "potion-effect"); + } + + private void registerGameMode() { + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new GameModeRequirement(interval, list); + }, "gamemode"); + this.registerRequirement((args, interval) -> { + List list = ListUtils.toList(args); + return new NotGameModeRequirement(interval, list); + }, "!gamemode"); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/BedrockPlayerRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/BedrockPlayerRequirement.java new file mode 100644 index 0000000..3d82d57 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/BedrockPlayerRequirement.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.bukkit.BukkitPlatform; +import net.momirealms.customnameplates.bukkit.compatibility.bedrock.FloodGateUtils; +import net.momirealms.customnameplates.bukkit.compatibility.bedrock.GeyserUtils; + +public class BedrockPlayerRequirement extends AbstractRequirement { + + private final boolean is; + + public BedrockPlayerRequirement(int refreshInterval, boolean is) { + super(refreshInterval); + this.is = is; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + BukkitPlatform platform = (BukkitPlatform) CustomNameplates.getInstance().getPlatform(); + if (platform.hasGeyser()) { + if (GeyserUtils.isBedrockPlayer(p1.uuid())) return true; + } + if (platform.hasFloodGate()) { + if (FloodGateUtils.isBedrockPlayer(p1.uuid())) return true; + } + return false; + } + + @Override + public String type() { + return "is-bedrock-player"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BedrockPlayerRequirement that = (BedrockPlayerRequirement) o; + return is == that.is; + } + + @Override + public int hashCode() { + return is ? 37 : 19; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/BiomeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/BiomeRequirement.java new file mode 100644 index 0000000..b163382 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/BiomeRequirement.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class BiomeRequirement extends AbstractRequirement { + + private final Set biomes; + + public BiomeRequirement(int refreshInterval, List biomes) { + super(refreshInterval); + this.biomes = new HashSet<>(biomes); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String currentBiome = ((Player) p1.player()).getLocation().getBlock().getBiome().toString(); + return biomes.contains(currentBiome); + } + + @Override + public String type() { + return "biome"; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BiomeRequirement that = (BiomeRequirement) o; + return biomes.equals(that.biomes); + } + + @Override + public int hashCode() { + return Objects.hashCode(biomes); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/CooldownRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/CooldownRequirement.java new file mode 100644 index 0000000..7f7fbd7 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/CooldownRequirement.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +import java.util.Objects; + +public class CooldownRequirement extends AbstractRequirement { + + private final String key; + private final int time; + + public CooldownRequirement(int refreshInterval, String key, int time) { + super(refreshInterval); + this.key = key; + this.time = time; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + return false; + } + + @Override + public String type() { + return "cooldown"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CooldownRequirement that = (CooldownRequirement) o; + return key.equals(that.key); + } + + @Override + public int hashCode() { + return Objects.hashCode(key); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DateRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DateRequirement.java new file mode 100644 index 0000000..6d741d8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DateRequirement.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +import java.util.*; + +public class DateRequirement extends AbstractRequirement { + + private final Set dates; + + public DateRequirement(int refreshInterval, List dates) { + super(refreshInterval); + this.dates = new HashSet<>(dates); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + Calendar calendar = Calendar.getInstance(); + String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); + return dates.contains(current); + } + + @Override + public String type() { + return "date"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DateRequirement that = (DateRequirement) o; + return dates.equals(that.dates); + } + + @Override + public int hashCode() { + return Objects.hashCode(dates); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DisguiseRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DisguiseRequirement.java new file mode 100644 index 0000000..d364378 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DisguiseRequirement.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.bukkit.BukkitPlatform; +import net.momirealms.customnameplates.bukkit.compatibility.disguise.DisguiseUtils; +import org.bukkit.entity.Player; + +import java.util.Objects; + +public class DisguiseRequirement extends AbstractRequirement { + + private final boolean is; + + public DisguiseRequirement(int refreshInterval, boolean is) { + super(refreshInterval); + this.is = is; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + BukkitPlatform platform = (BukkitPlatform) CustomNameplates.getInstance().getPlatform(); + if (!platform.hasLibsDisguises()) return true; + if (is) { + return DisguiseUtils.isDisguised((Player) p1.player()); + } else { + return !DisguiseUtils.isDisguised((Player) p1.player()); + } + } + + @Override + public String type() { + return "self-disguised"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + DisguiseRequirement that = (DisguiseRequirement) object; + return is == that.is; + } + + @Override + public int hashCode() { + return is ? 71 : 17; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DisguiseTypeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DisguiseTypeRequirement.java new file mode 100644 index 0000000..c354560 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/DisguiseTypeRequirement.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.bukkit.BukkitPlatform; +import net.momirealms.customnameplates.bukkit.compatibility.disguise.DisguiseUtils; +import org.bukkit.entity.Player; + +import java.util.Objects; +import java.util.Set; + +public class DisguiseTypeRequirement extends AbstractRequirement { + + private final Set types; + + public DisguiseTypeRequirement(int refreshInterval, Set types) { + super(refreshInterval); + this.types = types; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + BukkitPlatform platform = (BukkitPlatform) CustomNameplates.getInstance().getPlatform(); + if (!platform.hasLibsDisguises()) return true; + if (!DisguiseUtils.isDisguised((Player) p1.player())) return false; + return types.contains(DisguiseUtils.getDisguisedType((Player) p1.player()).name()); + } + + @Override + public String type() { + return "disguised-type"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + DisguiseTypeRequirement that = (DisguiseTypeRequirement) object; + return Objects.equals(types, that.types); + } + + @Override + public int hashCode() { + return Objects.hashCode(types); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/EnvironmentRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/EnvironmentRequirement.java new file mode 100644 index 0000000..43e02bd --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/EnvironmentRequirement.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.*; + +public class EnvironmentRequirement extends AbstractRequirement { + + private final Set environments; + + public EnvironmentRequirement(int refreshInterval, List list) { + super(refreshInterval); + this.environments = new HashSet<>(list); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + var name = ((Player) p1.player()).getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + return environments.contains(name); + } + + @Override + public String type() { + return "environment"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EnvironmentRequirement that = (EnvironmentRequirement) o; + return environments.equals(that.environments); + } + + @Override + public int hashCode() { + return Objects.hashCode(environments); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/FloodGateRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/FloodGateRequirement.java new file mode 100644 index 0000000..a19d4cb --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/FloodGateRequirement.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.bukkit.BukkitPlatform; +import net.momirealms.customnameplates.bukkit.compatibility.bedrock.FloodGateUtils; + +public class FloodGateRequirement extends AbstractRequirement { + + private final boolean is; + + public FloodGateRequirement(int refreshInterval, boolean is) { + super(refreshInterval); + this.is = is; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + BukkitPlatform platform = (BukkitPlatform) CustomNameplates.getInstance().getPlatform(); + if (!platform.hasFloodGate()) return true; + if (is) { + return FloodGateUtils.isBedrockPlayer(p1.uuid()); + } else { + return !FloodGateUtils.isBedrockPlayer(p1.uuid()); + } + } + + @Override + public String type() { + return "floodgate"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FloodGateRequirement that = (FloodGateRequirement) o; + return is == that.is; + } + + @Override + public int hashCode() { + return is ? 17 : 3; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/GameModeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/GameModeRequirement.java new file mode 100644 index 0000000..d5b0874 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/GameModeRequirement.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class GameModeRequirement extends AbstractRequirement { + + private final List modes; + + public GameModeRequirement(int refreshInterval, List list) { + super(refreshInterval); + this.modes = list; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + var name = ((Player) p1.player()).getGameMode().name().toLowerCase(Locale.ENGLISH); + return modes.contains(name); + } + + @Override + public String type() { + return "gamemode"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GameModeRequirement that = (GameModeRequirement) o; + return modes.equals(that.modes); + } + + @Override + public int hashCode() { + return Objects.hashCode(modes); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/GeyserRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/GeyserRequirement.java new file mode 100644 index 0000000..a2d40b7 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/GeyserRequirement.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.bukkit.BukkitPlatform; +import net.momirealms.customnameplates.bukkit.compatibility.bedrock.GeyserUtils; + +public class GeyserRequirement extends AbstractRequirement { + + private final boolean is; + + public GeyserRequirement(int refreshInterval, boolean is) { + super(refreshInterval); + this.is = is; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + BukkitPlatform platform = (BukkitPlatform) CustomNameplates.getInstance().getPlatform(); + if (!platform.hasGeyser()) return true; + if (is) { + return GeyserUtils.isBedrockPlayer(p1.uuid()); + } else { + return !GeyserUtils.isBedrockPlayer(p1.uuid()); + } + } + + @Override + public String type() { + return "geyser"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GeyserRequirement that = (GeyserRequirement) o; + return is == that.is; + } + + @Override + public int hashCode() { + return is ? 17 : 3; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/LevelRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/LevelRequirement.java new file mode 100644 index 0000000..62f4006 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/LevelRequirement.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +public class LevelRequirement extends AbstractRequirement { + + private final int level; + + public LevelRequirement(int refreshInterval, int level) { + super(refreshInterval); + this.level = level; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + return ((Player) p1.player()).getLevel() >= level; + } + + @Override + public String type() { + return "level"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LevelRequirement that = (LevelRequirement) o; + return level == that.level; + } + + @Override + public int hashCode() { + return Integer.hashCode(level); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotBiomeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotBiomeRequirement.java new file mode 100644 index 0000000..c49d610 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotBiomeRequirement.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class NotBiomeRequirement extends AbstractRequirement { + + private final Set biomes; + + public NotBiomeRequirement(int refreshInterval, List biomes) { + super(refreshInterval); + this.biomes = new HashSet<>(biomes); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String currentBiome = ((Player) p1.player()).getLocation().getBlock().getBiome().toString(); + return !biomes.contains(currentBiome); + } + + @Override + public String type() { + return "!biome"; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NotBiomeRequirement that = (NotBiomeRequirement) o; + return biomes.equals(that.biomes); + } + + @Override + public int hashCode() { + return Objects.hashCode(biomes); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotDisguiseTypeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotDisguiseTypeRequirement.java new file mode 100644 index 0000000..207b4bc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotDisguiseTypeRequirement.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.bukkit.BukkitPlatform; +import net.momirealms.customnameplates.bukkit.compatibility.disguise.DisguiseUtils; +import org.bukkit.entity.Player; + +import java.util.Objects; +import java.util.Set; + +public class NotDisguiseTypeRequirement extends AbstractRequirement { + + private final Set types; + + public NotDisguiseTypeRequirement(int refreshInterval, Set types) { + super(refreshInterval); + this.types = types; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + BukkitPlatform platform = (BukkitPlatform) CustomNameplates.getInstance().getPlatform(); + if (!platform.hasLibsDisguises()) return true; + if (!DisguiseUtils.isDisguised((Player) p1.player())) return true; + return !types.contains(DisguiseUtils.getDisguisedType((Player) p1.player()).name()); + } + + @Override + public String type() { + return "!disguised-type"; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + NotDisguiseTypeRequirement that = (NotDisguiseTypeRequirement) object; + return Objects.equals(types, that.types); + } + + @Override + public int hashCode() { + return Objects.hashCode(types); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotEnvironmentRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotEnvironmentRequirement.java new file mode 100644 index 0000000..e3fef53 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotEnvironmentRequirement.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.*; + +public class NotEnvironmentRequirement extends AbstractRequirement { + + private final Set environments; + + public NotEnvironmentRequirement(int refreshInterval, List list) { + super(refreshInterval); + this.environments = new HashSet<>(list); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + var name = ((Player) p1.player()).getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + return !environments.contains(name); + } + + @Override + public String type() { + return "!environment"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NotEnvironmentRequirement that = (NotEnvironmentRequirement) o; + return environments.equals(that.environments); + } + + @Override + public int hashCode() { + return Objects.hashCode(environments); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotGameModeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotGameModeRequirement.java new file mode 100644 index 0000000..ef0d81e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotGameModeRequirement.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class NotGameModeRequirement extends AbstractRequirement { + + private final List modes; + + public NotGameModeRequirement(int refreshInterval, List list) { + super(refreshInterval); + this.modes = list; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + var name = ((Player) p1.player()).getGameMode().name().toLowerCase(Locale.ENGLISH); + return !modes.contains(name); + } + + @Override + public String type() { + return "!gamemode"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NotGameModeRequirement that = (NotGameModeRequirement) o; + return modes.equals(that.modes); + } + + @Override + public int hashCode() { + return Objects.hashCode(modes); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotPermissionRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotPermissionRequirement.java new file mode 100644 index 0000000..2e84be5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotPermissionRequirement.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class NotPermissionRequirement extends AbstractRequirement { + + private final Set permissions; + + public NotPermissionRequirement(int refreshInterval, List permissions) { + super(refreshInterval); + this.permissions = new HashSet<>(permissions); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + for (String perm : permissions) { + if (((Player) p1.player()).hasPermission(perm)) { + return false; + } + } + return true; + } + + @Override + public String type() { + return "!permission"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NotPermissionRequirement that = (NotPermissionRequirement) o; + return permissions.equals(that.permissions); + } + + @Override + public int hashCode() { + return Objects.hashCode(permissions); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotWorldRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotWorldRequirement.java new file mode 100644 index 0000000..2fedd2b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/NotWorldRequirement.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class NotWorldRequirement extends AbstractRequirement { + + private final Set worlds; + + public NotWorldRequirement(int refreshInterval, List worlds) { + super(refreshInterval); + this.worlds = new HashSet<>(worlds); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String currentWorld = ((Player) p1.player()).getWorld().getName(); + return !worlds.contains(currentWorld); + } + + @Override + public String type() { + return "!world"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NotWorldRequirement that = (NotWorldRequirement) o; + return worlds.equals(that.worlds); + } + + @Override + public int hashCode() { + return Objects.hashCode(worlds); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/PermissionRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/PermissionRequirement.java new file mode 100644 index 0000000..862497d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/PermissionRequirement.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class PermissionRequirement extends AbstractRequirement { + + private final Set permissions; + + public PermissionRequirement(int refreshInterval, List permissions) { + super(refreshInterval); + this.permissions = new HashSet<>(permissions); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + for (String perm : permissions) { + if (((Player) p1.player()).hasPermission(perm)) { + return true; + } + } + return false; + } + + @Override + public String type() { + return "permission"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PermissionRequirement that = (PermissionRequirement) o; + return permissions.equals(that.permissions); + } + + @Override + public int hashCode() { + return Objects.hashCode(permissions); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/PotionEffectRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/PotionEffectRequirement.java new file mode 100644 index 0000000..48b6652 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/PotionEffectRequirement.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class PotionEffectRequirement extends AbstractRequirement { + + private final PotionEffectType type; + private final String operator; + private final int required; + + public PotionEffectRequirement(int refreshInterval, PotionEffectType type, String operator, int required) { + super(refreshInterval); + this.type = type; + this.operator = operator; + this.required = required; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + int level = -1; + PotionEffect potionEffect = ((Player) p1.player()).getPotionEffect(type); + if (potionEffect != null) { + level = potionEffect.getAmplifier(); + } + boolean result = false; + switch (operator) { + case ">=" -> { + if (level >= required) result = true; + } + case ">" -> { + if (level > required) result = true; + } + case "==" -> { + if (level == required) result = true; + } + case "!=" -> { + if (level != required) result = true; + } + case "<=" -> { + if (level <= required) result = true; + } + case "<" -> { + if (level < required) result = true; + } + } + return result; + } + + @Override + public String type() { + return "potion-effect"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PotionEffectRequirement that = (PotionEffectRequirement) o; + return type.equals(that.type) && operator.equals(that.operator) && required == that.required; + } + + @Override + public int hashCode() { + int hash = 3; + hash += type.hashCode() * 7; + hash += operator.hashCode() * 13; + hash += required * 19; + return hash; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/RandomRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/RandomRequirement.java new file mode 100644 index 0000000..8c8627f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/RandomRequirement.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; + +public class RandomRequirement extends AbstractRequirement { + + private final double random; + + public RandomRequirement(int refreshInterval, double random) { + super(refreshInterval); + this.random = random; + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + return Math.random() < random; + } + + @Override + public String type() { + return "random"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RandomRequirement that = (RandomRequirement) o; + return random == that.random; + } + + @Override + public int hashCode() { + return Double.hashCode(random); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/TimeRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/TimeRequirement.java new file mode 100644 index 0000000..c437380 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/TimeRequirement.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Objects; + +public class TimeRequirement extends AbstractRequirement { + + private final List> timePair; + + public TimeRequirement(int refreshInterval, List list) { + super(refreshInterval); + this.timePair = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + long time = ((Player) p1.player()).getWorld().getTime(); + for (Pair pair : timePair) + if (time >= pair.left() && time <= pair.right()) + return true; + return false; + } + + @Override + public String type() { + return "time"; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeRequirement that = (TimeRequirement) o; + return timePair.equals(that.timePair); + } + + @Override + public int hashCode() { + return Objects.hashCode(timePair); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/WeatherRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/WeatherRequirement.java new file mode 100644 index 0000000..f34c930 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/WeatherRequirement.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class WeatherRequirement extends AbstractRequirement { + + private final Set weathers; + + public WeatherRequirement(int refreshInterval, List weathers) { + super(refreshInterval); + this.weathers = new HashSet<>(weathers); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String currentWeather; + World world = ((Player) p1.player()).getWorld(); + if (world.isClearWeather()) currentWeather = "clear"; + else if (world.isThundering()) currentWeather = "thunder"; + else currentWeather = "rain"; + return weathers.contains(currentWeather); + } + + @Override + public String type() { + return "weather"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WeatherRequirement that = (WeatherRequirement) o; + return weathers.equals(that.weathers); + } + + @Override + public int hashCode() { + return Objects.hashCode(weathers); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/WorldRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/WorldRequirement.java new file mode 100644 index 0000000..fd620bc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/WorldRequirement.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class WorldRequirement extends AbstractRequirement { + + private final Set worlds; + + public WorldRequirement(int refreshInterval, List worlds) { + super(refreshInterval); + this.worlds = new HashSet<>(worlds); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + String currentWorld = ((Player) p1.player()).getWorld().getName(); + return worlds.contains(currentWorld); + } + + @Override + public String type() { + return "world"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WorldRequirement that = (WorldRequirement) o; + return worlds.equals(that.worlds); + } + + @Override + public int hashCode() { + return Objects.hashCode(worlds); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/YRequirement.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/YRequirement.java new file mode 100644 index 0000000..edbfeb9 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/requirement/builtin/YRequirement.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.requirement.builtin; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.requirement.AbstractRequirement; +import net.momirealms.customnameplates.common.util.Pair; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Objects; + +public class YRequirement extends AbstractRequirement { + + private final List> yPair; + + public YRequirement(int refreshInterval, List list) { + super(refreshInterval); + this.yPair = list.stream().map(line -> { + String[] split = line.split("~"); + return new Pair<>(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + }).toList(); + } + + @Override + public boolean isSatisfied(CNPlayer p1, CNPlayer p2) { + double y = ((Player) p1.player()).getY(); + for (Pair pair : yPair) + if (y >= pair.left() && y <= pair.right()) + return true; + return false; + } + + @Override + public String type() { + return "ypos"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + YRequirement that = (YRequirement) o; + return yPair.equals(that.yPair); + } + + @Override + public int hashCode() { + return Objects.hashCode(yPair); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/BukkitSchedulerAdapter.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/BukkitSchedulerAdapter.java new file mode 100644 index 0000000..2a6fea3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/BukkitSchedulerAdapter.java @@ -0,0 +1,53 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.bukkit.scheduler; + +import net.momirealms.customnameplates.api.helper.VersionHelper; +import net.momirealms.customnameplates.bukkit.BukkitCustomNameplates; +import net.momirealms.customnameplates.bukkit.scheduler.impl.BukkitExecutor; +import net.momirealms.customnameplates.bukkit.scheduler.impl.FoliaExecutor; +import net.momirealms.customnameplates.common.plugin.scheduler.AbstractJavaScheduler; +import net.momirealms.customnameplates.common.plugin.scheduler.RegionExecutor; +import org.bukkit.Location; + +public class BukkitSchedulerAdapter extends AbstractJavaScheduler { + + protected RegionExecutor sync; + + public BukkitSchedulerAdapter(BukkitCustomNameplates plugin) { + super(plugin); + if (VersionHelper.isFolia()) { + this.sync = new FoliaExecutor(plugin.getBootstrap()); + } else { + this.sync = new BukkitExecutor(plugin.getBootstrap()); + } + } + + @Override + public RegionExecutor sync() { + return this.sync; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/BukkitExecutor.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/BukkitExecutor.java new file mode 100644 index 0000000..ae56c76 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/BukkitExecutor.java @@ -0,0 +1,69 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.bukkit.scheduler.impl; + +import net.momirealms.customnameplates.common.plugin.scheduler.DummyTask; +import net.momirealms.customnameplates.common.plugin.scheduler.RegionExecutor; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +public class BukkitExecutor implements RegionExecutor { + + private final Plugin plugin; + + public BukkitExecutor(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run(Runnable r, Location l) { + if (Bukkit.isPrimaryThread()) { + r.run(); + return; + } + Bukkit.getScheduler().runTask(plugin, r); + } + + @Override + public SchedulerTask runLater(Runnable r, long delayTicks, Location l) { + if (delayTicks == 0) { + if (Bukkit.isPrimaryThread()) { + r.run(); + return new DummyTask(); + } else { + return new BukkitTask(Bukkit.getScheduler().runTask(plugin, r)); + } + } + return new BukkitTask(Bukkit.getScheduler().runTaskLater(plugin, r, delayTicks)); + } + + @Override + public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) { + return new BukkitTask(Bukkit.getScheduler().runTaskTimer(plugin, r, delayTicks, period)); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/BukkitTask.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/BukkitTask.java new file mode 100644 index 0000000..588efd6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/BukkitTask.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.scheduler.impl; + +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; + +public class BukkitTask implements SchedulerTask { + + private final org.bukkit.scheduler.BukkitTask bukkitTask; + + public BukkitTask(org.bukkit.scheduler.BukkitTask bukkitTask) { + this.bukkitTask = bukkitTask; + } + + @Override + public void cancel() { + this.bukkitTask.cancel(); + } + + @Override + public boolean cancelled() { + return bukkitTask.isCancelled(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/FoliaExecutor.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/FoliaExecutor.java new file mode 100644 index 0000000..93bf7a2 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/FoliaExecutor.java @@ -0,0 +1,74 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.bukkit.scheduler.impl; + +import net.momirealms.customnameplates.common.plugin.scheduler.RegionExecutor; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +import java.util.Optional; + +public class FoliaExecutor implements RegionExecutor { + + private final Plugin plugin; + + public FoliaExecutor(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run(Runnable r, Location l) { + Optional.ofNullable(l).ifPresentOrElse(loc -> Bukkit.getRegionScheduler().execute(plugin, loc, r), () -> Bukkit.getGlobalRegionScheduler().execute(plugin, r)); + } + + @Override + public SchedulerTask runLater(Runnable r, long delayTicks, Location l) { + if (l == null) { + if (delayTicks == 0) { + return new FoliaTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> r.run(), delayTicks)); + } else { + return new FoliaTask(Bukkit.getGlobalRegionScheduler().run(plugin, scheduledTask -> r.run())); + } + } else { + if (delayTicks == 0) { + return new FoliaTask(Bukkit.getRegionScheduler().run(plugin, l, scheduledTask -> r.run())); + } else { + return new FoliaTask(Bukkit.getRegionScheduler().runDelayed(plugin, l, scheduledTask -> r.run(), delayTicks)); + } + } + } + + @Override + public SchedulerTask runRepeating(Runnable r, long delayTicks, long period, Location l) { + if (l == null) { + return new FoliaTask(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, scheduledTask -> r.run(), delayTicks, period)); + } else { + return new FoliaTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, l, scheduledTask -> r.run(), delayTicks, period)); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/FoliaTask.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/FoliaTask.java new file mode 100644 index 0000000..4fad55b --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/scheduler/impl/FoliaTask.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.scheduler.impl; + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask; + +public class FoliaTask implements SchedulerTask { + + private final ScheduledTask task; + + public FoliaTask(ScheduledTask task) { + this.task = task; + } + + @Override + public void cancel() { + this.task.cancel(); + } + + @Override + public boolean cancelled() { + return task.isCancelled(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/BukkitReflectionUtils.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/BukkitReflectionUtils.java new file mode 100644 index 0000000..a75720d --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/BukkitReflectionUtils.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.util; + +import net.momirealms.customnameplates.common.util.ReflectionUtils; +import org.bukkit.Bukkit; + +import java.lang.reflect.Method; +import java.util.Objects; + +public final class BukkitReflectionUtils { + + private static final String PREFIX_MC = "net.minecraft."; + private static final String PREFIX_CRAFTBUKKIT = "org.bukkit.craftbukkit"; + private static final String CRAFT_SERVER = "CraftServer"; + private static final String CB_PKG_VERSION; + public static final int MAJOR_REVISION; + + private BukkitReflectionUtils() { + } + + static { + final Class serverClass; + if (Bukkit.getServer() == null) { + // Paper plugin Bootstrapper 1.20.6+ + serverClass = Objects.requireNonNull(ReflectionUtils.getClazz("org.bukkit.craftbukkit.CraftServer")); + } else { + serverClass = Bukkit.getServer().getClass(); + } + final String pkg = serverClass.getPackage().getName(); + final String nmsVersion = pkg.substring(pkg.lastIndexOf(".") + 1); + if (!nmsVersion.contains("_")) { + int fallbackVersion = -1; + if (Bukkit.getServer() != null) { + try { + final Method getMinecraftVersion = serverClass.getDeclaredMethod("getMinecraftVersion"); + fallbackVersion = Integer.parseInt(getMinecraftVersion.invoke(Bukkit.getServer()).toString().split("\\.")[1]); + } catch (final Exception ignored) { + } + } else { + // Paper plugin bootstrapper 1.20.6+ + try { + final Class sharedConstants = Objects.requireNonNull(ReflectionUtils.getClazz("net.minecraft.SharedConstants")); + final Method getCurrentVersion = sharedConstants.getDeclaredMethod("getCurrentVersion"); + final Object currentVersion = getCurrentVersion.invoke(null); + final Method getName = currentVersion.getClass().getDeclaredMethod("getName"); + final String versionName = (String) getName.invoke(currentVersion); + try { + fallbackVersion = Integer.parseInt(versionName.split("\\.")[1]); + } catch (final Exception ignored) { + } + } catch (final ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + MAJOR_REVISION = fallbackVersion; + } else { + MAJOR_REVISION = Integer.parseInt(nmsVersion.split("_")[1]); + } + String name = serverClass.getName(); + name = name.substring(PREFIX_CRAFTBUKKIT.length()); + name = name.substring(0, name.length() - CRAFT_SERVER.length()); + CB_PKG_VERSION = name; + } + + public static String assembleCBClass(String className) { + return PREFIX_CRAFTBUKKIT + CB_PKG_VERSION + className; + } + + public static String assembleMCClass(String className) { + return PREFIX_MC + className; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/EntityData.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/EntityData.java new file mode 100644 index 0000000..dc648ca --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/EntityData.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.util; + +import net.momirealms.customnameplates.api.helper.VersionHelper; + +import java.util.List; + +public class EntityData { + + private final int id; + private final Object serializer; + private final T defaultValue; + + // Entity + public static final EntityData EntityMasks = of(0, EntityDataValue.Serializers$BYTE, (byte) 0); + + // Display only + public static final EntityData InterpolationDelay = of(8, EntityDataValue.Serializers$INT, 0); + + // 1.19.4-1.20.1 + public static final EntityData InterpolationDuration = of(9, EntityDataValue.Serializers$INT, 0); + + // 1.20.2+ + public static final EntityData TransformationInterpolationDuration = of(9, EntityDataValue.Serializers$INT, 0); + public static final EntityData PositionRotationInterpolationDuration = of(10, EntityDataValue.Serializers$INT, 0); + + public static final EntityData Translation = of(11, EntityDataValue.Serializers$VECTOR3, Reflections.instance$Vector3f$None); + public static final EntityData Scale = of(12, EntityDataValue.Serializers$VECTOR3, Reflections.instance$Vector3f$None); + public static final EntityData RotationLeft = of(13, EntityDataValue.Serializers$QUATERNION, Reflections.instance$Quaternionf$None); + public static final EntityData RotationRight = of(14, EntityDataValue.Serializers$QUATERNION, Reflections.instance$Quaternionf$None); + public static final EntityData BillboardConstraints = of(15, EntityDataValue.Serializers$BYTE, (byte) 0); + public static final EntityData BrightnessOverride = of(16, EntityDataValue.Serializers$INT, -1); + public static final EntityData ViewRange = of(17, EntityDataValue.Serializers$FLOAT, 1f); + public static final EntityData ShadowRadius = of(18, EntityDataValue.Serializers$FLOAT, 0f); + public static final EntityData ShadowStrength = of(19, EntityDataValue.Serializers$FLOAT, 0f); + public static final EntityData Width = of(20, EntityDataValue.Serializers$FLOAT, 0f); + public static final EntityData Height = of(21, EntityDataValue.Serializers$FLOAT, 0f); + public static final EntityData GlowColorOverride = of(22, EntityDataValue.Serializers$INT, -1); + + // Text display only + public static final EntityData Text = of(23, EntityDataValue.Serializers$COMPONENT, Reflections.instance$Component$empty); + public static final EntityData LineWidth = of(24, EntityDataValue.Serializers$INT, 200); + public static final EntityData BackgroundColor = of(25, EntityDataValue.Serializers$INT, 0x40000000); + public static final EntityData TextOpacity = of(26, EntityDataValue.Serializers$BYTE, (byte) -1); + public static final EntityData TextDisplayMasks = of(27, EntityDataValue.Serializers$BYTE, (byte) 0); + + public static EntityData of(final int id, final Object serializer, T defaultValue) { + return new EntityData<>(id, serializer, defaultValue); + } + + public EntityData(int id, Object serializer, T defaultValue) { + if (!VersionHelper.isVersionNewerThan1_20_2()) { + if (id >= 11) { + id--; + } + } + this.id = id; + this.serializer = serializer; + this.defaultValue = defaultValue; + } + + public Object serializer() { + return serializer; + } + + public int id() { + return id; + } + + public T defaultValue() { + return defaultValue; + } + + public Object createEntityDataIfNotDefaultValue(T value) { + if (defaultValue().equals(value)) return null; + return EntityDataValue.create(id, serializer, value); + } + + public void addEntityDataIfNotDefaultValue(T value, List list) { + if (defaultValue().equals(value)) return; + list.add(EntityDataValue.create(id, serializer, value)); + } + + public void addEntityData(T value, List list) { + list.add(EntityDataValue.create(id, serializer, value)); + } + + private static final int HAS_SHADOW = 0x01; // 1 + private static final int IS_SEE_THROUGH = 0x02; // 2 + private static final int USE_DEFAULT_BACKGROUND = 0x04; // 4 + private static final int LEFT_ALIGNMENT = 0x08; // 8 + private static final int RIGHT_ALIGNMENT = 0x10; // 16 + + public static byte encodeMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, int alignment) { + int bitMask = 0; + + if (hasShadow) { + bitMask |= HAS_SHADOW; + } + if (isSeeThrough) { + bitMask |= IS_SEE_THROUGH; + } + if (useDefaultBackground) { + bitMask |= USE_DEFAULT_BACKGROUND; + } + + switch (alignment) { + case 0: // CENTER + break; + case 1: // LEFT + bitMask |= LEFT_ALIGNMENT; + break; + case 2: // RIGHT + bitMask |= RIGHT_ALIGNMENT; + break; + default: + throw new IllegalArgumentException("Invalid alignment value"); + } + + return (byte) bitMask; + } + + private static final int IS_ON_FIRE = 0x01; // 1 + private static final int IS_CROUCHING = 0x02; // 2 + private static final int UNUSED = 0x04; // 4 + private static final int IS_SPRINTING = 0x08; // 8 + private static final int IS_SWIMMING = 0x10; // 16 + private static final int IS_INVISIBLE = 0x20; // 32 + private static final int HAS_GLOWING_EFFECT = 0x40; // 64 + private static final int IS_FLYING_WITH_ELYTRA = 0x80; // 128 + + public static byte encodeMask(boolean isOnFire, boolean isCrouching, boolean isUnused, + boolean isSprinting, boolean isSwimming, boolean isInvisible, + boolean hasGlowingEffect, boolean isFlyingWithElytra) { + int bitMask = 0; + + if (isOnFire) { + bitMask |= IS_ON_FIRE; + } + if (isCrouching) { + bitMask |= IS_CROUCHING; + } + if (isUnused) { + bitMask |= UNUSED; + } + if (isSprinting) { + bitMask |= IS_SPRINTING; + } + if (isSwimming) { + bitMask |= IS_SWIMMING; + } + if (isInvisible) { + bitMask |= IS_INVISIBLE; + } + if (hasGlowingEffect) { + bitMask |= HAS_GLOWING_EFFECT; + } + if (isFlyingWithElytra) { + bitMask |= IS_FLYING_WITH_ELYTRA; + } + + return (byte) bitMask; + } + + public static boolean isCrouching(byte mask) { + return (mask & IS_CROUCHING) != 0; + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/EntityDataValue.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/EntityDataValue.java new file mode 100644 index 0000000..5db99f6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/EntityDataValue.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.util; + +import net.momirealms.customnameplates.api.helper.VersionHelper; +import net.momirealms.customnameplates.common.util.ReflectionUtils; + +public class EntityDataValue { + + private static int internalID = 0; + + private static final String[] fieldsObf = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + }; + + public static final Object Serializers$BYTE; + public static final Object Serializers$INT; + public static final Object Serializers$LONG; + public static final Object Serializers$FLOAT; + public static final Object Serializers$STRING; + public static final Object Serializers$COMPONENT; + public static final Object Serializers$OPTIONAL_COMPONENT; + public static final Object Serializers$ITEM_STACK; + public static final Object Serializers$BLOCK_STATE; + public static final Object Serializers$OPTIONAL_BLOCK_STATE; + public static final Object Serializers$BOOLEAN; + public static final Object Serializers$PARTICLE; + public static final Object Serializers$PARTICLES; + public static final Object Serializers$ROTATIONS; + public static final Object Serializers$BLOCK_POS; + public static final Object Serializers$OPTIONAL_BLOCK_POS; + public static final Object Serializers$DIRECTION; + public static final Object Serializers$OPTIONAL_UUID; + public static final Object Serializers$OPTIONAL_GLOBAL_POS; + public static final Object Serializers$COMPOUND_TAG; + public static final Object Serializers$VILLAGER_DATA; + public static final Object Serializers$OPTIONAL_UNSIGNED_INT; + public static final Object Serializers$POSE; + public static final Object Serializers$CAT_VARIANT; + public static final Object Serializers$WOLF_VARIANT; + public static final Object Serializers$FROG_VARIANT; + public static final Object Serializers$PAINTING_VARIANT; + public static final Object Serializers$ARMADILLO_STATE; + public static final Object Serializers$SNIFFER_STATE; + public static final Object Serializers$VECTOR3; + public static final Object Serializers$QUATERNION; + + static { + try { + Serializers$BYTE = initSerializersByName("BYTE"); + Serializers$INT = initSerializersByName("INT"); + Serializers$LONG = initSerializersByName("LONG"); + Serializers$FLOAT = initSerializersByName("FLOAT"); + Serializers$STRING = initSerializersByName("STRING"); + Serializers$COMPONENT = initSerializersByName("COMPONENT"); + Serializers$OPTIONAL_COMPONENT = initSerializersByName("OPTIONAL_COMPONENT"); + Serializers$ITEM_STACK = initSerializersByName("ITEM_STACK"); + Serializers$BLOCK_STATE = initSerializersByName("BLOCK_STATE"); + Serializers$OPTIONAL_BLOCK_STATE = initSerializersByName("OPTIONAL_BLOCK_STATE"); + Serializers$BOOLEAN = initSerializersByName("BOOLEAN"); + Serializers$PARTICLE = initSerializersByName("PARTICLE"); + if (VersionHelper.isVersionNewerThan1_20_5()) Serializers$PARTICLES = initSerializersByName("PARTICLES"); + else Serializers$PARTICLES = null; + Serializers$ROTATIONS = initSerializersByName("ROTATIONS"); + Serializers$BLOCK_POS = initSerializersByName("BLOCK_POS"); + Serializers$OPTIONAL_BLOCK_POS = initSerializersByName("OPTIONAL_BLOCK_POS"); + Serializers$DIRECTION = initSerializersByName("DIRECTION"); + Serializers$OPTIONAL_UUID = initSerializersByName("OPTIONAL_UUID"); + Serializers$OPTIONAL_GLOBAL_POS = initSerializersByName("OPTIONAL_GLOBAL_POS"); + Serializers$COMPOUND_TAG = initSerializersByName("COMPOUND_TAG"); + Serializers$VILLAGER_DATA = initSerializersByName("VILLAGER_DATA"); + Serializers$OPTIONAL_UNSIGNED_INT = initSerializersByName("OPTIONAL_UNSIGNED_INT"); + Serializers$POSE = initSerializersByName("POSE"); + Serializers$CAT_VARIANT = initSerializersByName("CAT_VARIANT"); + if (VersionHelper.isVersionNewerThan1_20_5()) Serializers$WOLF_VARIANT = initSerializersByName("WOLF_VARIANT"); + else Serializers$WOLF_VARIANT = null; + Serializers$FROG_VARIANT = initSerializersByName("FROG_VARIANT"); + Serializers$PAINTING_VARIANT = initSerializersByName("PAINTING_VARIANT"); + if (VersionHelper.isVersionNewerThan1_20_5()) Serializers$ARMADILLO_STATE = initSerializersByName("ARMADILLO_STATE"); + else Serializers$ARMADILLO_STATE = null; + Serializers$SNIFFER_STATE = initSerializersByName("SNIFFER_STATE"); + Serializers$VECTOR3 = initSerializersByName("VECTOR3"); + Serializers$QUATERNION = initSerializersByName("QUATERNION"); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static Object initSerializersByName(String name) throws ReflectiveOperationException { + return ReflectionUtils.getDeclaredField(Reflections.clazz$EntityDataSerializers, new String[]{fieldsObf[internalID++], name}).get(null); + } + + private EntityDataValue() { + throw new IllegalAccessError("Utility class"); + } + + public static Object create(int id, Object serializer, Object value) { + try { + Object entityDataAccessor =Reflections.constructor$EntityDataAccessor.newInstance(id, serializer); + return Reflections.method$SynchedEntityData$DataValue$create.invoke(null, entityDataAccessor, value); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java new file mode 100644 index 0000000..fb600f8 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/util/Reflections.java @@ -0,0 +1,744 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.util; + +import io.netty.channel.Channel; +import net.momirealms.customnameplates.api.helper.VersionHelper; +import net.momirealms.customnameplates.common.util.ReflectionUtils; +import sun.misc.Unsafe; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.UUID; + +import static java.util.Objects.requireNonNull; + +public class Reflections { + + public static final Class clazz$Component = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.Component"), + BukkitReflectionUtils.assembleMCClass("network.chat.IChatBaseComponent") + ) + ); + + public static final Class clazz$ComponentContents = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.ComponentContents") + ) + ); + + public static final Method method$Component$getContents = requireNonNull( + ReflectionUtils.getMethods( + clazz$Component, clazz$ComponentContents + ).get(0) + ); + + public static final Class clazz$ScoreContents = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.contents.ScoreContents") + ) + ); + + public static final Field field$ScoreContents$name = requireNonNull( + ReflectionUtils.getInstanceDeclaredField( + clazz$ScoreContents, String.class, 0 + ) + ); + + public static final Field field$ScoreContents$objective = requireNonNull( + ReflectionUtils.getInstanceDeclaredField( + clazz$ScoreContents, String.class, 1 + ) + ); + + public static final Class clazz$ClientboundSetActionBarTextPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundSetActionBarTextPacket") + ) + ); + + public static final Constructor constructor$ClientboundSetActionBarTextPacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ClientboundSetActionBarTextPacket, clazz$Component + ) + ); + + public static final Field field$ClientboundSetActionBarTextPacket$text = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundSetActionBarTextPacket, clazz$Component, 0 + ) + ); + + public static final Class clazz$ClientboundSystemChatPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundSystemChatPacket") + ) + ); + + public static final Field field$ClientboundSystemChatPacket$component = + ReflectionUtils.getDeclaredField( + clazz$ClientboundSystemChatPacket, clazz$Component, 0 + ); + + public static final Field field$ClientboundSystemChatPacket$overlay = + requireNonNull(ReflectionUtils.getDeclaredField( + clazz$ClientboundSystemChatPacket, boolean.class, 0 + )); + + public static final Field field$ClientboundSystemChatPacket$text = + ReflectionUtils.getDeclaredField( + clazz$ClientboundSystemChatPacket, String.class, 0 + ); + + public static final Class clazz$CraftChatMessage = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleCBClass("util.CraftChatMessage") + ) + ); + + public static final Method method$CraftChatMessage$fromJSON = requireNonNull( + ReflectionUtils.getMethod( + clazz$CraftChatMessage, + new String[]{"fromJSON"}, + String.class + ) + ); + + public static final Method method$CraftChatMessage$toJSON = requireNonNull( + ReflectionUtils.getMethod( + clazz$CraftChatMessage, + new String[]{"toJSON"}, + clazz$Component + ) + ); + + public static final Class clazz$ClientboundBossEventPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundBossEventPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutBoss") + ) + ); + + public static final Class clazz$ClientboundBossEventPacket$Operation = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundBossEventPacket$Operation"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutBoss$Action") + ) + ); + + public static final Constructor constructor$ClientboundBossEventPacket = requireNonNull( + ReflectionUtils.getDeclaredConstructor( + clazz$ClientboundBossEventPacket, + UUID.class, clazz$ClientboundBossEventPacket$Operation + ) + ); + + public static final Class clazz$ClientboundBossEventPacket$AddOperation = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundBossEventPacket$AddOperation"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutBoss$a") + ) + ); + + + public static final Class clazz$BossEvent$BossBarColor = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.BossEvent$BossBarColor"), + BukkitReflectionUtils.assembleMCClass("world.BossBattle$BarColor") + ) + ); + + public static final Method method$BossEvent$BossBarColor$valueOf = requireNonNull( + ReflectionUtils.getMethod( + clazz$BossEvent$BossBarColor, + new String[]{"valueOf"}, + String.class + ) + ); + + public static final Class clazz$BossEvent$BossBarOverlay = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.BossEvent$BossBarOverlay"), + BukkitReflectionUtils.assembleMCClass("world.BossBattle$BarStyle") + ) + ); + + public static final Method method$BossEvent$BossBarOverlay$valueOf = requireNonNull( + ReflectionUtils.getMethod( + clazz$BossEvent$BossBarOverlay, + new String[]{"valueOf"}, + String.class + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$name = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 0 + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$progress = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 1 + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$color = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 2 + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$overlay = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 3 + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$darkenScreen = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 4 + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$playMusic = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 5 + ) + ); + + public static final Field field$ClientboundBossEventPacket$AddOperation$createWorldFog = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket$AddOperation, + 6 + ) + ); + + public static final Unsafe UNSAFE; + + static { + try { + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + UNSAFE = (Unsafe) unsafeField.get(null); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + public static Object allocateAddOperationInstance() throws InstantiationException { + return UNSAFE.allocateInstance(clazz$ClientboundBossEventPacket$AddOperation); + } + + public static final Object instance$ClientboundBossEventPacket$REMOVE_OPERATION; + + static { + try { + Field field = ReflectionUtils.getDeclaredField( + clazz$ClientboundBossEventPacket, + "f", "REMOVE_OPERATION"); + field.setAccessible(true); + instance$ClientboundBossEventPacket$REMOVE_OPERATION = requireNonNull(field.get(null)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static final Class clazz$ClientboundBossEventPacket$UpdateNameOperation = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundBossEventPacket$UpdateNameOperation"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutBoss$e") + ) + ); + + public static final Constructor constructor$ClientboundBossEventPacket$UpdateNameOperation = requireNonNull( + ReflectionUtils.getDeclaredConstructor( + clazz$ClientboundBossEventPacket$UpdateNameOperation, + clazz$Component + ) + ); + + public static final Class clazz$CraftRegistry = ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleCBClass("CraftRegistry") + ); + + public static final Object instance$MinecraftRegistry; + + static { + if (VersionHelper.isVersionNewerThan1_20()) { + try { + Method method = requireNonNull(ReflectionUtils.getMethod(clazz$CraftRegistry, new String[]{"getMinecraftRegistry"})); + instance$MinecraftRegistry = method.invoke(null); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } else { + instance$MinecraftRegistry = null; + } + } + + public static final Class clazz$Component$Serializer = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.chat.Component$Serializer"), + BukkitReflectionUtils.assembleMCClass("network.chat.IChatBaseComponent$ChatSerializer") + ) + ); + + public static final Class clazz$HolderLookup$Provider = ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("core.HolderLookup$Provider"), + BukkitReflectionUtils.assembleMCClass("core.HolderLookup$b") + ); + + public static final Method method$Component$Serializer$fromJson = ReflectionUtils.getMethod( + clazz$Component$Serializer, + new String[] { "fromJson" }, + String.class, clazz$HolderLookup$Provider + ); + + public static final Method method$Component$Serializer$toJson = ReflectionUtils.getMethod( + clazz$Component$Serializer, + new String[] { "toJson" }, + clazz$Component, clazz$HolderLookup$Provider + ); + + public static final Class clazz$ClientboundBundlePacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundBundlePacket") + ) + ); + + public static final Constructor constructor$ClientboundBundlePacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundBundlePacket, Iterable.class) + ); + + public static final Class clazz$Packet = requireNonNull(ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.Packet") + )); + + public static final Class clazz$ServerPlayer = requireNonNull(ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("server.level.ServerPlayer"), + BukkitReflectionUtils.assembleMCClass("server.level.EntityPlayer") + )); + + public static final Class clazz$PlayerConnection = requireNonNull(ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("server.network.ServerGamePacketListenerImpl"), + BukkitReflectionUtils.assembleMCClass("server.network.PlayerConnection") + )); + + public static final Class clazz$CraftPlayer = requireNonNull(ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleCBClass("entity.CraftPlayer") + )); + + public static final Method method$CraftPlayer$getHandle = requireNonNull( + ReflectionUtils.getMethod(clazz$CraftPlayer, new String[] { "getHandle" }) + ); + + public static final Field field$PlayerConnection = requireNonNull( + ReflectionUtils.getDeclaredField(clazz$ServerPlayer, clazz$PlayerConnection, 0) + ); + + public static final Method method$SendPacket = requireNonNull( + ReflectionUtils.getMethods(clazz$PlayerConnection, void.class, clazz$Packet).get(0) + ); + + public static final Class clazz$NetworkManager = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.Connection"), + BukkitReflectionUtils.assembleMCClass("network.NetworkManager") + ) + ); + + public static final Field field$NetworkManager = requireNonNull( + VersionHelper.isVersionNewerThan1_20_2() ? + ReflectionUtils.getDeclaredField(clazz$PlayerConnection.getSuperclass(), clazz$NetworkManager, 0) : + ReflectionUtils.getDeclaredField(clazz$PlayerConnection, clazz$NetworkManager, 0) + ); + + public static final Field field$Channel = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$NetworkManager, Channel.class, 0 + ) + ); + + public static final Field field$BundlePacket$packets = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundBundlePacket.getSuperclass(), Iterable.class, 0 + ) + ); + + public static final Class clazz$ClientboundAddEntityPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutSpawnEntity"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundAddEntityPacket") + ) + ); + + public static final Class clazz$PacketPlayOutNamedEntitySpawn = + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutNamedEntitySpawn") + ); + + public static final Class clazz$ClientboundRemoveEntitiesPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundRemoveEntitiesPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntityDestroy") + ) + ); + + public static final Field field$ClientboundRemoveEntitiesPacket$entityIds = requireNonNull( + ReflectionUtils.getInstanceDeclaredField( + clazz$ClientboundRemoveEntitiesPacket, 0 + ) + ); + + public static final Field field$ClientboundAddEntityPacket$entityId = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundAddEntityPacket, int.class, 0 + ) + ); + + public static final Field field$PacketPlayOutNamedEntitySpawn$entityId = clazz$PacketPlayOutNamedEntitySpawn != null ? + ReflectionUtils.getDeclaredField( + clazz$PacketPlayOutNamedEntitySpawn, int.class, 0 + ) : null; + + public static final Class clazz$EntityType = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.entity.EntityType"), + BukkitReflectionUtils.assembleMCClass("world.entity.EntityTypes") + ) + ); + + public static final Class clazz$Vec3 = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.phys.Vec3"), + BukkitReflectionUtils.assembleMCClass("world.phys.Vec3D") + ) + ); + + public static final Constructor constructor$Vec3 = requireNonNull( + ReflectionUtils.getConstructor( + clazz$Vec3, double.class, double.class, double.class + ) + ); + + public static final Field field$EntityType$TEXT_DISPLAY; + + static { + if (VersionHelper.isVersionNewerThan1_20_5()) { + field$EntityType$TEXT_DISPLAY = ReflectionUtils.getDeclaredField( + clazz$EntityType, + "TEXT_DISPLAY", "bb"); + } else if (VersionHelper.isVersionNewerThan1_20_4()) { + field$EntityType$TEXT_DISPLAY = ReflectionUtils.getDeclaredField( + clazz$EntityType, + "TEXT_DISPLAY", "aY"); + } else { + field$EntityType$TEXT_DISPLAY = ReflectionUtils.getDeclaredField( + clazz$EntityType, + "TEXT_DISPLAY", "aX"); + } + } + + public static final Object instance$EntityType$TEXT_DISPLAY; + + static { + try { + instance$EntityType$TEXT_DISPLAY = field$EntityType$TEXT_DISPLAY.get(clazz$EntityType); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + public static final Constructor constructor$ClientboundAddEntityPacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundAddEntityPacket, + int.class, UUID.class, + double.class, double.class, double.class, + float.class, float.class, + clazz$EntityType, + int.class, clazz$Vec3, double.class + ) + ); + + public static final Constructor constructor$ClientboundRemoveEntitiesPacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundRemoveEntitiesPacket, int[].class) + ); + + public static final Class clazz$ClientboundSetPassengersPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutMount"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundSetPassengersPacket") + ) + ); + + public static final Field field$ClientboundSetPassengersPacket$vehicle = requireNonNull( + ReflectionUtils.getInstanceDeclaredField( + clazz$ClientboundSetPassengersPacket, 0 + ) + ); + + public static final Field field$ClientboundSetPassengersPacket$passengers = requireNonNull( + ReflectionUtils.getInstanceDeclaredField( + clazz$ClientboundSetPassengersPacket, 1 + ) + ); + + public static Object allocateClientboundSetPassengersPacketInstance() throws InstantiationException { + return UNSAFE.allocateInstance(clazz$ClientboundSetPassengersPacket); + } + + public static final Field field$Vec3$Zero = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$Vec3, clazz$Vec3, 0 + ) + ); + + public static final Object instance$Vec3$Zero; + + static { + try { + instance$Vec3$Zero = field$Vec3$Zero.get(null); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static final Class clazz$ClientboundSetEntityDataPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntityMetadata"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundSetEntityDataPacket") + ) + ); + + public static final Constructor constructor$ClientboundSetEntityDataPacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundSetEntityDataPacket, + int.class, List.class) + ); + + public static final Class clazz$EntityDataSerializers = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.syncher.EntityDataSerializers"), + BukkitReflectionUtils.assembleMCClass("network.syncher.DataWatcherRegistry") + ) + ); + + public static final Class clazz$EntityDataSerializer = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.syncher.EntityDataSerializer"), + BukkitReflectionUtils.assembleMCClass("network.syncher.DataWatcherSerializer") + ) + ); + + public static final Class clazz$EntityDataAccessor = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.syncher.EntityDataAccessor"), + BukkitReflectionUtils.assembleMCClass("network.syncher.DataWatcherObject") + ) + ); + + public static final Constructor constructor$EntityDataAccessor = requireNonNull( + ReflectionUtils.getConstructor( + clazz$EntityDataAccessor, int.class, clazz$EntityDataSerializer + ) + ); + + public static final Class clazz$SynchedEntityData$DataValue = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.syncher.SynchedEntityData$DataValue"), + BukkitReflectionUtils.assembleMCClass("network.syncher.DataWatcher$b") + ) + ); + + public static final Method method$SynchedEntityData$DataValue$create = requireNonNull( + ReflectionUtils.getMethod( + clazz$SynchedEntityData$DataValue, clazz$SynchedEntityData$DataValue, clazz$EntityDataAccessor, Object.class + ) + ); + + public static final Method method$Component$empty = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$Component, clazz$Component + ) + ); + + public static final Object instance$Component$empty; + + static { + try { + instance$Component$empty = method$Component$empty.invoke(null); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static final Class clazz$Quaternionf = requireNonNull( + ReflectionUtils.getClazz( + "org.joml.Quaternionf" + ) + ); + + public static final Constructor constructor$Quaternionf = requireNonNull( + ReflectionUtils.getConstructor( + clazz$Quaternionf, float.class, float.class, float.class, float.class + ) + ); + + public static final Object instance$Quaternionf$None; + + static { + try { + instance$Quaternionf$None = constructor$Quaternionf.newInstance(0f, 0f, 0f, 1f); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static final Class clazz$Vector3f = requireNonNull( + ReflectionUtils.getClazz( + "org.joml.Vector3f" + ) + ); + + public static final Constructor constructor$Vector3f = requireNonNull( + ReflectionUtils.getConstructor( + clazz$Vector3f, float.class, float.class, float.class + ) + ); + + public static final Object instance$Vector3f$None; + + static { + try { + instance$Vector3f$None = constructor$Vector3f.newInstance(0f, 0f, 0f); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static final Field field$ClientboundSetEntityDataPacket$id = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundSetEntityDataPacket, int.class, 0 + ) + ); + + public static final Field field$ClientboundSetEntityDataPacket$packedItems = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundSetEntityDataPacket, List.class, 0 + ) + ); + + public static final Field field$SynchedEntityData$DataValue$id = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$SynchedEntityData$DataValue, int.class, 0 + ) + ); + + public static final Field field$SynchedEntityData$DataValue$value = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$SynchedEntityData$DataValue, 2 + ) + ); + + public static final Class clazz$ClientboundUpdateAttributesPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundUpdateAttributesPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutUpdateAttributes") + ) + ); + + public static final Field field$ClientboundUpdateAttributesPacket$id = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundUpdateAttributesPacket, int.class, 0 + ) + ); + + public static final Field field$ClientboundUpdateAttributesPacket$attributes = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundUpdateAttributesPacket, List.class, 0 + ) + ); + + public static final Class clazz$ClientboundUpdateAttributesPacket$AttributeSnapshot = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundUpdateAttributesPacket$AttributeSnapshot"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutUpdateAttributes$AttributeSnapshot") + ) + ); + + public static final Class clazz$Holder = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("core.Holder") + ) + ); + + public static final Field field$ClientboundUpdateAttributesPacket$AttributeSnapshot$attribute = + ReflectionUtils.getDeclaredField( + clazz$ClientboundUpdateAttributesPacket$AttributeSnapshot, clazz$Holder, 0 + ); + + public static final Field field$ClientboundUpdateAttributesPacket$AttributeSnapshot$base = + ReflectionUtils.getDeclaredField( + clazz$ClientboundUpdateAttributesPacket$AttributeSnapshot, double.class, 0 + ); + + public static final Field field$ClientboundUpdateAttributesPacket$AttributeSnapshot$modifiers = + ReflectionUtils.getDeclaredField( + clazz$ClientboundUpdateAttributesPacket$AttributeSnapshot, Collection.class, 0 + ); + + public static final Method method$Holder$value = requireNonNull( + ReflectionUtils.getMethod( + clazz$Holder, new String[]{"a", "value"} + ) + ); + + public static final Class clazz$Attribute = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.entity.ai.attributes.Attribute"), + BukkitReflectionUtils.assembleMCClass("world.entity.ai.attributes.AttributeBase") + ) + ); + + public static final Field field$Attribute$id = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$Attribute, String.class, 0 + ) + ); + + public static final Class clazz$AttributeModifier = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.entity.ai.attributes.AttributeModifier") + ) + ); + + public static final Field field$AttributeModifier$amount = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$AttributeModifier, double.class, 0 + ) + ); +} diff --git a/bukkit/src/main/resources/commands.yml b/bukkit/src/main/resources/commands.yml new file mode 100644 index 0000000..0757d86 --- /dev/null +++ b/bukkit/src/main/resources/commands.yml @@ -0,0 +1,136 @@ +# +# Don't change this +# +config-version: "${config_version}" + +# +# For safety reasons, editing this file requires a restart to apply +# + +# A command to reload the plugin +# Usage: [COMMAND] +reload: + enable: true + permission: nameplates.command.reload + usage: + - /nameplates reload + +# A command to debug the load of the cpu +# Usage: [COMMAND] +debug_performance: + enable: true + permission: nameplates.command.debug.performance + usage: + - /nameplates debug performance + +# A command to debug the width +# Usage: [COMMAND] [TEXT] +debug_width: + enable: true + permission: nameplates.command.debug.width + usage: + - /nameplates debug width + +# A command to debug the lines +# Usage: [COMMAND] [WIDTH] [TEXT] +debug_lines: + enable: true + permission: nameplates.command.debug.lines + usage: + - /nameplates debug lines + +# A command to force a player to equip the specified nameplate +# Usage: [COMMAND] [PLAYER] [NAMEPLATE] +nameplates_force_equip: + enable: true + permission: nameplates.command.force_equip + usage: + - /nameplates force-equip + +# A command to force a player to unequip the current nameplate +# Usage: [COMMAND] [PLAYER] +nameplates_force_unequip: + enable: true + permission: nameplates.command.force_unequip + usage: + - /nameplates force-unequip + +# A command to force a player to preview the nameplate +# Usage: [COMMAND] [PLAYER] +nameplates_force_preview: + enable: true + permission: nameplates.command.force_preview + usage: + - /nameplates force-preview + +# A command to equip the nameplate +# Usage: [COMMAND] [NAMEPLATE] +nameplates_equip: + enable: true + permission: nameplates.command.equip + usage: + - /nameplates equip + +# A command to unequip the nameplate +# Usage: [COMMAND] +nameplates_unequip: + enable: true + permission: nameplates.command.unequip + usage: + - /nameplates unequip + +# A command to preview the nameplate +# Usage: [COMMAND] +nameplates_preview: + enable: true + permission: nameplates.command.preview + usage: + - /nameplates preview + +# A command to view the available nameplates +# Usage: [COMMAND] +nameplates_list: + enable: true + permission: nameplates.command.list + usage: + - /nameplates list + +# A command to force a player to equip the specified bubble +# Usage: [COMMAND] [PLAYER] [BUBBLE] +bubbles_force_equip: + enable: true + permission: bubbles.command.force_equip + usage: + - /bubbles force-equip + +# A command to force a player to unequip the current bubble +# Usage: [COMMAND] [PLAYER] +bubbles_force_unequip: + enable: true + permission: bubbles.command.force_unequip + usage: + - /bubbles force-unequip + +# A command to equip the bubble +# Usage: [COMMAND] [BUBBLE] +bubbles_equip: + enable: true + permission: bubbles.command.equip + usage: + - /bubbles equip + +# A command to unequip the bubble +# Usage: [COMMAND] +bubbles_unequip: + enable: true + permission: bubbles.command.unequip + usage: + - /bubbles unequip + +# A command to view the available bubbles +# Usage: [COMMAND] +bubbles_list: + enable: true + permission: bubbles.command.list + usage: + - /bubbles list \ No newline at end of file diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml new file mode 100644 index 0000000..f87d7bd --- /dev/null +++ b/bukkit/src/main/resources/plugin.yml @@ -0,0 +1,36 @@ +name: CustomNameplates +version: '${project_version}' +main: net.momirealms.customnameplates.bukkit.BukkitBootstrap +api-version: 1.19 +authors: [ XiaoMoMi ] +folia-supported: true +softdepend: + - PlaceholderAPI + - MagicCosmetics + - ItemsAdder + - Oraxen + - TrChat + - CarbonChat + - HuskChat + - AdvancedChat + - VentureChat + - EssentialsChat +permissions: + nameplates.command.equip: + default: true + nameplates.command.unequip: + default: true + nameplates.command.preview: + default: true + nameplates.command.list: + default: true + bubbles.command.equip: + default: true + bubbles.command.unequip: + default: true + bubbles.command.list: + default: true + bubbles.send: + default: true + bubbles.see: + default: true \ No newline at end of file diff --git a/bungeecord/build.gradle.kts b/bungeecord/build.gradle.kts deleted file mode 100644 index 9148a59..0000000 --- a/bungeecord/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -dependencies { - implementation(project(":common")) - // TAB - compileOnly("me.neznamy:tab-api:4.0.2") - // BungeeTabListPlus - compileOnly("codecrafter47.bungeetablistplus:bungeetablistplus-api-bungee:3.6.4") - // BungeeCord - compileOnly("net.md-5:bungeecord-api:1.20-R0.2-SNAPSHOT") - compileOnly("net.md-5:bungeecord-protocol:1.20-R0.2-SNAPSHOT") - compileOnly("net.md-5:bungeecord-event:1.20-R0.2-SNAPSHOT") - compileOnly("net.md-5:bungeecord-proxy:1.20-R0.2-SNAPSHOT") - - implementation("net.kyori:adventure-api:4.16.0") - implementation("net.kyori:adventure-platform-bungeecord:4.3.2") - implementation("net.kyori:adventure-text-minimessage:4.16.0") - implementation("net.kyori:adventure-text-serializer-legacy:4.16.0") -} - -tasks { - shadowJar { - relocate ("net.kyori", "net.momirealms.customnameplates.libraries") - } -} \ No newline at end of file diff --git a/bungeecord/src/main/java/net/momirealms/customnameplates/bungeecord/CustomNameplatesBungeeCord.java b/bungeecord/src/main/java/net/momirealms/customnameplates/bungeecord/CustomNameplatesBungeeCord.java deleted file mode 100644 index ae47ca7..0000000 --- a/bungeecord/src/main/java/net/momirealms/customnameplates/bungeecord/CustomNameplatesBungeeCord.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.bungeecord; - -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.api.plugin.Plugin; - -public class CustomNameplatesBungeeCord extends Plugin implements Listener { - - private static final String CHANNEL = "customnameplates:cnp"; - private static CustomNameplatesBungeeCord instance; - - @Override - public void onEnable() { - instance = this; - this.getProxy().registerChannel(CHANNEL); - this.getProxy().getPluginManager().registerListener(this, this); - } - - @Override - public void onDisable() { - this.getProxy().unregisterChannel(CHANNEL); - this.getProxy().getPluginManager().unregisterListener(this); - } - - public static CustomNameplatesBungeeCord getInstance() { - return instance; - } - - public static CustomNameplatesBungeeCord get() { - return instance; - } -} diff --git a/bungeecord/src/main/resources/bungee.yml b/bungeecord/src/main/resources/bungee.yml deleted file mode 100644 index af11dc3..0000000 --- a/bungeecord/src/main/resources/bungee.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: CustomNameplates -main: net.momirealms.customnameplates.bungeecord.CustomNameplatesBungeeCord -version: '${version}' -author: XiaoMoMi \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 8254100..377aa58 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,33 @@ dependencies { - compileOnly("org.jetbrains:annotations:24.1.0") + compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}") { + exclude(module = "adventure-bom") + exclude(module = "checker-qual") + exclude(module = "annotations") + } + compileOnly("org.incendo:cloud-core:${rootProject.properties["cloud_core_version"]}") + compileOnly("org.incendo:cloud-minecraft-extras:${rootProject.properties["cloud_minecraft_extras_version"]}") + compileOnly("dev.dejvokep:boosted-yaml:${rootProject.properties["boosted_yaml_version"]}") + compileOnly("org.jetbrains:annotations:${rootProject.properties["jetbrains_annotations_version"]}") + compileOnly("org.slf4j:slf4j-api:${rootProject.properties["slf4j_version"]}") + compileOnly("org.apache.logging.log4j:log4j-core:${rootProject.properties["log4j_version"]}") + compileOnly("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") + compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}") + compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}") + compileOnly("net.objecthunter:exp4j:${rootProject.properties["exp4j_version"]}") + compileOnly("net.bytebuddy:byte-buddy:${rootProject.properties["byte_buddy_version"]}") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) } \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/SimpleLocation.java b/common/src/main/java/net/momirealms/customnameplates/common/SimpleLocation.java deleted file mode 100644 index 5eacfb3..0000000 --- a/common/src/main/java/net/momirealms/customnameplates/common/SimpleLocation.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.common; - -import java.util.Objects; - -public record SimpleLocation(String worldName, int x, int y, int z) { - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final SimpleLocation other = (SimpleLocation) obj; - if (!Objects.equals(worldName, other.worldName())) { - return false; - } - if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) { - return false; - } - if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) { - return false; - } - if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 3; - hash = 19 * hash + (worldName != null ? worldName.hashCode() : 0); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); - return hash; - } -} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/Tuple.java b/common/src/main/java/net/momirealms/customnameplates/common/Tuple.java deleted file mode 100644 index a1a2876..0000000 --- a/common/src/main/java/net/momirealms/customnameplates/common/Tuple.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.common; - -public class Tuple { - - private L left; - private M mid; - private R right; - - public Tuple(L left, M mid, R right) { - this.left = left; - this.mid = mid; - this.right = right; - } - - public static Tuple of(final L left, final M mid, final R right) { - return new Tuple<>(left, mid, right); - } - - public L getLeft() { - return left; - } - - public void setLeft(L left) { - this.left = left; - } - - public M getMid() { - return mid; - } - - public void setMid(M mid) { - this.mid = mid; - } - - public R getRight() { - return right; - } - - public void setRight(R right) { - this.right = right; - } - - @Override - public String toString() { - return "Tuple{" + - "left=" + left + - ", mid=" + mid + - ", right=" + right + - '}'; - } -} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/command/AbstractCommandFeature.java b/common/src/main/java/net/momirealms/customnameplates/common/command/AbstractCommandFeature.java new file mode 100644 index 0000000..13f47ef --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/command/AbstractCommandFeature.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customnameplates.common.sender.SenderFactory; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; + +public abstract class AbstractCommandFeature implements CommandFeature { + + protected final CustomNameplatesCommandManager commandManager; + protected CommandConfig commandConfig; + + public AbstractCommandFeature(CustomNameplatesCommandManager commandManager) { + this.commandManager = commandManager; + } + + protected abstract SenderFactory getSenderFactory(); + + public abstract Command.Builder assembleCommand(CommandManager manager, Command.Builder builder); + + @Override + @SuppressWarnings("unchecked") + public Command registerCommand(CommandManager manager, Command.Builder builder) { + Command command = (Command) assembleCommand(manager, builder).build(); + manager.command(command); + return command; + } + + @Override + public void registerRelatedFunctions() { + // empty + } + + @Override + public void unregisterRelatedFunctions() { + // empty + } + + @Override + @SuppressWarnings("unchecked") + public void handleFeedback(CommandContext context, TranslatableComponent.Builder key, Component... args) { + if (context.flags().hasFlag("silent")) { + return; + } + commandManager.handleCommandFeedback((C) context.sender(), key, args); + } + + @Override + public void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args) { + commandManager.handleCommandFeedback(sender, key, args); + } + + @Override + public CustomNameplatesCommandManager getCustomFishingCommandManager() { + return commandManager; + } + + @Override + public CommandConfig getCommandConfig() { + return commandConfig; + } + + public void setCommandConfig(CommandConfig commandConfig) { + this.commandConfig = commandConfig; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/command/AbstractCommandManager.java b/common/src/main/java/net/momirealms/customnameplates/common/command/AbstractCommandManager.java new file mode 100644 index 0000000..3f58483 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/command/AbstractCommandManager.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.command; + +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.block.implementation.Section; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TranslatableComponent; +import net.momirealms.customnameplates.common.locale.CustomNameplatesCaptionFormatter; +import net.momirealms.customnameplates.common.locale.CustomNameplatesCaptionProvider; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.sender.Sender; +import net.momirealms.customnameplates.common.util.ArrayUtils; +import net.momirealms.customnameplates.common.util.TriConsumer; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.StandardCaptionKeys; +import org.incendo.cloud.component.CommandComponent; +import org.incendo.cloud.exception.*; +import org.incendo.cloud.exception.handling.ExceptionContext; +import org.incendo.cloud.minecraft.extras.MinecraftExceptionHandler; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +public abstract class AbstractCommandManager implements CustomNameplatesCommandManager { + + protected final HashSet> registeredRootCommandComponents = new HashSet<>(); + protected final HashSet> registeredFeatures = new HashSet<>(); + protected final CommandManager commandManager; + protected final NameplatesPlugin plugin; + private final CustomNameplatesCaptionFormatter captionFormatter = new CustomNameplatesCaptionFormatter(); + private final MinecraftExceptionHandler.Decorator decorator = (formatter, ctx, msg) -> msg; + + private TriConsumer feedbackConsumer; + + public AbstractCommandManager(NameplatesPlugin plugin, CommandManager commandManager) { + this.commandManager = commandManager; + this.plugin = plugin; + this.inject(); + this.feedbackConsumer = defaultFeedbackConsumer(); + } + + @Override + public void setFeedbackConsumer(@NotNull TriConsumer feedbackConsumer) { + this.feedbackConsumer = feedbackConsumer; + } + + @Override + public TriConsumer defaultFeedbackConsumer() { + return ((sender, node, component) -> { + wrapSender(sender).sendMessage( + component, true + ); + }); + } + + protected abstract Sender wrapSender(C c); + + private void inject() { + getCommandManager().captionRegistry().registerProvider(new CustomNameplatesCaptionProvider<>()); + injectExceptionHandler(InvalidSyntaxException.class, MinecraftExceptionHandler.createDefaultInvalidSyntaxHandler(), StandardCaptionKeys.EXCEPTION_INVALID_SYNTAX); + injectExceptionHandler(InvalidCommandSenderException.class, MinecraftExceptionHandler.createDefaultInvalidSenderHandler(), StandardCaptionKeys.EXCEPTION_INVALID_SENDER); + injectExceptionHandler(NoPermissionException.class, MinecraftExceptionHandler.createDefaultNoPermissionHandler(), StandardCaptionKeys.EXCEPTION_NO_PERMISSION); + injectExceptionHandler(ArgumentParseException.class, MinecraftExceptionHandler.createDefaultArgumentParsingHandler(), StandardCaptionKeys.EXCEPTION_INVALID_ARGUMENT); + injectExceptionHandler(CommandExecutionException.class, MinecraftExceptionHandler.createDefaultCommandExecutionHandler(), StandardCaptionKeys.EXCEPTION_UNEXPECTED); + } + + @SuppressWarnings("unchecked") + private void injectExceptionHandler(Class type, MinecraftExceptionHandler.MessageFactory factory, Caption key) { + getCommandManager().exceptionController().registerHandler(type, ctx -> { + final @Nullable ComponentLike message = factory.message(captionFormatter, (ExceptionContext) ctx); + if (message != null) { + handleCommandFeedback(ctx.context().sender(), key.key(), decorator.decorate(captionFormatter, ctx, message.asComponent()).asComponent()); + } + }); + } + + @Override + public CommandConfig getCommandConfig(YamlDocument document, String featureID) { + Section section = document.getSection(featureID); + if (section == null) return null; + return new CommandConfig.Builder() + .permission(section.getString("permission")) + .usages(section.getStringList("usage")) + .enable(section.getBoolean("enable", false)) + .build(); + } + + @Override + public Collection> buildCommandBuilders(CommandConfig config) { + ArrayList> list = new ArrayList<>(); + for (String usage : config.getUsages()) { + if (!usage.startsWith("/")) continue; + String command = usage.substring(1).trim(); + String[] split = command.split(" "); + Command.Builder builder = new CommandBuilder.BasicCommandBuilder<>(getCommandManager(), split[0]) + .setCommandNode(ArrayUtils.subArray(split, 1)) + .setPermission(config.getPermission()) + .getBuiltCommandBuilder(); + list.add(builder); + } + return list; + } + + @Override + public void registerFeature(CommandFeature feature, CommandConfig config) { + if (!config.isEnable()) throw new RuntimeException("Registering a disabled command feature is not allowed"); + for (Command.Builder builder : buildCommandBuilders(config)) { + Command command = feature.registerCommand(commandManager, builder); + this.registeredRootCommandComponents.add(command.rootComponent()); + } + feature.registerRelatedFunctions(); + this.registeredFeatures.add(feature); + ((AbstractCommandFeature) feature).setCommandConfig(config); + } + + @Override + public void registerDefaultFeatures() { + YamlDocument document = plugin.getConfigManager().loadConfig(commandsFile, '.'); + try { + document.save(new File(plugin.getDataDirectory().toFile(), "commands.yml")); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.getFeatures().values().forEach(feature -> { + CommandConfig config = getCommandConfig(document, feature.getFeatureID()); + if (config.isEnable()) { + registerFeature(feature, config); + } + }); + } + + @Override + public void unregisterFeatures() { + this.registeredRootCommandComponents.forEach(component -> this.commandManager.commandRegistrationHandler().unregisterRootCommand(component)); + this.registeredRootCommandComponents.clear(); + this.registeredFeatures.forEach(CommandFeature::unregisterRelatedFunctions); + this.registeredFeatures.clear(); + } + + @Override + public CommandManager getCommandManager() { + return commandManager; + } + + @Override + public void handleCommandFeedback(C sender, TranslatableComponent.Builder key, Component... args) { + TranslatableComponent component = key.arguments(args).build(); + this.feedbackConsumer.accept(sender, component.key(), TranslationManager.render(component)); + } + + @Override + public void handleCommandFeedback(C sender, String node, Component component) { + this.feedbackConsumer.accept(sender, node, component); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/command/CommandBuilder.java b/common/src/main/java/net/momirealms/customnameplates/common/command/CommandBuilder.java new file mode 100644 index 0000000..566822c --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/command/CommandBuilder.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.command; + +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; + +public interface CommandBuilder { + + CommandBuilder setPermission(String permission); + + CommandBuilder setCommandNode(String... subNodes); + + Command.Builder getBuiltCommandBuilder(); + + class BasicCommandBuilder implements CommandBuilder { + + private Command.Builder commandBuilder; + + public BasicCommandBuilder(CommandManager commandManager, String rootNode) { + this.commandBuilder = commandManager.commandBuilder(rootNode); + } + + @Override + public CommandBuilder setPermission(String permission) { + this.commandBuilder = this.commandBuilder.permission(permission); + return this; + } + + @Override + public CommandBuilder setCommandNode(String... subNodes) { + for (String sub : subNodes) { + this.commandBuilder = this.commandBuilder.literal(sub); + } + return this; + } + + @Override + public Command.Builder getBuiltCommandBuilder() { + return commandBuilder; + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/command/CommandConfig.java b/common/src/main/java/net/momirealms/customnameplates/common/command/CommandConfig.java new file mode 100644 index 0000000..e4a85cb --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/command/CommandConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.command; + +import java.util.ArrayList; +import java.util.List; + +public class CommandConfig { + + private boolean enable = false; + private List usages = new ArrayList<>(); + private String permission = null; + + private CommandConfig() { + } + + public CommandConfig(boolean enable, List usages, String permission) { + this.enable = enable; + this.usages = usages; + this.permission = permission; + } + + public boolean isEnable() { + return enable; + } + + public List getUsages() { + return usages; + } + + public String getPermission() { + return permission; + } + + public static class Builder { + + private final CommandConfig config; + + public Builder() { + this.config = new CommandConfig<>(); + } + + public Builder usages(List usages) { + config.usages = usages; + return this; + } + + public Builder permission(String permission) { + config.permission = permission; + return this; + } + + public Builder enable(boolean enable) { + config.enable = enable; + return this; + } + + public CommandConfig build() { + return config; + } + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/command/CommandFeature.java b/common/src/main/java/net/momirealms/customnameplates/common/command/CommandFeature.java new file mode 100644 index 0000000..56d398a --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/command/CommandFeature.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.command; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.context.CommandContext; + +public interface CommandFeature { + + Command registerCommand(CommandManager cloudCommandManager, Command.Builder builder); + + String getFeatureID(); + + void registerRelatedFunctions(); + + void unregisterRelatedFunctions(); + + void handleFeedback(CommandContext context, TranslatableComponent.Builder key, Component... args); + + void handleFeedback(C sender, TranslatableComponent.Builder key, Component... args); + + CustomNameplatesCommandManager getCustomFishingCommandManager(); + + CommandConfig getCommandConfig(); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/command/CustomNameplatesCommandManager.java b/common/src/main/java/net/momirealms/customnameplates/common/command/CustomNameplatesCommandManager.java new file mode 100644 index 0000000..68a4843 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/command/CustomNameplatesCommandManager.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.command; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.util.Index; +import net.momirealms.customnameplates.common.util.TriConsumer; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +public interface CustomNameplatesCommandManager { + + String commandsFile = "commands.yml"; + + void unregisterFeatures(); + + void registerFeature(CommandFeature feature, CommandConfig config); + + void registerDefaultFeatures(); + + Index> getFeatures(); + + void setFeedbackConsumer(@NotNull TriConsumer feedbackConsumer); + + TriConsumer defaultFeedbackConsumer(); + + CommandConfig getCommandConfig(YamlDocument document, String featureID); + + Collection> buildCommandBuilders(CommandConfig config); + + CommandManager getCommandManager(); + + void handleCommandFeedback(C sender, TranslatableComponent.Builder key, Component... args); + + void handleCommandFeedback(C sender, String node, Component component); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java new file mode 100644 index 0000000..5c3b9b1 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java @@ -0,0 +1,385 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.dependency; + +import net.momirealms.customnameplates.common.dependency.relocation.Relocation; +import net.momirealms.customnameplates.common.plugin.CustomNameplatesProperties; +import org.jetbrains.annotations.Nullable; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * The dependencies used by CustomFishing. + */ +public enum Dependency { + + ASM( + "org.ow2.asm", + "asm", + "maven", + "asm" + ), + ASM_COMMONS( + "org.ow2.asm", + "asm-commons", + "maven", + "asm-commons" + ), + JAR_RELOCATOR( + "me.lucko", + "jar-relocator", + "maven", + "jar-relocator" + ), + H2_DRIVER( + "com.h2database", + "h2", + "maven", + "h2-driver" + ), + SQLITE_DRIVER( + "org.xerial", + "sqlite-jdbc", + "maven", + "sqlite-driver" + ), + CLOUD_CORE( + "org{}incendo", + "cloud-core", + "maven", + "cloud-core", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_BRIGADIER( + "org{}incendo", + "cloud-brigadier", + "maven", + "cloud-brigadier", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_SERVICES( + "org{}incendo", + "cloud-services", + "maven", + "cloud-services", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_BUKKIT( + "org{}incendo", + "cloud-bukkit", + "maven", + "cloud-bukkit", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_PAPER( + "org{}incendo", + "cloud-paper", + "maven", + "cloud-paper", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + CLOUD_MINECRAFT_EXTRAS( + "org{}incendo", + "cloud-minecraft-extras", + "maven", + "cloud-minecraft-extras", + Relocation.of("cloud", "org{}incendo{}cloud"), + Relocation.of("adventure", "net{}kyori{}adventure"), + Relocation.of("option", "net{}kyori{}option"), + Relocation.of("examination", "net{}kyori{}examination"), + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + GEANTY_REF( + "io{}leangen{}geantyref", + "geantyref", + "maven", + "geantyref", + Relocation.of("geantyref", "io{}leangen{}geantyref") + ), + BOOSTED_YAML( + "dev{}dejvokep", + "boosted-yaml", + "maven", + "boosted-yaml", + Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") + ), + BYTEBUDDY( + "net{}bytebuddy", + "byte-buddy", + "maven", + "byte-buddy", + Relocation.of("bytebuddy", "net{}bytebuddy") + ), + MARIADB_DRIVER( + "org{}mariadb{}jdbc", + "mariadb-java-client", + "maven", + "mariadb-java-client", + Relocation.of("mariadb", "org{}mariadb") + ), + MYSQL_DRIVER( + "com{}mysql", + "mysql-connector-j", + "maven", + "mysql-connector-j", + Relocation.of("mysql", "com{}mysql") + ), + HIKARI_CP( + "com{}zaxxer", + "HikariCP", + "maven", + "hikari-cp", + Relocation.of("hikari", "com{}zaxxer{}hikari") + ), + MONGODB_DRIVER_CORE( + "org{}mongodb", + "mongodb-driver-core", + "maven", + "mongodb-driver-core", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ), + MONGODB_DRIVER_SYNC( + "org{}mongodb", + "mongodb-driver-sync", + "maven", + "mongodb-driver-sync", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ) { + @Override + public String getVersion() { + return Dependency.MONGODB_DRIVER_CORE.getVersion(); + } + }, + MONGODB_DRIVER_BSON( + "org{}mongodb", + "bson", + "maven", + "mongodb-bson", + Relocation.of("mongodb", "com{}mongodb"), + Relocation.of("bson", "org{}bson") + ) { + @Override + public String getVersion() { + return Dependency.MONGODB_DRIVER_CORE.getVersion(); + } + }, + COMMONS_POOL_2( + "org{}apache{}commons", + "commons-pool2", + "maven", + "commons-pool", + Relocation.of("commonspool2", "org{}apache{}commons{}pool2") + ), + BSTATS_BASE( + "org{}bstats", + "bstats-base", + "maven", + "bstats-base", + Relocation.of("bstats", "org{}bstats") + ), + BSTATS_BUKKIT( + "org{}bstats", + "bstats-bukkit", + "maven", + "bstats-bukkit", + Relocation.of("bstats", "org{}bstats") + ) { + @Override + public String getVersion() { + return Dependency.BSTATS_BASE.getVersion(); + } + }, + GSON( + "com.google.code.gson", + "gson", + "maven", + "gson" + ), + CAFFEINE( + "com{}github{}ben-manes{}caffeine", + "caffeine", + "maven", + "caffeine", + Relocation.of("caffeine", "com{}github{}benmanes{}caffeine") + ), + JEDIS( + "redis{}clients", + "jedis", + "maven", + "jedis", + Relocation.of("jedis", "redis{}clients{}jedis"), + Relocation.of("commonspool2", "org{}apache{}commons{}pool2") + ), + EXP4J( + "net{}objecthunter", + "exp4j", + "maven", + "exp4j", + Relocation.of("exp4j", "net{}objecthunter{}exp4j") + ), + SLF4J_SIMPLE( + "org.slf4j", + "slf4j-simple", + "maven", + "slf4j_simple" + ) { + @Override + public String getVersion() { + return Dependency.SLF4J_API.getVersion(); + } + }, + SLF4J_API( + "org.slf4j", + "slf4j-api", + "maven", + "slf4j" + ), + FONT_BOX( + "org{}apache{}pdfbox", + "fontbox", + "maven", + "fontbox", + Relocation.of("fontbox", "org{}apache{}fontbox"), + Relocation.of("pdfbox", "org{}apache{}pdfbox") + ), + PDF_BOX( + "org{}apache{}pdfbox", + "pdfbox-io", + "maven", + "pdfbox-io", + Relocation.of("pdfbox", "org{}apache{}pdfbox") + ), + BYTE_BUDDY( + "net{}bytebuddy", + "byte-buddy", + "maven", + "byte-buddy", + Relocation.of("bytebuddy", "net{}bytebuddy") + ), + COMMONS_IO( + "commons-io", + "commons-io", + "maven", + "commons-io", + Relocation.of("commons", "org{}apache{}commons") + ); + + private final List relocations; + private final String repo; + private final String groupId; + private final String artifactId; + private final String customArtifactID; + private String artifactIdSuffix; + + private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; + + Dependency(String groupId, String artifactId, String repo, String customArtifactID) { + this(groupId, artifactId, repo, customArtifactID, new Relocation[0]); + } + + Dependency(String groupId, String artifactId, String repo, String customArtifactID, Relocation... relocations) { + this.artifactId = artifactId; + this.artifactIdSuffix = ""; + this.groupId = groupId; + this.relocations = new ArrayList<>(Arrays.stream(relocations).toList()); + this.repo = repo; + this.customArtifactID = customArtifactID; + } + + Dependency(String groupId, String artifactId, String repo, String customArtifactID, String artifactIdSuffix, Relocation... relocations) { + this.artifactId = artifactId; + this.artifactIdSuffix = artifactIdSuffix; + this.groupId = groupId; + this.relocations = new ArrayList<>(Arrays.stream(relocations).toList()); + this.repo = repo; + this.customArtifactID = customArtifactID; + } + + public void setArtifactIdSuffix(String artifactIdSuffix) { + this.artifactIdSuffix = artifactIdSuffix; + } + + public String getVersion() { + return CustomNameplatesProperties.getValue(customArtifactID); + } + + private static String rewriteEscaping(String s) { + return s.replace("{}", "."); + } + + public String getFileName(String classifier) { + String name = customArtifactID.toLowerCase(Locale.ROOT).replace('_', '-'); + String extra = classifier == null || classifier.isEmpty() + ? "" + : "-" + classifier; + return name + "-" + this.getVersion() + extra + ".jar"; + } + + String getMavenRepoPath() { + return String.format(MAVEN_FORMAT, + rewriteEscaping(groupId).replace(".", "/"), + rewriteEscaping(artifactId), + getVersion(), + rewriteEscaping(artifactId), + getVersion() + artifactIdSuffix + ); + } + + public List getRelocations() { + return this.relocations; + } + + /** + * Creates a {@link MessageDigest} suitable for computing the checksums + * of dependencies. + * + * @return the digest + */ + public static MessageDigest createDigest() { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @Nullable + public String getRepo() { + return repo; + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyDownloadException.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyDownloadException.java similarity index 96% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyDownloadException.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyDownloadException.java index 1bed837..679517c 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyDownloadException.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyDownloadException.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies; +package net.momirealms.customnameplates.common.dependency; /** * Exception thrown if a dependency cannot be downloaded. diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyManager.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyManager.java similarity index 96% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyManager.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyManager.java index ccdadf2..1a8939c 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyManager.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyManager.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies; +package net.momirealms.customnameplates.common.dependency; import java.util.Collection; import java.util.Set; diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyManagerImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyManagerImpl.java similarity index 68% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyManagerImpl.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyManagerImpl.java index bee1889..43a4d37 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyManagerImpl.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyManagerImpl.java @@ -23,27 +23,23 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies; +package net.momirealms.customnameplates.common.dependency; -import com.google.common.collect.ImmutableSet; -import net.draycia.carbon.common.config.ConfigManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.libraries.classpath.ClassPathAppender; -import net.momirealms.customnameplates.paper.libraries.dependencies.classloader.IsolatedClassLoader; -import net.momirealms.customnameplates.paper.libraries.dependencies.relocation.Relocation; -import net.momirealms.customnameplates.paper.libraries.dependencies.relocation.RelocationHandler; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import net.momirealms.customnameplates.common.dependency.classloader.IsolatedClassLoader; +import net.momirealms.customnameplates.common.dependency.relocation.Relocation; +import net.momirealms.customnameplates.common.dependency.relocation.RelocationHandler; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.plugin.classpath.ClassPathAppender; -import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; /** * Loads and manages runtime dependencies for the plugin. @@ -59,20 +55,24 @@ public class DependencyManagerImpl implements DependencyManager { /** A map of dependencies which have already been loaded. */ private final EnumMap loaded = new EnumMap<>(Dependency.class); /** A map of isolated classloaders which have been created. */ - private final Map, IsolatedClassLoader> loaders = new HashMap<>(); + private final Map, IsolatedClassLoader> loaders = new HashMap<>(); /** Cached relocation handler instance. */ - private @MonotonicNonNull RelocationHandler relocationHandler = null; + private final RelocationHandler relocationHandler; + private final Executor loadingExecutor; + private final NameplatesPlugin plugin; - public DependencyManagerImpl(CustomNameplatesPluginImpl plugin, ClassPathAppender classPathAppender) { + public DependencyManagerImpl(NameplatesPlugin plugin) { + this.plugin = plugin; this.registry = new DependencyRegistry(); this.cacheDirectory = setupCacheDirectory(plugin); - this.classPathAppender = classPathAppender; + this.classPathAppender = plugin.getClassPathAppender(); + this.loadingExecutor = plugin.getScheduler().async(); this.relocationHandler = new RelocationHandler(this); } @Override public ClassLoader obtainClassLoaderWith(Set dependencies) { - ImmutableSet set = ImmutableSet.copyOf(dependencies); + Set set = new HashSet<>(dependencies); for (Dependency dependency : dependencies) { if (!this.loaded.containsKey(dependency)) { @@ -113,13 +113,15 @@ public class DependencyManagerImpl implements DependencyManager { continue; } - try { - loadDependency(dependency); - } catch (Throwable e) { - new RuntimeException("Unable to load dependency " + dependency.name(), e).printStackTrace(); - } finally { - latch.countDown(); - } + this.loadingExecutor.execute(() -> { + try { + loadDependency(dependency); + } catch (Throwable e) { + this.plugin.getPluginLogger().warn("Unable to load dependency " + dependency.name(), e); + } finally { + latch.countDown(); + } + }); } try { @@ -144,7 +146,8 @@ public class DependencyManagerImpl implements DependencyManager { } private Path downloadDependency(Dependency dependency) throws DependencyDownloadException { - Path file = this.cacheDirectory.resolve(dependency.getFileName(null)); + String fileName = dependency.getFileName(null); + Path file = this.cacheDirectory.resolve(fileName); // if the file already exists, don't attempt to re-download it. if (Files.exists(file)) { @@ -152,31 +155,19 @@ public class DependencyManagerImpl implements DependencyManager { } DependencyDownloadException lastError = null; - - String fileName = dependency.getFileName(null); String forceRepo = dependency.getRepo(); - if (forceRepo == null) { - // attempt to download the dependency from each repo in order. - for (DependencyRepository repo : DependencyRepository.repos()) { + List repository = DependencyRepository.getByID(forceRepo); + if (!repository.isEmpty()) { + int i = 0; + while (i < repository.size()) { try { - LogUtils.info("Downloading dependency(" + fileName + ")[" + repo.getUrl() + dependency.getMavenRepoPath() + "]"); - repo.download(dependency, file); - LogUtils.info("Successfully downloaded " + fileName); - return file; - } catch (DependencyDownloadException e) { - lastError = e; - } - } - } else { - DependencyRepository repository = DependencyRepository.getByID(forceRepo); - if (repository != null) { - try { - LogUtils.info("Downloading dependency(" + fileName + ")[" + repository.getUrl() + dependency.getMavenRepoPath() + "]"); - repository.download(dependency, file); - LogUtils.info("Successfully downloaded " + fileName); + plugin.getPluginLogger().info("Downloading dependency(" + fileName + ")[" + repository.get(i).getUrl() + dependency.getMavenRepoPath() + "]"); + repository.get(i).download(dependency, file); + plugin.getPluginLogger().info("Successfully downloaded " + fileName); return file; } catch (DependencyDownloadException e) { lastError = e; + i++; } } } @@ -196,16 +187,29 @@ public class DependencyManagerImpl implements DependencyManager { return remappedFile; } - LogUtils.info("Remapping " + dependency.getFileName(null)); + plugin.getPluginLogger().info("Remapping " + dependency.getFileName(null)); relocationHandler.remap(normalFile, remappedFile, rules); - LogUtils.info("Successfully remapped " + dependency.getFileName(null)); + plugin.getPluginLogger().info("Successfully remapped " + dependency.getFileName(null)); return remappedFile; } - private static Path setupCacheDirectory(CustomNameplatesPluginImpl plugin) { - File folder = new File(plugin.getDataFolder(), "libs"); - folder.mkdirs(); - return folder.toPath(); + private static Path setupCacheDirectory(NameplatesPlugin plugin) { + Path cacheDirectory = plugin.getDataDirectory().resolve("libs"); + try { + if (Files.exists(cacheDirectory) && (Files.isDirectory(cacheDirectory) || Files.isSymbolicLink(cacheDirectory))) { + return cacheDirectory; + } + + try { + Files.createDirectories(cacheDirectory); + } catch (FileAlreadyExistsException e) { + // ignore + } + } catch (IOException e) { + throw new RuntimeException("Unable to create libs directory", e); + } + + return cacheDirectory; } @Override @@ -225,8 +229,7 @@ public class DependencyManagerImpl implements DependencyManager { } if (firstEx != null) { - firstEx.printStackTrace(); + plugin.getPluginLogger().severe(firstEx.getMessage(), firstEx); } } - } diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyRegistry.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyRegistry.java similarity index 93% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyRegistry.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyRegistry.java index 6f2a274..89865f8 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyRegistry.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyRegistry.java @@ -23,12 +23,12 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies; +package net.momirealms.customnameplates.common.dependency; import com.google.gson.JsonElement; /** - * Applies CustomNameplates specific behaviour for {@link Dependency}s. + * Applies CustomFishing specific behaviour for {@link Dependency}s. */ public class DependencyRegistry { diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyRepository.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyRepository.java similarity index 84% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyRepository.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyRepository.java index 1b98d27..646499f 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/DependencyRepository.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/DependencyRepository.java @@ -23,9 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies; - -import com.google.common.io.ByteStreams; +package net.momirealms.customnameplates.common.dependency; import java.io.IOException; import java.io.InputStream; @@ -37,27 +35,28 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.concurrent.TimeUnit; /** * Represents a repository which contains {@link Dependency}s. */ public enum DependencyRepository { + /** + * Maven Central + */ MAVEN_CENTRAL("maven", "https://repo1.maven.org/maven2/") { @Override protected URLConnection openConnection(Dependency dependency) throws IOException { URLConnection connection = super.openConnection(dependency); - connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(5)); - connection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(5)); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); return connection; } }, - /** - * Maven Central + * Maven Central Mirror */ - MAVEN_CENTRAL_MIRROR("aliyun", "https://maven.aliyun.com/repository/public/"); + MAVEN_CENTRAL_MIRROR("maven", "https://maven.aliyun.com/repository/public/"); private final String url; private final String id; @@ -71,21 +70,18 @@ public enum DependencyRepository { return url; } - public static List repos() { - ArrayList list = new ArrayList<>(List.of(values())); - if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) { - Collections.reverse(list); - } - return list; - } - - public static DependencyRepository getByID(String id) { + public static List getByID(String id) { + ArrayList repositories = new ArrayList<>(); for (DependencyRepository repository : values()) { if (id.equals(repository.id)) { - return repository; + repositories.add(repository); } } - return null; + // 中国大陆优先使用国内阿里云镜像 + if (id.equals("maven") && Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) { + Collections.reverse(repositories); + } + return repositories; } /** @@ -111,7 +107,7 @@ public enum DependencyRepository { try { URLConnection connection = openConnection(dependency); try (InputStream in = connection.getInputStream()) { - byte[] bytes = ByteStreams.toByteArray(in); + byte[] bytes = in.readAllBytes(); if (bytes.length == 0) { throw new DependencyDownloadException("Empty stream"); } diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/classloader/IsolatedClassLoader.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/classloader/IsolatedClassLoader.java similarity index 92% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/classloader/IsolatedClassLoader.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/classloader/IsolatedClassLoader.java index bdb9c99..c38f03f 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/classloader/IsolatedClassLoader.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/classloader/IsolatedClassLoader.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies.classloader; +package net.momirealms.customnameplates.common.dependency.classloader; import java.net.URL; import java.net.URLClassLoader; @@ -31,7 +31,7 @@ import java.net.URLClassLoader; /** * A classloader "isolated" from the rest of the Minecraft server. * - *

Used to load specific CustomNameplates dependencies without causing conflicts + *

Used to load specific CustomFishing dependencies without causing conflicts * with other plugins, or libraries provided by the server implementation.

*/ public class IsolatedClassLoader extends URLClassLoader { diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/Relocation.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java similarity index 96% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/Relocation.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java index ced09e4..071138c 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/Relocation.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies.relocation; +package net.momirealms.customnameplates.common.dependency.relocation; import java.util.Objects; diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/RelocationHandler.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/RelocationHandler.java similarity index 88% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/RelocationHandler.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/RelocationHandler.java index c93d411..ed50a60 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/RelocationHandler.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/RelocationHandler.java @@ -23,11 +23,11 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies.relocation; +package net.momirealms.customnameplates.common.dependency.relocation; -import net.momirealms.customnameplates.paper.libraries.dependencies.Dependency; -import net.momirealms.customnameplates.paper.libraries.dependencies.DependencyManager; -import net.momirealms.customnameplates.paper.libraries.dependencies.classloader.IsolatedClassLoader; +import net.momirealms.customnameplates.common.dependency.Dependency; +import net.momirealms.customnameplates.common.dependency.DependencyManager; +import net.momirealms.customnameplates.common.dependency.classloader.IsolatedClassLoader; import java.io.File; import java.io.IOException; @@ -66,8 +66,8 @@ public class RelocationHandler { this.jarRelocatorRunMethod.setAccessible(true); } catch (Exception e) { try { - if (classLoader instanceof IsolatedClassLoader) { - ((IsolatedClassLoader) classLoader).close(); + if (classLoader instanceof IsolatedClassLoader isolatedClassLoader) { + isolatedClassLoader.close(); } } catch (IOException ex) { e.addSuppressed(ex); diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/RelocationHelper.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/RelocationHelper.java similarity index 94% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/RelocationHelper.java rename to common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/RelocationHelper.java index 209e8b5..417b2dd 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/relocation/RelocationHelper.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/RelocationHelper.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.dependencies.relocation; +package net.momirealms.customnameplates.common.dependency.relocation; public final class RelocationHelper { @@ -32,6 +32,5 @@ public final class RelocationHelper { public static final String OKHTTP3_STRING = String.valueOf(new char[]{'o', 'k', 'h', 't', 't', 'p', '3'}); private RelocationHelper() { - } } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/Cancellable.java b/common/src/main/java/net/momirealms/customnameplates/common/event/Cancellable.java new file mode 100644 index 0000000..cee9be6 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/Cancellable.java @@ -0,0 +1,14 @@ +package net.momirealms.customnameplates.common.event; + +public interface Cancellable { + + /** + * Gets the cancelled state. + */ + boolean cancelled(); + + /** + * Sets the cancelled state. + */ + void cancelled(final boolean cancelled); +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventConfig.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventConfig.java new file mode 100644 index 0000000..a4a0340 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventConfig.java @@ -0,0 +1,143 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event; + +public interface EventConfig { + + /** + * The default value for {@link #order()}. + * + */ + int DEFAULT_ORDER = 0; + + /** + * The default value for {@link #acceptsCancelled()}. + * + */ + boolean DEFAULT_ACCEPTS_CANCELLED = true; + + /** + * The default value for {@link #exact()}. + * + */ + boolean DEFAULT_EXACT = false; + + /** + * Gets the post order. + * + * @return the post order + */ + int order(); + + /** + * Sets the post order. + * + * @param order the post order + * @return an {@link EventConfig} + */ + EventConfig order(final int order); + + /** + * Gets if cancelled events are accepted. + * + * @return if cancelled events are accepted + */ + boolean acceptsCancelled(); + + /** + * Sets if cancelled events are accepted. + * + * @param acceptsCancelled if cancelled events are accepted + * @return an {@link EventConfig} + */ + EventConfig acceptsCancelled(final boolean acceptsCancelled); + + /** + * Gets if only the exact event type is accepted. + * + * @return if only the exact event type is accepted + */ + boolean exact(); + + /** + * Sets if only the exact event type is accepted. + * + * @param exact if only the exact event type is accepted + * @return an {@link EventConfig} + */ + EventConfig exact(final boolean exact); + + static EventConfig defaults() { + return EventConfigImpl.DEFAULTS; + } + + static EventConfig of( + final int order, + final boolean acceptsCancelled, + final boolean exact + ) { + return new EventConfigImpl(order, acceptsCancelled, exact); + } + + static Builder builder() { + return new EventConfigImpl.BuilderImpl(); + } + + /** + * Builder. + */ + interface Builder { + /** + * Sets the post order. + * + * @param order the post order + * @return {@code this} + */ + Builder order(final int order); + + /** + * Sets if cancelled events are accepted. + * + * @param acceptsCancelled if cancelled events are accepted + * @return {@code this} + */ + Builder acceptsCancelled(final boolean acceptsCancelled); + + /** + * Sets if only the exact event type is accepted. + * + * @param exact if only the exact event type is accepted + * @return {@code this} + */ + Builder exact(final boolean exact); + + /** + * Builds. + * + * @return an {@link EventConfig} + */ + EventConfig build(); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventConfigImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventConfigImpl.java new file mode 100644 index 0000000..7caddf8 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventConfigImpl.java @@ -0,0 +1,88 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event; + +record EventConfigImpl( + int order, + boolean acceptsCancelled, + boolean exact +) implements EventConfig { + static final EventConfigImpl DEFAULTS = new EventConfigImpl(DEFAULT_ORDER, DEFAULT_ACCEPTS_CANCELLED, DEFAULT_EXACT); + + static EventConfigImpl create( + final int order, + final boolean acceptsCancelled, + final boolean exact + ) { + if (order == DEFAULT_ORDER && acceptsCancelled == DEFAULT_ACCEPTS_CANCELLED && exact == DEFAULT_EXACT) { + return DEFAULTS; + } + return new EventConfigImpl(order, acceptsCancelled, exact); + } + + @Override + public EventConfig order(final int order) { + return create(order, this.acceptsCancelled, this.exact); + } + + @Override + public EventConfig acceptsCancelled(final boolean acceptsCancelled) { + return create(this.order, acceptsCancelled, this.exact); + } + + @Override + public EventConfig exact(final boolean exact) { + return create(this.order, this.acceptsCancelled, exact); + } + + static final class BuilderImpl implements Builder { + private int order = DEFAULT_ORDER; + private boolean acceptsCancelled = DEFAULT_ACCEPTS_CANCELLED; + private boolean exact = DEFAULT_EXACT; + + @Override + public Builder order(final int order) { + this.order = order; + return this; + } + + @Override + public Builder acceptsCancelled(final boolean acceptsCancelled) { + this.acceptsCancelled = acceptsCancelled; + return this; + } + + @Override + public Builder exact(final boolean exact) { + this.exact = exact; + return this; + } + + @Override + public EventConfig build() { + return create(this.order, this.acceptsCancelled, this.exact); + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventConsumer.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventConsumer.java new file mode 100644 index 0000000..793332a --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventConsumer.java @@ -0,0 +1,34 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event; + +@FunctionalInterface +public interface EventConsumer { + + /** + * Invokes this event consumer. + */ + void onEvent(final E event) throws Throwable; +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventManager.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventManager.java new file mode 100644 index 0000000..02d515f --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventManager.java @@ -0,0 +1,31 @@ +package net.momirealms.customnameplates.common.event; + +import net.momirealms.customnameplates.common.event.bus.EventBus; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; + +import java.util.OptionalInt; + +public interface EventManager { + + class SingletonHolder { + private static EventManager INSTANCE = null; + } + + static EventManager create(NameplatesPlugin plugin) { + if (SingletonHolder.INSTANCE == null) { + SingletonHolder.INSTANCE = new EventManagerImpl(plugin); + } + return SingletonHolder.INSTANCE; + } + + EventSubscription subscribe(Class event, EventSubscriber subscriber); + + EventSubscription subscribe(Class event, EventConfig config, EventSubscriber subscriber); + + NameplatesEvent dispatch(Class eventClass, Object... params); + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + NameplatesEvent dispatch(Class eventClass, OptionalInt order, Object... params); + + EventBus getEventBus(); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventManagerImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventManagerImpl.java new file mode 100644 index 0000000..4554eff --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventManagerImpl.java @@ -0,0 +1,65 @@ +package net.momirealms.customnameplates.common.event; + +import net.momirealms.customnameplates.common.event.bus.EventBus; +import net.momirealms.customnameplates.common.event.bus.SimpleEventBus; +import net.momirealms.customnameplates.common.event.gen.EventGenerator; +import net.momirealms.customnameplates.common.event.registry.EventRegistry; +import net.momirealms.customnameplates.common.event.registry.SimpleEventRegistry; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; + +import java.util.OptionalInt; + +public class EventManagerImpl implements EventManager { + + private final EventBus eventBus; + private final EventRegistry registry; + private final NameplatesPlugin plugin; + + protected EventManagerImpl(NameplatesPlugin plugin) { + this.plugin = plugin; + this.registry = new SimpleEventRegistry<>(NameplatesEvent.class); + this.eventBus = new SimpleEventBus<>(registry, new EventBus.EventExceptionHandler() { + @Override + public void eventExceptionCaught(EventBus bus, EventSubscription subscription, E event, Throwable throwable) { + plugin.getPluginLogger().severe("Exception caught in event handler", throwable); + } + }); + } + + @Override + public EventSubscription subscribe(final Class event, final EventSubscriber subscriber) { + return registry.subscribe(event, subscriber); + } + + @Override + public EventSubscription subscribe(final Class event, EventConfig config, final EventSubscriber subscriber) { + return registry.subscribe(event, config, subscriber); + } + + @Override + public NameplatesEvent dispatch(Class eventClass, Object... params) { + NameplatesEvent event = generate(eventClass, params); + this.eventBus.post(event, OptionalInt.empty()); + return event; + } + + @Override + public NameplatesEvent dispatch(Class eventClass, OptionalInt order, Object... params) { + NameplatesEvent event = generate(eventClass, params); + this.eventBus.post(event, order); + return event; + } + + @Override + public EventBus getEventBus() { + return this.eventBus; + } + + private NameplatesEvent generate(Class eventClass, Object... params) { + try { + return EventGenerator.generate(eventClass).newInstance(this.plugin, params); + } catch (Throwable e) { + throw new RuntimeException("Exception occurred whilst generating event instance", e); + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventSubscriber.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventSubscriber.java new file mode 100644 index 0000000..93d7499 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventSubscriber.java @@ -0,0 +1,28 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event; + +public interface EventSubscriber extends EventConsumer { +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/EventSubscription.java b/common/src/main/java/net/momirealms/customnameplates/common/event/EventSubscription.java new file mode 100644 index 0000000..3a9d605 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/EventSubscription.java @@ -0,0 +1,50 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event; + +public interface EventSubscription { + + /** + * Gets the event type. + */ + Class event(); + + /** + * Gets the configuration. + */ + EventConfig config(); + + /** + * Gets the subscriber. + */ + EventSubscriber subscriber(); + + /** + * Disposes this subscription. + * + *

The subscriber held by this subscription will no longer receive events.

+ */ + void dispose(); +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/NameplatesEvent.java b/common/src/main/java/net/momirealms/customnameplates/common/event/NameplatesEvent.java new file mode 100644 index 0000000..ba12348 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/NameplatesEvent.java @@ -0,0 +1,21 @@ +package net.momirealms.customnameplates.common.event; + +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import org.jetbrains.annotations.NotNull; + +public interface NameplatesEvent { + + /** + * Get the plugin instance this event was dispatched from + */ + @NotNull + NameplatesPlugin plugin(); + + /** + * Gets the type of the event. + * + * @return the type of the event + */ + @NotNull + Class eventType(); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/Param.java b/common/src/main/java/net/momirealms/customnameplates/common/event/Param.java new file mode 100644 index 0000000..84853ac --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/Param.java @@ -0,0 +1,49 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Represents the position of a parameter within an event. + * + *

This is an implementation detail and should not be relied upon.

+ */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Param { + + /** + * Gets the index of the parameter. + * + * @return the index + */ + int value(); + +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/bus/EventBus.java b/common/src/main/java/net/momirealms/customnameplates/common/event/bus/EventBus.java new file mode 100644 index 0000000..6f0e00d --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/bus/EventBus.java @@ -0,0 +1,47 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event.bus; + +import net.momirealms.customnameplates.common.event.EventSubscription; + +import java.util.OptionalInt; + +public interface EventBus { + + default void post(final E event) { + this.post(event, OptionalInt.empty()); + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + void post(final E event, final OptionalInt order); + + @FunctionalInterface + interface EventExceptionHandler { + /** + * Handles a caught exception. + */ + void eventExceptionCaught(final EventBus bus, final EventSubscription subscription, final E event, final Throwable throwable); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/bus/SimpleEventBus.java b/common/src/main/java/net/momirealms/customnameplates/common/event/bus/SimpleEventBus.java new file mode 100644 index 0000000..e45a2b9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/bus/SimpleEventBus.java @@ -0,0 +1,106 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event.bus; + +import net.momirealms.customnameplates.common.event.Cancellable; +import net.momirealms.customnameplates.common.event.EventConfig; +import net.momirealms.customnameplates.common.event.EventSubscription; +import net.momirealms.customnameplates.common.event.registry.EventRegistry; + +import java.util.List; +import java.util.OptionalInt; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class SimpleEventBus implements EventBus { + protected final EventRegistry registry; + protected final EventExceptionHandler exceptions; + + /** + * Constructs a new {@code SimpleEventBus}. + * + * @param registry the event registry + * @param exceptions the event exception handler + */ + public SimpleEventBus(final EventRegistry registry, final EventBus.EventExceptionHandler exceptions) { + this.registry = requireNonNull(registry, "registry"); + this.exceptions = requireNonNull(exceptions, "exceptions"); + } + + @Override + public void post(final E event, final OptionalInt order) { + @SuppressWarnings("unchecked") + final Class type = (Class) event.getClass(); + final List> subscriptions = this.registry.subscriptions(type); + if (subscriptions.isEmpty()) { + return; + } + for (final EventSubscription subscription : subscriptions) { + if (this.accepts(subscription, event, order)) { + try { + subscription.subscriber().onEvent(event); + } catch (final Throwable t) { + this.exceptions.eventExceptionCaught(this, subscription, event, t); + } + } + } + } + + @SuppressWarnings("RedundantIfStatement") + protected boolean accepts(final EventSubscription subscription, final E event, final OptionalInt order) { + final EventConfig config = subscription.config(); + + if (config.exact()) { + if (event.getClass() != subscription.event()) { + return false; + } + } + + if (order.isPresent()) { + if (config.order() != order.getAsInt()) { + return false; + } + } + + if (!config.acceptsCancelled()) { + if (this.currentlyCancelled(event)) { + return false; + } + } + + return true; + } + + /** + * Checks if {@code event} is cancelled. + * + * @param event the event + * @return {@code true} if the event is cancelled, {@code false} otherwise + */ + protected boolean currentlyCancelled(final E event) { + return event instanceof Cancellable && ((Cancellable) event).cancelled(); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/gen/AbstractNameplatesEvent.java b/common/src/main/java/net/momirealms/customnameplates/common/event/gen/AbstractNameplatesEvent.java new file mode 100644 index 0000000..835d1e0 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/gen/AbstractNameplatesEvent.java @@ -0,0 +1,26 @@ +package net.momirealms.customnameplates.common.event.gen; + +import net.momirealms.customnameplates.common.event.NameplatesEvent; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import org.jetbrains.annotations.NotNull; + +import java.lang.invoke.MethodHandles; + +public abstract class AbstractNameplatesEvent implements NameplatesEvent { + + private final NameplatesPlugin plugin; + + protected AbstractNameplatesEvent(NameplatesPlugin plugin) { + this.plugin = plugin; + } + + @NotNull + @Override + public NameplatesPlugin plugin() { + return plugin; + } + + public MethodHandles.Lookup mhl() { + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/gen/EventGenerator.java b/common/src/main/java/net/momirealms/customnameplates/common/event/gen/EventGenerator.java new file mode 100644 index 0000000..f4531cb --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/gen/EventGenerator.java @@ -0,0 +1,177 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event.gen; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.ClassFileVersion; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.FixedValue; +import net.bytebuddy.implementation.MethodCall; +import net.momirealms.customnameplates.common.event.Cancellable; +import net.momirealms.customnameplates.common.event.NameplatesEvent; +import net.momirealms.customnameplates.common.event.Param; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import static net.bytebuddy.matcher.ElementMatchers.*; + +public class EventGenerator { + + /** + * A loading cache of event types to {@link EventGenerator}s. + */ + private static final Map, EventGenerator> CACHE = new HashMap<>(); + + /** + * Generate a {@link EventGenerator} for the given {@code event} type. + * + * @param event the event type + * @return the generated class + */ + public static EventGenerator generate(Class event) { + if (CACHE.containsKey(event)) { + return CACHE.get(event); + } else { + try { + var generator = new EventGenerator(event); + CACHE.put(event, generator); + return generator; + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + } + + /** + * A method handle for the constructor of the event class. + */ + private final MethodHandle constructor; + + /** + * An array of {@link MethodHandle}s, which can set values for each of the properties in the event class. + */ + private final MethodHandle[] setters; + + private EventGenerator(Class eventClass) throws Throwable { + // get a TypeDescription for the event class + TypeDescription eventClassType = new TypeDescription.ForLoadedType(eventClass); + + // determine a generated class name of the event + String eventClassSuffix = eventClass.getName().substring(NameplatesEvent.class.getPackage().getName().length()); + String packageWithName = EventGenerator.class.getName(); + String generatedClassName = packageWithName.substring(0, packageWithName.lastIndexOf('.')) + eventClassSuffix; + + DynamicType.Builder builder = new ByteBuddy(ClassFileVersion.JAVA_V17) + // create a subclass of AbstractEvent + .subclass(AbstractNameplatesEvent.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING) + // using the predetermined generated class name + .name(generatedClassName) + // implement the event interface + .implement(eventClassType) + // implement all methods annotated with Param by simply returning the value from the corresponding field with the same name + .method(isAnnotatedWith(Param.class)) + .intercept(FieldAccessor.of(NamedElement.WithRuntimeName::getInternalName)) + // implement NameplatesEvent#eventType by returning the event class type + .method(named("eventType").and(returns(Class.class)).and(takesArguments(0))) + .intercept(FixedValue.value(eventClassType)) + // implement AbstractEvent#mh by calling & returning the value of MethodHandles.lookup() + .method(named("mhl").and(returns(MethodHandles.Lookup.class)).and(takesArguments(0))) + .intercept(MethodCall.invoke(MethodHandles.class.getMethod("lookup"))) + // implement a toString method + .withToString(); + + if (Cancellable.class.isAssignableFrom(eventClass)) { + builder = builder + .defineField("cancelled", boolean.class, Visibility.PRIVATE) + .method(named("cancelled").and(returns(boolean.class)).and(takesArguments(0))) + .intercept(FieldAccessor.ofField("cancelled")) + .method(named("cancelled").and(takesArguments(boolean.class))) + .intercept(FieldAccessor.ofField("cancelled")); + } + + // get a sorted array of all methods on the event interface annotated with @Param + Method[] properties = Arrays.stream(eventClass.getMethods()) + .filter(m -> m.isAnnotationPresent(Param.class)) + .sorted(Comparator.comparingInt(o -> o.getAnnotation(Param.class).value())) + .toArray(Method[]::new); + + // for each property, define a field on the generated class to hold the value + for (Method method : properties) { + builder = builder.defineField(method.getName(), method.getReturnType(), Visibility.PRIVATE); + } + + // finish building, load the class, get a constructor + Class generatedClass = builder.make().load(EventGenerator.class.getClassLoader()).getLoaded(); + this.constructor = MethodHandles.publicLookup().in(generatedClass) + .findConstructor(generatedClass, MethodType.methodType(void.class, NameplatesPlugin.class)) + .asType(MethodType.methodType(AbstractNameplatesEvent.class, NameplatesPlugin.class)); + + // create a dummy instance of the generated class & get the method handle lookup instance + MethodHandles.Lookup lookup = ((AbstractNameplatesEvent) this.constructor.invoke((Object) null)).mhl(); + + // get 'setter' MethodHandles for each property + this.setters = new MethodHandle[properties.length]; + for (int i = 0; i < properties.length; i++) { + Method method = properties[i]; + this.setters[i] = lookup.findSetter(generatedClass, method.getName(), method.getReturnType()) + .asType(MethodType.methodType(void.class, new Class[]{AbstractNameplatesEvent.class, Object.class})); + } + } + + /** + * Creates a new instance of the event class. + */ + public NameplatesEvent newInstance(NameplatesPlugin plugin, Object... properties) throws Throwable { + if (properties.length != this.setters.length) { + throw new IllegalStateException("Unexpected number of properties. given: " + properties.length + ", expected: " + this.setters.length); + } + + // create a new instance of the event + final AbstractNameplatesEvent event = (AbstractNameplatesEvent) this.constructor.invokeExact(plugin); + + // set the properties onto the event instance + for (int i = 0; i < this.setters.length; i++) { + MethodHandle setter = this.setters[i]; + Object value = properties[i]; + setter.invokeExact(event, value); + } + + return event; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/registry/EventRegistry.java b/common/src/main/java/net/momirealms/customnameplates/common/event/registry/EventRegistry.java new file mode 100644 index 0000000..991c48d --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/registry/EventRegistry.java @@ -0,0 +1,87 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event.registry; + +import net.momirealms.customnameplates.common.event.EventConfig; +import net.momirealms.customnameplates.common.event.EventSubscriber; +import net.momirealms.customnameplates.common.event.EventSubscription; + +import java.util.List; +import java.util.function.Predicate; + +public interface EventRegistry { + + /** + * Gets the base event type. + * + *

This is represented by the {@code E} type parameter.

+ */ + Class type(); + + /** + * Determines whether the specified event has subscribers. + * + * @param event the event type + * @return whether the specified event has subscribers + */ + default boolean subscribed(final Class event) { + return !this.subscriptions(event).isEmpty(); + } + + /** + * Registers the given {@code subscriber} to receive events, using the default {@link EventConfig configuration}. + * + * @param event the event type + * @param subscriber the subscriber + * @param the event type + */ + default EventSubscription subscribe(final Class event, final EventSubscriber subscriber) { + return this.subscribe(event, EventConfig.defaults(), subscriber); + } + + /** + * Registers the given {@code subscriber} to receive events. + * + * @param event the event type + * @param config the event configuration + * @param subscriber the subscriber + * @param the event type + */ + EventSubscription subscribe(final Class event, final EventConfig config, final EventSubscriber subscriber); + + /** + * Removes subscriptions matching {@code predicate}. + * + * @param predicate the predicate used to determine which subscriptions to remove + */ + void unsubscribeIf(final Predicate> predicate); + + /** + * Gets an unmodifiable list containing all subscriptions currently registered for events of type {@code event}. + * + * @return a list of all subscriptions for events of type {@code event} + */ + List> subscriptions(final Class event); +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/registry/Internals.java b/common/src/main/java/net/momirealms/customnameplates/common/event/registry/Internals.java new file mode 100644 index 0000000..3a649dc --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/registry/Internals.java @@ -0,0 +1,54 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event.registry; + +import java.util.ArrayList; +import java.util.List; + +final class Internals { + private Internals() { + } + + @SuppressWarnings("unchecked") + static List> ancestors(final Class type) { + final List> types = new ArrayList<>(); + types.add(type); + for (int i = 0; i < types.size(); i++) { + final Class next = types.get(i); + final Class superclass = next.getSuperclass(); + if (superclass != null) { + types.add((Class) superclass); + } + final Class[] interfaces = next.getInterfaces(); + for (final Class iface : interfaces) { + // we have a list because we want to preserve order, but we don't want duplicates + if (!types.contains(iface)) { + types.add((Class) iface); + } + } + } + return types; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/event/registry/SimpleEventRegistry.java b/common/src/main/java/net/momirealms/customnameplates/common/event/registry/SimpleEventRegistry.java new file mode 100644 index 0000000..294761d --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/event/registry/SimpleEventRegistry.java @@ -0,0 +1,164 @@ +/* + * This file is part of event, licensed under the MIT License. + * + * Copyright (c) 2021-2023 Seiama + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.event.registry; + +import net.momirealms.customnameplates.common.event.EventConfig; +import net.momirealms.customnameplates.common.event.EventSubscriber; +import net.momirealms.customnameplates.common.event.EventSubscription; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; + +public class SimpleEventRegistry implements EventRegistry { + private static final Comparator> ORDER_COMPARATOR = Comparator.comparingInt(subscription -> subscription.config().order()); + + private final Map, Collection>> classes = new HashMap<>(); + + private final Map, List>> unbaked = new HashMap<>(); + private final Map, List>> baked = new HashMap<>(); + + private final Object lock = new Object(); + + private final Class type; + + /** + * Constructs a new {@code SimpleEventRegistry}. + * + * @param type the base event type + */ + public SimpleEventRegistry(final Class type) { + this.type = requireNonNull(type, "type"); + } + + @Override + public Class type() { + return this.type; + } + + @Override + public EventSubscription subscribe(final Class event, final EventConfig config, final EventSubscriber subscriber) { + requireNonNull(event, "event"); + requireNonNull(config, "config"); + requireNonNull(subscriber, "subscriber"); + final EventSubscription subscription = new EventSubscriptionImpl<>(event, config, subscriber); + synchronized (this.lock) { + final List> subscriptions = yayGenerics(this.unbaked.computeIfAbsent(event, key -> new ArrayList<>())); + subscriptions.add(subscription); + this.baked.clear(); + } + return subscription; + } + + @Override + public void unsubscribeIf(final Predicate> predicate) { + synchronized (this.lock) { + boolean removedAny = false; + for (final List> subscriptions : this.unbaked.values()) { + removedAny |= subscriptions.removeIf(predicate); + } + if (removedAny) { + this.baked.clear(); + } + } + } + + @Override + public List> subscriptions(final Class event) { + synchronized (this.lock) { + return this.baked.computeIfAbsent(event, this::computeSubscriptions); + } + } + + private List> computeSubscriptions(final Class event) { + final List> subscriptions = new ArrayList<>(); + final Collection> types = this.classes.computeIfAbsent(event, this::findClasses); + for (final Class type : types) { + subscriptions.addAll(this.unbaked.getOrDefault(type, Collections.emptyList())); + } + subscriptions.sort(ORDER_COMPARATOR); + return subscriptions; + } + + private Collection> findClasses(final Class type) { + final Collection> classes = Internals.ancestors(type); + classes.removeIf(klass -> !this.type.isAssignableFrom(klass)); + return classes; + } + + @SuppressWarnings("unchecked") + private static List yayGenerics(final List list) { + return (List) list; + } + + private class EventSubscriptionImpl implements EventSubscription { + private final Class event; + private final EventConfig config; + private final EventSubscriber subscriber; + + EventSubscriptionImpl(final Class event, final EventConfig config, final EventSubscriber subscriber) { + this.event = event; + this.config = config; + this.subscriber = subscriber; + } + + @Override + public Class event() { + return this.event; + } + + @Override + public EventConfig config() { + return this.config; + } + + @Override + public EventSubscriber subscriber() { + return this.subscriber; + } + + @Override + public void dispose() { + synchronized (SimpleEventRegistry.this.lock) { + final @Nullable List> subscriptions = yayGenerics(SimpleEventRegistry.this.unbaked.get(this.event)); + if (subscriptions != null) { + subscriptions.remove(this); + SimpleEventRegistry.this.baked.clear(); + } + } + } + + @Override + public String toString() { + return new StringJoiner(", ", this.getClass().getSimpleName() + "[", "]") + .add("event=" + this.event) + .add("config=" + this.config) + .add("subscriber=" + this.subscriber) + .toString(); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionFormatter.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionFormatter.java new file mode 100644 index 0000000..14b7c51 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionFormatter.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.caption.CaptionVariable; +import org.incendo.cloud.minecraft.extras.caption.ComponentCaptionFormatter; + +import java.util.List; + +public class CustomNameplatesCaptionFormatter implements ComponentCaptionFormatter { + + @Override + public @NonNull Component formatCaption(@NonNull Caption captionKey, @NonNull C recipient, @NonNull String caption, @NonNull List<@NonNull CaptionVariable> variables) { + Component component = ComponentCaptionFormatter.translatable().formatCaption(captionKey, recipient, caption, variables); + return TranslationManager.render(component); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionKeys.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionKeys.java new file mode 100644 index 0000000..91ffab9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionKeys.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import org.incendo.cloud.caption.Caption; + +public final class CustomNameplatesCaptionKeys { + + public static final Caption ARGUMENT_PARSE_FAILURE_TIME = Caption.of("argument.parse.failure.time"); + public static final Caption ARGUMENT_PARSE_FAILURE_URL = Caption.of("argument.parse.failure.url"); + public static final Caption ARGUMENT_PARSE_FAILURE_NAMEDTEXTCOLOR = Caption.of("argument.parse.failure.namedtextcolor"); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionProvider.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionProvider.java new file mode 100644 index 0000000..6d6b84b --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/CustomNameplatesCaptionProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.CaptionProvider; +import org.incendo.cloud.caption.DelegatingCaptionProvider; + +public final class CustomNameplatesCaptionProvider extends DelegatingCaptionProvider { + + private static final CaptionProvider PROVIDER = CaptionProvider.constantProvider() + .putCaption(CustomNameplatesCaptionKeys.ARGUMENT_PARSE_FAILURE_URL, "") + .putCaption(CustomNameplatesCaptionKeys.ARGUMENT_PARSE_FAILURE_TIME, "") + .putCaption(CustomNameplatesCaptionKeys.ARGUMENT_PARSE_FAILURE_NAMEDTEXTCOLOR, "") + .build(); + + @SuppressWarnings("unchecked") + @Override + public @NonNull CaptionProvider delegate() { + return (CaptionProvider) PROVIDER; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/MessageConstants.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/MessageConstants.java new file mode 100644 index 0000000..1a5058d --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/MessageConstants.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; + +public interface MessageConstants { + + TranslatableComponent.Builder COMMAND_RELOAD_SUCCESS = Component.translatable().key("command.reload.success"); + TranslatableComponent.Builder COMMAND_RELOAD_GENERATING = Component.translatable().key("command.reload.generating"); + TranslatableComponent.Builder COMMAND_RELOAD_GENERATED = Component.translatable().key("command.reload.generated"); + TranslatableComponent.Builder COMMAND_DEBUG_PERFORMANCE = Component.translatable().key("command.debug.performance"); + + TranslatableComponent.Builder COMMAND_NAMEPLATES_EQUIP_FAILURE_NOT_EXISTS = Component.translatable().key("command.nameplates.equip.failure.not_exists"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_EQUIP_FAILURE_PERMISSION = Component.translatable().key("command.nameplates.equip.failure.permission"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_EQUIP_FAILURE_NO_CHANGE = Component.translatable().key("command.nameplates.equip.failure.no_change"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_EQUIP_SUCCESS = Component.translatable().key("command.nameplates.equip.success"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_UNEQUIP_FAILURE_NOT_EQUIP = Component.translatable().key("command.nameplates.unequip.failure.not_equip"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_UNEQUIP_SUCCESS = Component.translatable().key("command.nameplates.unequip.success"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_PREVIEW_FAILURE_COOLDOWN = Component.translatable().key("command.nameplates.preview.failure.cooldown"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_PREVIEW_SUCCESS = Component.translatable().key("command.nameplates.preview.success"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_LIST_FAILURE_NONE = Component.translatable().key("command.nameplates.list.failure.none"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_LIST_SUCCESS = Component.translatable().key("command.nameplates.list.success"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_LIST_DELIMITER = Component.translatable().key("command.nameplates.list.delimiter"); + + TranslatableComponent.Builder COMMAND_NAMEPLATES_FORCE_EQUIP_SUCCESS = Component.translatable().key("command.nameplates.force_equip.success"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_FORCE_EQUIP_FAILURE_NOT_EXISTS = Component.translatable().key("command.nameplates.force_equip.failure.not_exists"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_FORCE_UNEQUIP_SUCCESS = Component.translatable().key("command.nameplates.force_unequip.success"); + TranslatableComponent.Builder COMMAND_NAMEPLATES_FORCE_PREVIEW_SUCCESS = Component.translatable().key("command.nameplates.force_preview.success"); + + TranslatableComponent.Builder COMMAND_BUBBLES_EQUIP_FAILURE_NOT_EXISTS = Component.translatable().key("command.bubbles.equip.failure.not_exists"); + TranslatableComponent.Builder COMMAND_BUBBLES_EQUIP_FAILURE_PERMISSION = Component.translatable().key("command.bubbles.equip.failure.permission"); + TranslatableComponent.Builder COMMAND_BUBBLES_EQUIP_FAILURE_NO_CHANGE = Component.translatable().key("command.bubbles.equip.failure.no_change"); + TranslatableComponent.Builder COMMAND_BUBBLES_EQUIP_SUCCESS = Component.translatable().key("command.bubbles.equip.success"); + TranslatableComponent.Builder COMMAND_BUBBLES_UNEQUIP_FAILURE_NOT_EQUIP = Component.translatable().key("command.bubbles.unequip.failure.not_equip"); + TranslatableComponent.Builder COMMAND_BUBBLES_UNEQUIP_SUCCESS = Component.translatable().key("command.bubbles.unequip.success"); + TranslatableComponent.Builder COMMAND_BUBBLES_LIST_FAILURE_NONE = Component.translatable().key("command.bubbles.list.failure.none"); + TranslatableComponent.Builder COMMAND_BUBBLES_LIST_SUCCESS = Component.translatable().key("command.bubbles.list.success"); + TranslatableComponent.Builder COMMAND_BUBBLES_LIST_DELIMITER = Component.translatable().key("command.bubbles.list.delimiter"); + + TranslatableComponent.Builder COMMAND_BUBBLES_FORCE_EQUIP_SUCCESS = Component.translatable().key("command.bubbles.force_equip.success"); + TranslatableComponent.Builder COMMAND_BUBBLES_FORCE_EQUIP_FAILURE_NOT_EXISTS = Component.translatable().key("command.bubbles.force_equip.failure.not_exists"); + TranslatableComponent.Builder COMMAND_BUBBLES_FORCE_UNEQUIP_SUCCESS = Component.translatable().key("command.bubbles.force_unequip.success"); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslationRegistry.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslationRegistry.java new file mode 100644 index 0000000..e8bd1e5 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslationRegistry.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.translation.Translator; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public interface MiniMessageTranslationRegistry extends Translator { + + static @NotNull MiniMessageTranslationRegistry create(final Key name, final MiniMessage miniMessage) { + return new MiniMessageTranslationRegistryImpl(requireNonNull(name, "name"), requireNonNull(miniMessage, "MiniMessage")); + } + + void register(@NotNull String key, @NotNull Locale locale, @NotNull String format); + + void unregister(@NotNull String key); + + boolean contains(@NotNull String key); + + String miniMessageTranslation(@NotNull String key, @NotNull Locale locale); + + void defaultLocale(@NotNull Locale defaultLocale); + + default void registerAll(final @NotNull Locale locale, final @NotNull Map bundle) { + this.registerAll(locale, bundle.keySet(), bundle::get); + } + + default void registerAll(final @NotNull Locale locale, final @NotNull Set keys, final Function function) { + IllegalArgumentException firstError = null; + int errorCount = 0; + for (final String key : keys) { + try { + this.register(key, locale, function.apply(key)); + } catch (final IllegalArgumentException e) { + if (firstError == null) { + firstError = e; + } + errorCount++; + } + } + if (firstError != null) { + if (errorCount == 1) { + throw firstError; + } else if (errorCount > 1) { + throw new IllegalArgumentException(String.format("Invalid key (and %d more)", errorCount - 1), firstError); + } + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslationRegistryImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslationRegistryImpl.java new file mode 100644 index 0000000..872ce5b --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslationRegistryImpl.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import net.kyori.adventure.internal.Internals; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.minimessage.Context; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.ParsingException; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.kyori.adventure.util.TriState; +import net.kyori.examination.Examinable; +import net.kyori.examination.ExaminableProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +public class MiniMessageTranslationRegistryImpl implements Examinable, MiniMessageTranslationRegistry { + private final Key name; + private final Map translations = new ConcurrentHashMap<>(); + private Locale defaultLocale = Locale.US; + private final MiniMessage miniMessage; + + MiniMessageTranslationRegistryImpl(final Key name, final MiniMessage miniMessage) { + this.name = name; + this.miniMessage = miniMessage; + } + + @Override + public void register(final @NotNull String key, final @NotNull Locale locale, final @NotNull String format) { + this.translations.computeIfAbsent(key, Translation::new).register(locale, format); + } + + @Override + public void unregister(final @NotNull String key) { + this.translations.remove(key); + } + + @Override + public boolean contains(final @NotNull String key) { + return this.translations.containsKey(key); + } + + @Override + public @NotNull Key name() { + return name; + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + // No need to implement this method + return null; + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + Translation translation = translations.get(component.key()); + if (translation == null) { + return null; + } + String miniMessageString = translation.translate(locale); + if (miniMessageString == null) { + return null; + } + if (miniMessageString.isEmpty()) { + return Component.empty(); + } + final Component resultingComponent; + if (component.arguments().isEmpty()) { + resultingComponent = this.miniMessage.deserialize(miniMessageString); + } else { + resultingComponent = this.miniMessage.deserialize(miniMessageString, new ArgumentTag(component.arguments())); + } + if (component.children().isEmpty()) { + return resultingComponent; + } else { + return resultingComponent.children(component.children()); + } + } + + @Override + public String miniMessageTranslation(@NotNull String key, @NotNull Locale locale) { + Translation translation = translations.get(key); + if (translation == null) { + return null; + } + return translation.translate(locale); + } + + @Override + public @NotNull TriState hasAnyTranslations() { + if (!this.translations.isEmpty()) { + return TriState.TRUE; + } + return TriState.FALSE; + } + + @Override + public void defaultLocale(final @NotNull Locale defaultLocale) { + this.defaultLocale = requireNonNull(defaultLocale, "defaultLocale"); + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of(ExaminableProperty.of("translations", this.translations)); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof MiniMessageTranslationRegistryImpl that)) return false; + return this.name.equals(that.name) + && this.translations.equals(that.translations) + && this.defaultLocale.equals(that.defaultLocale); + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.translations, this.defaultLocale); + } + + @Override + public String toString() { + return Internals.toString(this); + } + + public static class ArgumentTag implements TagResolver { + private static final String NAME = "argument"; + private static final String NAME_1 = "arg"; + + private final List argumentComponents; + + public ArgumentTag(final @NotNull List argumentComponents) { + this.argumentComponents = Objects.requireNonNull(argumentComponents, "argumentComponents"); + } + + @Override + public @Nullable Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException { + final int index = arguments.popOr("No argument number provided").asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments)); + + if (index < 0 || index >= argumentComponents.size()) { + throw ctx.newException("Invalid argument number", arguments); + } + + return Tag.inserting(argumentComponents.get(index)); + } + + @Override + public boolean has(final @NotNull String name) { + return name.equals(NAME) || name.equals(NAME_1); + } + } + + final class Translation implements Examinable { + private final String key; + private final Map formats; + + Translation(final @NotNull String key) { + this.key = requireNonNull(key, "translation key"); + this.formats = new ConcurrentHashMap<>(); + } + + void register(final @NotNull Locale locale, final @NotNull String format) { + if (this.formats.putIfAbsent(requireNonNull(locale, "locale"), requireNonNull(format, "message format")) != null) { + throw new IllegalArgumentException(String.format("Translation already exists: %s for %s", this.key, locale)); + } + } + + @Nullable String translate(final @NotNull Locale locale) { + String format = this.formats.get(requireNonNull(locale, "locale")); + if (format == null) { + format = this.formats.get(new Locale(locale.getLanguage())); // try without country + if (format == null) { + format = this.formats.get(MiniMessageTranslationRegistryImpl.this.defaultLocale); // try local default locale + } + } + return format; + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of( + ExaminableProperty.of("key", this.key), + ExaminableProperty.of("formats", this.formats) + ); + } + + @Override + public boolean equals(final Object other) { + if (this == other) return true; + if (!(other instanceof Translation that)) return false; + return this.key.equals(that.key) && + this.formats.equals(that.formats); + } + + @Override + public int hashCode() { + return Objects.hash(this.key, this.formats); + } + + @Override + public String toString() { + return Internals.toString(this); + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslator.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslator.java new file mode 100644 index 0000000..e2c3a33 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslator.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.translation.Translator; +import net.kyori.examination.Examinable; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public interface MiniMessageTranslator extends Translator, Examinable { + + static @NotNull MiniMessageTranslator translator() { + return MiniMessageTranslatorImpl.INSTANCE; + } + + static @NotNull TranslatableComponentRenderer renderer() { + return MiniMessageTranslatorImpl.INSTANCE.renderer; + } + + static @NotNull Component render(final @NotNull Component component, final @NotNull Locale locale) { + return renderer().render(component, locale); + } + + @NotNull Iterable sources(); + + boolean addSource(final @NotNull Translator source); + + boolean removeSource(final @NotNull Translator source); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslatorImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslatorImpl.java new file mode 100644 index 0000000..f1bff0c --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/MiniMessageTranslatorImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; +import net.kyori.adventure.translation.Translator; +import net.kyori.adventure.util.TriState; +import net.kyori.examination.ExaminableProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.MessageFormat; +import java.util.Collections; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +public class MiniMessageTranslatorImpl implements MiniMessageTranslator { + + private static final Key NAME = Key.key("customfishing", "main"); + static final MiniMessageTranslatorImpl INSTANCE = new MiniMessageTranslatorImpl(); + final TranslatableComponentRenderer renderer = TranslatableComponentRenderer.usingTranslationSource(this); + private final Set sources = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + @Override + public @NotNull Key name() { + return NAME; + } + + @Override + public @NotNull TriState hasAnyTranslations() { + if (!this.sources.isEmpty()) { + return TriState.TRUE; + } + return TriState.FALSE; + } + + @Override + public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) { + // No need to implement this method + return null; + } + + @Override + public @Nullable Component translate(@NotNull TranslatableComponent component, @NotNull Locale locale) { + for (final Translator source : this.sources) { + final Component translation = source.translate(component, locale); + if (translation != null) return translation; + } + return null; + } + + @Override + public @NotNull Iterable sources() { + return Collections.unmodifiableSet(this.sources); + } + + @Override + public boolean addSource(final @NotNull Translator source) { + if (source == this) throw new IllegalArgumentException("MiniMessageTranslationSource"); + return this.sources.add(source); + } + + @Override + public boolean removeSource(final @NotNull Translator source) { + return this.sources.remove(source); + } + + @Override + public @NotNull Stream examinableProperties() { + return Stream.of(ExaminableProperty.of("sources", this.sources)); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/locale/TranslationManager.java b/common/src/main/java/net/momirealms/customnameplates/common/locale/TranslationManager.java new file mode 100644 index 0000000..abae309 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/locale/TranslationManager.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.locale; + +import dev.dejvokep.boostedyaml.YamlDocument; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.translation.Translator; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.util.Pair; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TranslationManager { + + private static final Locale DEFAULT_LOCALE = Locale.ENGLISH; + private static Locale FORCE_LOCALE = null; + private static final List locales = List.of("en", "zh_cn"); + private static TranslationManager instance; + + private final NameplatesPlugin plugin; + private final Set installed = ConcurrentHashMap.newKeySet(); + private MiniMessageTranslationRegistry registry; + private final Path translationsDirectory; + + public TranslationManager(NameplatesPlugin plugin) { + this.plugin = plugin; + this.translationsDirectory = this.plugin.getConfigDirectory().resolve("translations"); + instance = this; + } + + public static void forceLocale(Locale locale) { + FORCE_LOCALE = locale; + } + + public void reload() { + // remove any previous registry + if (this.registry != null) { + MiniMessageTranslator.translator().removeSource(this.registry); + this.installed.clear(); + } + for (String lang : locales) { + this.plugin.getConfigManager().saveResource("translations/" + lang + ".yml"); + } + + this.registry = MiniMessageTranslationRegistry.create(Key.key("customnameplates", "main"), MiniMessage.miniMessage()); + this.registry.defaultLocale(DEFAULT_LOCALE); + this.loadFromFileSystem(this.translationsDirectory, false); + MiniMessageTranslator.translator().addSource(this.registry); + } + + public static String miniMessageTranslation(String key) { + return miniMessageTranslation(key, null); + } + + public static String miniMessageTranslation(String key, @Nullable Locale locale) { + if (FORCE_LOCALE != null) { + return instance.registry.miniMessageTranslation(key, FORCE_LOCALE); + } + if (locale == null) { + locale = Locale.getDefault(); + if (locale == null) { + locale = DEFAULT_LOCALE; + } + } + return instance.registry.miniMessageTranslation(key, locale); + } + + public static Component render(Component component) { + return render(component, null); + } + + public static Component render(Component component, @Nullable Locale locale) { + if (FORCE_LOCALE != null) { + return MiniMessageTranslator.render(component, FORCE_LOCALE); + } + if (locale == null) { + locale = Locale.getDefault(); + if (locale == null) { + locale = DEFAULT_LOCALE; + } + } + return MiniMessageTranslator.render(component, locale); + } + + public void loadFromFileSystem(Path directory, boolean suppressDuplicatesError) { + List translationFiles; + try (Stream stream = Files.list(directory)) { + translationFiles = stream.filter(TranslationManager::isTranslationFile).collect(Collectors.toList()); + } catch (IOException e) { + translationFiles = Collections.emptyList(); + } + + if (translationFiles.isEmpty()) { + return; + } + + Map> loaded = new HashMap<>(); + for (Path translationFile : translationFiles) { + try { + Pair> result = loadTranslationFile(translationFile); + loaded.put(result.left(), result.right()); + } catch (Exception e) { + if (!suppressDuplicatesError || !isAdventureDuplicatesException(e)) { + this.plugin.getPluginLogger().warn("Error loading locale file: " + translationFile.getFileName(), e); + } + } + } + + // try registering the locale without a country code - if we don't already have a registration for that + loaded.forEach((locale, bundle) -> { + Locale localeWithoutCountry = new Locale(locale.getLanguage()); + if (!locale.equals(localeWithoutCountry) && !localeWithoutCountry.equals(DEFAULT_LOCALE) && this.installed.add(localeWithoutCountry)) { + try { + this.registry.registerAll(localeWithoutCountry, bundle); + } catch (IllegalArgumentException e) { + // ignore + } + } + }); + + Locale localLocale = Locale.getDefault(); + if (!this.installed.contains(localLocale) && FORCE_LOCALE == null) { + plugin.getPluginLogger().warn(localLocale.toString().toLowerCase(Locale.ENGLISH) + ".yml not exists, using " + DEFAULT_LOCALE.toString().toLowerCase(Locale.ENGLISH) + ".yml as default locale."); + } + } + + public static boolean isTranslationFile(Path path) { + return path.getFileName().toString().endsWith(".yml"); + } + + private static boolean isAdventureDuplicatesException(Exception e) { + return e instanceof IllegalArgumentException && (e.getMessage().startsWith("Invalid key") || e.getMessage().startsWith("Translation already exists")); + } + + @SuppressWarnings("unchecked") + private Pair> loadTranslationFile(Path translationFile) { + String fileName = translationFile.getFileName().toString(); + String localeString = fileName.substring(0, fileName.length() - ".yml".length()); + Locale locale = parseLocale(localeString); + + if (locale == null) { + throw new IllegalStateException("Unknown locale '" + localeString + "' - unable to register."); + } + + Map bundle = new HashMap<>(); + YamlDocument document = plugin.getConfigManager().loadConfig("translations" + File.separator + translationFile.getFileName(), '@'); + try { + document.save(new File(plugin.getDataDirectory().toFile(), "translations" + File.separator + translationFile.getFileName())); + } catch (IOException e) { + throw new IllegalStateException("Could not update translation file: " + translationFile.getFileName(), e); + } + Map map = document.getStringRouteMappedValues(false); + map.remove("config-version"); + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof List list) { + List strList = (List) list; + StringJoiner stringJoiner = new StringJoiner(""); + for (String str : strList) { + stringJoiner.add(str); + } + bundle.put(entry.getKey(), stringJoiner.toString()); + } else if (entry.getValue() instanceof String str) { + bundle.put(entry.getKey(), str); + } + } + + this.registry.registerAll(locale, bundle); + this.installed.add(locale); + + return Pair.of(locale, bundle); + } + + public static @Nullable Locale parseLocale(@Nullable String locale) { + return locale == null || locale.isEmpty() ? null : Translator.parseLocale(locale); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/CustomNameplatesProperties.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/CustomNameplatesProperties.java new file mode 100644 index 0000000..25e16ed --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/CustomNameplatesProperties.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.plugin; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class CustomNameplatesProperties { + + private final HashMap propertyMap; + + private CustomNameplatesProperties(HashMap propertyMap) { + this.propertyMap = propertyMap; + } + + public static String getValue(String key) { + if (!SingletonHolder.INSTANCE.propertyMap.containsKey(key)) { + throw new RuntimeException("Unknown key: " + key); + } + return SingletonHolder.INSTANCE.propertyMap.get(key); + } + + private static class SingletonHolder { + + private static final CustomNameplatesProperties INSTANCE = getInstance(); + + private static CustomNameplatesProperties getInstance() { + try (InputStream inputStream = CustomNameplatesProperties.class.getClassLoader().getResourceAsStream("custom-nameplates.properties")) { + HashMap versionMap = new HashMap<>(); + Properties properties = new Properties(); + properties.load(inputStream); + for (Map.Entry entry : properties.entrySet()) { + if (entry.getKey() instanceof String key && entry.getValue() instanceof String value) { + versionMap.put(key, value); + } + } + return new CustomNameplatesProperties(versionMap); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/NameplatesPlugin.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/NameplatesPlugin.java new file mode 100644 index 0000000..f73b11c --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/NameplatesPlugin.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.plugin; + +import net.momirealms.customnameplates.common.dependency.DependencyManager; +import net.momirealms.customnameplates.common.locale.TranslationManager; +import net.momirealms.customnameplates.common.plugin.classpath.ClassPathAppender; +import net.momirealms.customnameplates.common.plugin.config.ConfigLoader; +import net.momirealms.customnameplates.common.plugin.logging.PluginLogger; +import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerAdapter; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.function.Supplier; + +/** + * Interface representing the main CustomFishing plugin. + */ +public interface NameplatesPlugin { + + /** + * Retrieves an input stream for a resource file within the plugin. + * + * @param filePath the path to the resource file + * @return an {@link InputStream} for the resource file + */ + InputStream getResourceStream(String filePath); + + /** + * Retrieves the plugin logger. + * + * @return the {@link PluginLogger} instance + */ + PluginLogger getPluginLogger(); + + /** + * Retrieves the class path appender. + * + * @return the {@link ClassPathAppender} instance + */ + ClassPathAppender getClassPathAppender(); + + /** + * Retrieves the scheduler adapter. + * + * @return the {@link SchedulerAdapter} instance + */ + SchedulerAdapter getScheduler(); + + /** + * Retrieves the data directory path. + * + * @return the {@link Path} to the data directory + */ + Path getDataDirectory(); + + /** + * Retrieves the configuration directory path. + * By default, this is the same as the data directory. + * + * @return the {@link Path} to the configuration directory + */ + default Path getConfigDirectory() { + return getDataDirectory(); + } + + /** + * Retrieves the dependency manager. + * + * @return the {@link DependencyManager} instance + */ + DependencyManager getDependencyManager(); + + /** + * Retrieves the translation manager. + * + * @return the {@link TranslationManager} instance + */ + TranslationManager getTranslationManager(); + + /** + * Retrieves the configuration manager. + * + * @return the {@link ConfigLoader} instance + */ + ConfigLoader getConfigManager(); + + /** + * Retrieves the server version. + * + * @return the server version as a string + */ + String getServerVersion(); + + /** + * Retrieves the plugin version. + * + * @return the plugin version as a string + */ + String getPluginVersion(); + + /** + * Loads the plugin. + * This method is called during the plugin's loading phase. + */ + void load(); + + /** + * Enables the plugin. + * This method is called during the plugin's enabling phase. + */ + void enable(); + + /** + * Disables the plugin. + * This method is called during the plugin's disabling phase. + */ + void disable(); + + /** + * Reloads the plugin. + */ + void reload(); + + /** + * Debug + * + * @param supplier debug message supplier + */ + void debug(Supplier supplier); + + boolean isUpToDate(); + + default File getDataFolder() { + return getDataDirectory().toFile(); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/ClassPathAppender.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/ClassPathAppender.java similarity index 95% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/ClassPathAppender.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/ClassPathAppender.java index 4b787a9..30d2643 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/ClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/ClassPathAppender.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.classpath; +package net.momirealms.customnameplates.common.plugin.classpath; import java.nio.file.Path; diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/ReflectionClassPathAppender.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/ReflectionClassPathAppender.java similarity index 87% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/ReflectionClassPathAppender.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/ReflectionClassPathAppender.java index 89897f0..5f8f8d6 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/ReflectionClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/ReflectionClassPathAppender.java @@ -23,7 +23,9 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.classpath; +package net.momirealms.customnameplates.common.plugin.classpath; + +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; import java.net.MalformedURLException; import java.net.URLClassLoader; @@ -40,6 +42,10 @@ public class ReflectionClassPathAppender implements ClassPathAppender { } } + public ReflectionClassPathAppender(NameplatesPlugin plugin) throws IllegalStateException { + this(plugin.getClass().getClassLoader()); + } + @Override public void addJarToClasspath(Path file) { try { diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/URLClassLoaderAccess.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/URLClassLoaderAccess.java similarity index 95% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/URLClassLoaderAccess.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/URLClassLoaderAccess.java index f50da32..209372e 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/URLClassLoaderAccess.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/classpath/URLClassLoaderAccess.java @@ -23,9 +23,9 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.classpath; +package net.momirealms.customnameplates.common.plugin.classpath; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -66,7 +66,7 @@ public abstract class URLClassLoaderAccess { * * @param url the URL to add */ - public abstract void addURL(@NonNull URL url); + public abstract void addURL(@NotNull URL url); private static void throwError(Throwable cause) throws UnsupportedOperationException { throw new UnsupportedOperationException("CustomNameplates is unable to inject into the plugin URLClassLoader.\n" + @@ -100,7 +100,7 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { try { ADD_URL_METHOD.invoke(super.classLoader, url); } catch (ReflectiveOperationException e) { @@ -162,7 +162,7 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { if (this.unopenedURLs == null || this.pathURLs == null) { URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs")); } @@ -182,7 +182,7 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NonNull URL url) { + public void addURL(@NotNull URL url) { URLClassLoaderAccess.throwError(null); } } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/config/ConfigLoader.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/config/ConfigLoader.java new file mode 100644 index 0000000..7d9fb6c --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/config/ConfigLoader.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.plugin.config; + +import dev.dejvokep.boostedyaml.YamlDocument; + +import java.io.File; + +/** + * Interface for loading and managing configuration files. + */ +public interface ConfigLoader { + + /** + * Loads a YAML configuration file from the specified file path. + * + * @param filePath the path to the configuration file + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadConfig(String filePath); + + /** + * Loads a YAML configuration file from the specified file path with a custom route separator. + * + * @param filePath the path to the configuration file + * @param routeSeparator the custom route separator character + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadConfig(String filePath, char routeSeparator); + + /** + * Loads a YAML data file. + * + * @param file the {@link File} object representing the data file + * @return the loaded {@link YamlDocument} + */ + YamlDocument loadData(File file); + + /** + * Saves a resource file from the plugin's jar to the specified file path. + * + * @param filePath the path where the resource file will be saved + */ + void saveResource(String filePath); +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/feature/Reloadable.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/feature/Reloadable.java new file mode 100644 index 0000000..5ee6cca --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/feature/Reloadable.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.plugin.feature; + +public interface Reloadable { + + default void reload() { + unload(); + load(); + } + + default void unload() { + } + + default void load() { + } + + default void disable() { + unload(); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/JarInJarClassPathAppender.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/JavaPluginLogger.java similarity index 53% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/JarInJarClassPathAppender.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/JavaPluginLogger.java index d535c40..dcf04fd 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/classpath/JarInJarClassPathAppender.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/JavaPluginLogger.java @@ -23,40 +23,40 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.classpath; +package net.momirealms.customnameplates.common.plugin.logging; -import net.momirealms.customnameplates.paper.libraries.loader.JarInJarClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; -import java.io.IOException; -import java.net.MalformedURLException; -import java.nio.file.Path; +public class JavaPluginLogger implements PluginLogger { + private final Logger logger; -public class JarInJarClassPathAppender implements ClassPathAppender { - private final JarInJarClassLoader classLoader; - - public JarInJarClassPathAppender(ClassLoader classLoader) { - if (!(classLoader instanceof JarInJarClassLoader)) { - throw new IllegalArgumentException("Loader is not a JarInJarClassLoader: " + classLoader.getClass().getName()); - } - this.classLoader = (JarInJarClassLoader) classLoader; + public JavaPluginLogger(Logger logger) { + this.logger = logger; } @Override - public void addJarToClasspath(Path file) { - try { - this.classLoader.addJarToClasspath(file.toUri().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } + public void info(String s) { + this.logger.info(s); } @Override - public void close() { - this.classLoader.deleteJarResource(); - try { - this.classLoader.close(); - } catch (IOException e) { - e.printStackTrace(); - } + public void warn(String s) { + this.logger.warning(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.log(Level.WARNING, s, t); + } + + @Override + public void severe(String s) { + this.logger.severe(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.log(Level.SEVERE, s, t); } } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/Log4jPluginLogger.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/Log4jPluginLogger.java new file mode 100644 index 0000000..c85d6e4 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/Log4jPluginLogger.java @@ -0,0 +1,61 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.plugin.logging; + +import org.apache.logging.log4j.Logger; + +public class Log4jPluginLogger implements PluginLogger { + private final Logger logger; + + public Log4jPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } + + @Override + public void warn(String s) { + this.logger.warn(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.warn(s, t); + } + + @Override + public void severe(String s) { + this.logger.error(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.error(s, t); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/LoadingException.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/PluginLogger.java similarity index 71% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/LoadingException.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/PluginLogger.java index adc317f..55d5952 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/LoadingException.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/PluginLogger.java @@ -23,19 +23,24 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.loader; +package net.momirealms.customnameplates.common.plugin.logging; /** - * Runtime exception used if there is a problem during loading + * Represents the logger instance being used by CustomFishing on the platform. + * + *

Messages sent using the logger are sent prefixed with the CustomFishing tag, + * and on some implementations will be colored depending on the message type.

*/ -public class LoadingException extends RuntimeException { +public interface PluginLogger { - public LoadingException(String message) { - super(message); - } + void info(String s); - public LoadingException(String message, Throwable cause) { - super(message, cause); - } + void warn(String s); + + void warn(String s, Throwable t); + + void severe(String s); + + void severe(String s, Throwable t); } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/Slf4jPluginLogger.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/Slf4jPluginLogger.java new file mode 100644 index 0000000..9df6f4a --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/logging/Slf4jPluginLogger.java @@ -0,0 +1,61 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.plugin.logging; + +import org.slf4j.Logger; + +public class Slf4jPluginLogger implements PluginLogger { + private final Logger logger; + + public Slf4jPluginLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void info(String s) { + this.logger.info(s); + } + + @Override + public void warn(String s) { + this.logger.warn(s); + } + + @Override + public void warn(String s, Throwable t) { + this.logger.warn(s, t); + } + + @Override + public void severe(String s) { + this.logger.error(s); + } + + @Override + public void severe(String s, Throwable t) { + this.logger.error(s, t); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/AbstractJavaScheduler.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/AbstractJavaScheduler.java new file mode 100644 index 0000000..15f4396 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/AbstractJavaScheduler.java @@ -0,0 +1,136 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.plugin.scheduler; + +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Arrays; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Abstract implementation of {@link SchedulerAdapter} using a {@link ScheduledExecutorService}. + */ +public abstract class AbstractJavaScheduler implements SchedulerAdapter { + private static final int PARALLELISM = 16; + + private final NameplatesPlugin plugin; + + private final ScheduledThreadPoolExecutor scheduler; + private final ForkJoinPool worker; + + public AbstractJavaScheduler(NameplatesPlugin plugin) { + this.plugin = plugin; + + this.scheduler = new ScheduledThreadPoolExecutor(4, r -> { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setName("nameplates-scheduler"); + return thread; + }); + this.scheduler.setRemoveOnCancelPolicy(true); + this.scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.worker = new ForkJoinPool(PARALLELISM, new WorkerThreadFactory(), new ExceptionHandler(), false); + } + + @Override + public Executor async() { + return this.worker; + } + + @Override + public SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit) { + if (delay == 0) { + async().execute(task); + return new DummyTask(); + } + ScheduledFuture future = this.scheduler.schedule(() -> this.worker.execute(task), delay, unit); + return new AsyncTask(future); + } + + @Override + public SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit) { + ScheduledFuture future = this.scheduler.scheduleAtFixedRate(() -> this.worker.execute(task), delay, interval, unit); + return new AsyncTask(future); + } + + @Override + public void shutdownScheduler() { + this.scheduler.shutdown(); + try { + if (!this.scheduler.awaitTermination(1, TimeUnit.MINUTES)) { + this.plugin.getPluginLogger().severe("Timed out waiting for the CustomFishing scheduler to terminate"); + reportRunningTasks(thread -> thread.getName().equals("customfishing-scheduler")); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public void shutdownExecutor() { + this.worker.shutdown(); + try { + if (!this.worker.awaitTermination(1, TimeUnit.MINUTES)) { + this.plugin.getPluginLogger().severe("Timed out waiting for the CustomFishing worker thread pool to terminate"); + reportRunningTasks(thread -> thread.getName().startsWith("nameplates-worker-")); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void reportRunningTasks(Predicate predicate) { + Thread.getAllStackTraces().forEach((thread, stack) -> { + if (predicate.test(thread)) { + this.plugin.getPluginLogger().warn("Thread " + thread.getName() + " is blocked, and may be the reason for the slow shutdown!\n" + + Arrays.stream(stack).map(el -> " " + el).collect(Collectors.joining("\n")) + ); + } + }); + } + + private static final class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private static final AtomicInteger COUNT = new AtomicInteger(0); + + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setDaemon(true); + thread.setName("nameplates-worker-" + COUNT.getAndIncrement()); + return thread; + } + } + + private final class ExceptionHandler implements UncaughtExceptionHandler { + @Override + public void uncaughtException(Thread t, Throwable e) { + AbstractJavaScheduler.this.plugin.getPluginLogger().warn("Thread " + t.getName() + " threw an uncaught exception", e); + } + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/MariaDBImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/AsyncTask.java similarity index 59% rename from paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/MariaDBImpl.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/AsyncTask.java index 6e12ff6..34ab060 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/MariaDBImpl.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/AsyncTask.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,19 +15,25 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.storage.method.database.sql; +package net.momirealms.customnameplates.common.plugin.scheduler; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.StorageType; +import java.util.concurrent.ScheduledFuture; -public class MariaDBImpl extends AbstractHikariDatabase { +public class AsyncTask implements SchedulerTask { - public MariaDBImpl(CustomNameplatesPlugin plugin) { - super(plugin); + private final ScheduledFuture future; + + public AsyncTask(ScheduledFuture future) { + this.future = future; } @Override - public StorageType getStorageType() { - return StorageType.MariaDB; + public void cancel() { + future.cancel(false); + } + + @Override + public boolean cancelled() { + return future.isCancelled(); } } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/Pair.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/DummyTask.java similarity index 70% rename from common/src/main/java/net/momirealms/customnameplates/common/Pair.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/DummyTask.java index 574b448..29be4e1 100644 --- a/common/src/main/java/net/momirealms/customnameplates/common/Pair.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/DummyTask.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.common; +package net.momirealms.customnameplates.common.plugin.scheduler; -public record Pair(L left, R right) { +public class DummyTask implements SchedulerTask { + + @Override + public void cancel() { - public static Pair of(final L left, final R right) { - return new Pair<>(left, right); } -} \ No newline at end of file + + @Override + public boolean cancelled() { + return true; + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/scheduler/CancellableTask.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/RegionExecutor.java similarity index 67% rename from api/src/main/java/net/momirealms/customnameplates/api/scheduler/CancellableTask.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/RegionExecutor.java index d226bf8..7c575cd 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/scheduler/CancellableTask.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/RegionExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,19 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.scheduler; +package net.momirealms.customnameplates.common.plugin.scheduler; -public interface CancellableTask { +public interface RegionExecutor { - /** - * Cancel the task - */ - void cancel(); + void run(Runnable r, T l); - /** - * Get if the task is cancelled or not - * - * @return cancelled or not - */ - boolean isCancelled(); + SchedulerTask runLater(Runnable r, long delayTicks, T l); + + SchedulerTask runRepeating(Runnable r, long delayTicks, long period, T l); } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/SchedulerAdapter.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/SchedulerAdapter.java new file mode 100644 index 0000000..dd23011 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/SchedulerAdapter.java @@ -0,0 +1,106 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.plugin.scheduler; + +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * A scheduler for running tasks using the systems provided by the platform + */ +public interface SchedulerAdapter { + + /** + * Gets an async executor instance + * + * @return an async executor instance + */ + Executor async(); + + /** + * Gets a sync executor instance + * + * @return a sync executor instance + */ + RegionExecutor sync(); + + /** + * Executes a task async + * + * @param task the task + */ + default void executeAsync(Runnable task) { + async().execute(task); + } + + /** + * Executes a task sync + * + * @param task the task + */ + default void executeSync(Runnable task, T location) { + sync().run(task, location); + } + + default void executeSync(Runnable task) { + sync().run(task, null); + } + + /** + * Executes the given task with a delay. + * + * @param task the task + * @param delay the delay + * @param unit the unit of delay + * @return the resultant task instance + */ + SchedulerTask asyncLater(Runnable task, long delay, TimeUnit unit); + + /** + * Executes the given task repeatedly at a given interval. + * + * @param task the task + * @param interval the interval + * @param unit the unit of interval + * @return the resultant task instance + */ + SchedulerTask asyncRepeating(Runnable task, long delay, long interval, TimeUnit unit); + + /** + * Shuts down the scheduler instance. + * + *

{@link #asyncLater(Runnable, long, TimeUnit)} and {@link #asyncRepeating(Runnable, long, long, TimeUnit)}.

+ */ + void shutdownScheduler(); + + /** + * Shuts down the executor instance. + * + *

{@link #async()} and {@link #executeAsync(Runnable)}.

+ */ + void shutdownExecutor(); + +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/LoaderBootstrap.java b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/SchedulerTask.java similarity index 84% rename from paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/LoaderBootstrap.java rename to common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/SchedulerTask.java index 91845aa..5e37781 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/LoaderBootstrap.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/plugin/scheduler/SchedulerTask.java @@ -23,17 +23,17 @@ * SOFTWARE. */ -package net.momirealms.customnameplates.paper.libraries.loader; +package net.momirealms.customnameplates.common.plugin.scheduler; /** - * Minimal bootstrap plugin, called by the loader plugin. + * Represents a scheduled task */ -public interface LoaderBootstrap { +public interface SchedulerTask { - void onLoad(); - - default void onEnable() {} - - default void onDisable() {} + /** + * Cancels the task. + */ + void cancel(); + boolean cancelled(); } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/sender/AbstractSender.java b/common/src/main/java/net/momirealms/customnameplates/common/sender/AbstractSender.java new file mode 100644 index 0000000..2fca2b6 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/sender/AbstractSender.java @@ -0,0 +1,116 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.sender; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.util.Tristate; + +import java.util.UUID; + +/** + * Simple implementation of {@link Sender} using a {@link SenderFactory} + * + * @param the command sender type + */ +public final class AbstractSender implements Sender { + private final NameplatesPlugin plugin; + private final SenderFactory factory; + private final T sender; + + private final UUID uniqueId; + private final String name; + private final boolean isConsole; + + AbstractSender(NameplatesPlugin plugin, SenderFactory factory, T sender) { + this.plugin = plugin; + this.factory = factory; + this.sender = sender; + this.uniqueId = factory.getUniqueId(this.sender); + this.name = factory.getName(this.sender); + this.isConsole = this.factory.isConsole(this.sender); + } + + @Override + public NameplatesPlugin getPlugin() { + return this.plugin; + } + + @Override + public UUID getUniqueId() { + return this.uniqueId; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void sendMessage(Component message) { + this.factory.sendMessage(this.sender, message); + } + + @Override + public void sendMessage(Component message, boolean ignoreEmpty) { + if (ignoreEmpty && message.equals(Component.empty())) { + return; + } + sendMessage(message); + } + + @Override + public Tristate getPermissionValue(String permission) { + return (isConsole() && this.factory.consoleHasAllPermissions()) ? Tristate.TRUE : this.factory.getPermissionValue(this.sender, permission); + } + + @Override + public boolean hasPermission(String permission) { + return (isConsole() && this.factory.consoleHasAllPermissions()) || this.factory.hasPermission(this.sender, permission); + } + + @Override + public void performCommand(String commandLine) { + this.factory.performCommand(this.sender, commandLine); + } + + @Override + public boolean isConsole() { + return this.isConsole; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof AbstractSender that)) return false; + return this.getUniqueId().equals(that.getUniqueId()); + } + + @Override + public int hashCode() { + return this.uniqueId.hashCode(); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/sender/DummyConsoleSender.java b/common/src/main/java/net/momirealms/customnameplates/common/sender/DummyConsoleSender.java new file mode 100644 index 0000000..c4de997 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/sender/DummyConsoleSender.java @@ -0,0 +1,63 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.sender; + +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; + +import java.util.UUID; + +public abstract class DummyConsoleSender implements Sender { + + private final NameplatesPlugin platform; + + public DummyConsoleSender(NameplatesPlugin plugin) { + this.platform = plugin; + } + + @Override + public void performCommand(String commandLine) { + } + + @Override + public boolean isConsole() { + return true; + } + + @Override + public NameplatesPlugin getPlugin() { + return this.platform; + } + + @Override + public UUID getUniqueId() { + return Sender.CONSOLE_UUID; + } + + @Override + public String getName() { + return Sender.CONSOLE_NAME; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/sender/Sender.java b/common/src/main/java/net/momirealms/customnameplates/common/sender/Sender.java new file mode 100644 index 0000000..795dd4c --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/sender/Sender.java @@ -0,0 +1,121 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.sender; + +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.util.Tristate; + +import java.util.UUID; + +/** + * Wrapper interface to represent a CommandSender/CommandSource within the common command implementations. + */ +public interface Sender { + + /** The uuid used by the console sender. */ + UUID CONSOLE_UUID = new UUID(0, 0); // 00000000-0000-0000-0000-000000000000 + /** The name used by the console sender. */ + String CONSOLE_NAME = "Console"; + + /** + * Gets the plugin instance the sender is from. + * + * @return the plugin + */ + NameplatesPlugin getPlugin(); + + /** + * Gets the sender's username + * + * @return a friendly username for the sender + */ + String getName(); + + /** + * Gets the sender's unique id. + * + *

See {@link #CONSOLE_UUID} for the console's UUID representation.

+ * + * @return the sender's uuid + */ + UUID getUniqueId(); + + /** + * Send a json message to the Sender. + * + * @param message the message to send. + */ + void sendMessage(Component message); + + /** + * Send a json message to the Sender. + * + * @param message the message to send. + * @param ignoreEmpty whether to ignore empty component + */ + void sendMessage(Component message, boolean ignoreEmpty); + + /** + * Gets the tristate a permission is set to. + * + * @param permission the permission to check for + * @return a tristate + */ + Tristate getPermissionValue(String permission); + + /** + * Check if the Sender has a permission. + * + * @param permission the permission to check for + * @return true if the sender has the permission + */ + boolean hasPermission(String permission); + + /** + * Makes the sender perform a command. + * + * @param commandLine the command + */ + void performCommand(String commandLine); + + /** + * Gets whether this sender is the console + * + * @return if the sender is the console + */ + boolean isConsole(); + + /** + * Gets whether this sender is still valid & receiving messages. + * + * @return if this sender is valid + */ + default boolean isValid() { + return true; + } + +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/sender/SenderFactory.java b/common/src/main/java/net/momirealms/customnameplates/common/sender/SenderFactory.java new file mode 100644 index 0000000..dc4356e --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/sender/SenderFactory.java @@ -0,0 +1,82 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.sender; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.common.plugin.NameplatesPlugin; +import net.momirealms.customnameplates.common.util.Tristate; + +import java.util.Objects; +import java.util.UUID; + +/** + * Factory class to make a thread-safe sender instance + * + * @param

the plugin type + * @param the command sender type + */ +public abstract class SenderFactory

implements AutoCloseable { + private final P plugin; + + public SenderFactory(P plugin) { + this.plugin = plugin; + } + + protected P getPlugin() { + return this.plugin; + } + + protected abstract UUID getUniqueId(T sender); + + protected abstract String getName(T sender); + + public abstract Audience getAudience(T sender); + + protected abstract void sendMessage(T sender, Component message); + + protected abstract Tristate getPermissionValue(T sender, String node); + + protected abstract boolean hasPermission(T sender, String node); + + protected abstract void performCommand(T sender, String command); + + protected abstract boolean isConsole(T sender); + + protected boolean consoleHasAllPermissions() { + return true; + } + + public final Sender wrap(T sender) { + Objects.requireNonNull(sender, "sender"); + return new AbstractSender<>(this.plugin, this, sender); + } + + @Override + public void close() { + + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/team/TeamCollisionRule.java b/common/src/main/java/net/momirealms/customnameplates/common/team/TeamCollisionRule.java deleted file mode 100644 index c67265c..0000000 --- a/common/src/main/java/net/momirealms/customnameplates/common/team/TeamCollisionRule.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.common.team; - -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; - -public enum TeamCollisionRule { - - ALWAYS("always"), - NEVER("never"), - PUSH_OTHER_TEAMS("pushOtherTeams"), - PUSH_OWN_TEAM("pushOwnTeam"); - - private final String id; - - TeamCollisionRule(@NotNull String id) { - this.id = id; - } - - public String getId() { - return id; - } - - @NotNull - public static TeamCollisionRule byId(String id) { - return Arrays.stream(values()) - .filter(mode -> mode.id.equals(id)) - .findFirst() - .orElse(ALWAYS); - } -} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/team/TeamTagVisibility.java b/common/src/main/java/net/momirealms/customnameplates/common/team/TeamTagVisibility.java deleted file mode 100644 index a2a8bc8..0000000 --- a/common/src/main/java/net/momirealms/customnameplates/common/team/TeamTagVisibility.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.common.team; - -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; - -public enum TeamTagVisibility { - - ALWAYS("always"), - HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"), - HIDE_FOR_OWN_TEAM("hideForOwnTeam"), - NEVER("never"); - - private final String id; - - TeamTagVisibility(@NotNull String id) { - this.id = id; - } - - public String getId() { - return id; - } - - @NotNull - public static TeamTagVisibility byId(String id) { - return Arrays.stream(values()) - .filter(mode -> mode.id.equals(id)) - .findFirst() - .orElse(ALWAYS); - } -} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/ArrayUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/ArrayUtils.java new file mode 100644 index 0000000..91c3f7c --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/ArrayUtils.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utility class for handling operations with arrays. + */ +public class ArrayUtils { + + private ArrayUtils() {} + + /** + * Creates a subarray from the specified array starting from the given index. + * + * @param array the original array + * @param index the starting index for the subarray + * @param the type of the elements in the array + * @return the subarray starting from the given index + * @throws IllegalArgumentException if the index is less than 0 + */ + public static T[] subArray(T[] array, int index) { + if (index < 0) { + throw new IllegalArgumentException("Index should be a value no lower than 0"); + } + if (array.length <= index) { + @SuppressWarnings("unchecked") + T[] emptyArray = (T[]) Array.newInstance(array.getClass().getComponentType(), 0); + return emptyArray; + } + @SuppressWarnings("unchecked") + T[] subArray = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length - index); + System.arraycopy(array, index, subArray, 0, array.length - index); + return subArray; + } + + /** + * Splits the specified array into a list of subarrays, each with the specified chunk size. + * + * @param array the original array + * @param chunkSize the size of each chunk + * @param the type of the elements in the array + * @return a list of subarrays + */ + public static List splitArray(T[] array, int chunkSize) { + List result = new ArrayList<>(); + for (int i = 0; i < array.length; i += chunkSize) { + int end = Math.min(array.length, i + chunkSize); + @SuppressWarnings("unchecked") + T[] chunk = (T[]) Array.newInstance(array.getClass().getComponentType(), end - i); + System.arraycopy(array, i, chunk, 0, end - i); + result.add(chunk); + } + return result; + } + + /** + * Appends an element to the specified array. + * + * @param array the original array + * @param element the element to append + * @param the type of the elements in the array + * @return a new array with the appended element + */ + public static T[] appendElementToArray(T[] array, T element) { + T[] newArray = Arrays.copyOf(array, array.length + 1); + newArray[array.length] = element; + return newArray; + } + + /** + * Splits a string value into an array of substrings based on comma separation. + * The input string is expected to be in the format "[value1, value2, ...]". + * + * @param value the string value to split + * @return an array of substrings + */ + public static String[] splitValue(String value) { + return value.substring(value.indexOf('[') + 1, value.lastIndexOf(']')) + .replaceAll("\\s", "") + .split(","); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ClassUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/ClassUtils.java similarity index 95% rename from paper/src/main/java/net/momirealms/customnameplates/paper/util/ClassUtils.java rename to common/src/main/java/net/momirealms/customnameplates/common/util/ClassUtils.java index ba36931..517a4c0 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ClassUtils.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/ClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.util; +package net.momirealms.customnameplates.common.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,6 +29,9 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; +/** + * Utility class for handling classes. + */ public class ClassUtils { private ClassUtils() {} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/Either.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Either.java new file mode 100644 index 0000000..f965afa --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Either.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +/** + * Interface representing a value that can be either of two types, primary or fallback. + * Provides methods to create instances of either type and to map between them. + * + * @param the type of the primary value + * @param the type of the fallback value + */ +public interface Either { + + /** + * Creates an {@link Either} instance with a primary value. + * + * @param value the primary value + * @param the type of the primary value + * @param the type of the fallback value + * @return an {@link Either} instance with the primary value + */ + static @NotNull Either ofPrimary(final @NotNull U value) { + return EitherImpl.of(requireNonNull(value, "value"), null); + } + + /** + * Creates an {@link Either} instance with a fallback value. + * + * @param value the fallback value + * @param the type of the primary value + * @param the type of the fallback value + * @return an {@link Either} instance with the fallback value + */ + static @NotNull Either ofFallback(final @NotNull V value) { + return EitherImpl.of(null, requireNonNull(value, "value")); + } + + /** + * Retrieves the primary value, if present. + * + * @return an {@link Optional} containing the primary value, or empty if not present + */ + @NotNull + Optional primary(); + + /** + * Retrieves the fallback value, if present. + * + * @return an {@link Optional} containing the fallback value, or empty if not present + */ + @NotNull + Optional fallback(); + + /** + * Retrieves the primary value, or maps the fallback value to the primary type if the primary is not present. + * + * @param mapFallback a function to map the fallback value to the primary type + * @return the primary value, or the mapped fallback value if the primary is not present + */ + default @Nullable U primaryOrMapFallback(final @NotNull Function mapFallback) { + return this.primary().orElseGet(() -> mapFallback.apply(this.fallback().get())); + } + + /** + * Retrieves the fallback value, or maps the primary value to the fallback type if the fallback is not present. + * + * @param mapPrimary a function to map the primary value to the fallback type + * @return the fallback value, or the mapped primary value if the fallback is not present + */ + default @Nullable V fallbackOrMapPrimary(final @NotNull Function mapPrimary) { + return this.fallback().orElseGet(() -> mapPrimary.apply(this.primary().get())); + } + + /** + * Maps either the primary or fallback value to a new type. + * + * @param mapPrimary a function to map the primary value + * @param mapFallback a function to map the fallback value + * @param the type of the result + * @return the mapped result + */ + default @NotNull R mapEither( + final @NotNull Function mapPrimary, + final @NotNull Function mapFallback + ) { + return this.primary() + .map(mapPrimary) + .orElseGet(() -> this.fallback().map(mapFallback).get()); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/EitherImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/util/EitherImpl.java new file mode 100644 index 0000000..5c1d933 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/EitherImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +final class EitherImpl implements Either { + private final @Nullable U primary; + private final @Nullable V fallback; + + private EitherImpl(Optional primary, Optional fallback) { + this.primary = primary.orElse(null); + this.fallback = fallback.orElse(null); + } + + private EitherImpl(@Nullable U primary, @Nullable V fallback) { + this.primary = primary; + this.fallback = fallback; + } + + private EitherImpl( + EitherImpl original, + @Nullable U primary, + @Nullable V fallback + ) { + this.primary = primary; + this.fallback = fallback; + } + + @Override + public @NotNull Optional primary() { + return Optional.ofNullable(primary); + } + + @Override + public @NotNull Optional fallback() { + return Optional.ofNullable(fallback); + } + + public final EitherImpl withPrimary(@Nullable U value) { + @Nullable U newValue = value; + if (this.primary == newValue) return this; + return new EitherImpl<>(this, newValue, this.fallback); + } + + public EitherImpl withPrimary(Optional optional) { + @Nullable U value = optional.orElse(null); + if (this.primary == value) return this; + return new EitherImpl<>(this, value, this.fallback); + } + + public EitherImpl withFallback(@Nullable V value) { + @Nullable V newValue = value; + if (this.fallback == newValue) return this; + return new EitherImpl<>(this, this.primary, newValue); + } + + public EitherImpl withFallback(Optional optional) { + @Nullable V value = optional.orElse(null); + if (this.fallback == value) return this; + return new EitherImpl<>(this, this.primary, value); + } + + @Override + public boolean equals(@Nullable Object another) { + if (this == another) return true; + return another instanceof EitherImpl + && equalTo((EitherImpl) another); + } + + private boolean equalTo(EitherImpl another) { + return Objects.equals(primary, another.primary) + && Objects.equals(fallback, another.fallback); + } + + @Override + public int hashCode() { + int h = 5381; + h += (h << 5) + Objects.hashCode(primary); + h += (h << 5) + Objects.hashCode(fallback); + return h; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("Either{"); + if (primary != null) { + builder.append("primary=").append(primary); + } + if (fallback != null) { + if (builder.length() > 7) builder.append(", "); + builder.append("fallback=").append(fallback); + } + return builder.append("}").toString(); + } + + public static EitherImpl of(Optional primary, Optional fallback) { + return new EitherImpl<>(primary, fallback); + } + + public static EitherImpl of(@Nullable U primary, @Nullable V fallback) { + return new EitherImpl<>(primary, fallback); + } + + public static EitherImpl copyOf(Either instance) { + if (instance instanceof EitherImpl) { + return (EitherImpl) instance; + } + return EitherImpl.of(instance.primary(), instance.fallback()); + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/Key.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Key.java similarity index 58% rename from common/src/main/java/net/momirealms/customnameplates/common/Key.java rename to common/src/main/java/net/momirealms/customnameplates/common/util/Key.java index 4a27e2f..530279b 100644 --- a/common/src/main/java/net/momirealms/customnameplates/common/Key.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Key.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,19 +15,39 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.common; +package net.momirealms.customnameplates.common.util; +/** + * Represents a key consisting of a namespace and a value. + * This class provides methods for creating and manipulating keys. + */ public record Key(String namespace, String value) { + /** + * Creates a new {@link Key} instance with the specified namespace and value. + * + * @param namespace the namespace of the key + * @param value the value of the key + * @return a new {@link Key} instance + */ public static Key of(String namespace, String value) { return new Key(namespace, value); } + /** + * Creates a new {@link Key} instance from a string in the format "namespace:value". + * + * @param key the string representation of the key + * @return a new {@link Key} instance + */ + public static Key fromString(String key) { + String[] split = key.split(":", 2); + return of(split[0], split[1]); + } + @Override public int hashCode() { - int result = this.namespace.hashCode(); - result = (31 * result) + this.value.hashCode(); - return result; + return toString().hashCode(); } @Override @@ -43,4 +63,4 @@ public record Key(String namespace, String value) { public String toString() { return namespace + ":" + value; } -} +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/ListUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/ListUtils.java new file mode 100644 index 0000000..5e4b364 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/ListUtils.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.util.List; + +/** + * Utility class for handling operations related to lists. + */ +public class ListUtils { + + private ListUtils() { + } + + /** + * Converts an object to a list of strings. + * If the object is a string, it returns a list containing the string. + * If the object is a list, it casts and returns the list as a list of strings. + * + * @param obj the object to convert + * @return the resulting list of strings + * @throws IllegalArgumentException if the object cannot be converted to a list of strings + */ + @SuppressWarnings("unchecked") + public static List toList(final Object obj) { + if (obj instanceof String s) { + return List.of(s); + } else if (obj instanceof List list) { + return (List) list; + } + throw new IllegalArgumentException("Cannot convert " + obj + " to a list"); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/Overlay.java b/common/src/main/java/net/momirealms/customnameplates/common/util/OSUtils.java similarity index 54% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/Overlay.java rename to common/src/main/java/net/momirealms/customnameplates/common/util/OSUtils.java index a251da6..f2c11db 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/Overlay.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/OSUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,25 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bossbar; +package net.momirealms.customnameplates.common.util; -import org.jetbrains.annotations.NotNull; +public class OSUtils { -import java.util.Locale; - -public enum Overlay { - - NOTCHED_6, - NOTCHED_10, - NOTCHED_12, - NOTCHED_20, - PROGRESS; - - public static Overlay getOverlay(@NotNull String name) { - try { - return Overlay.valueOf(name.toUpperCase(Locale.ENGLISH)); - } catch (IllegalArgumentException e) { - throw new RuntimeException("Overlay: " + name + " doesn't exist"); + public static String getOSName() { + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + return "windows"; + } else if (os.contains("mac")) { + return "macos"; + } else if (os.contains("nux") || os.contains("nix")) { + return "linux"; + } else if (os.contains("freebsd")) { + return "freebsd"; + } else { + return "unknown"; } } } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/Pair.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Pair.java new file mode 100644 index 0000000..cdb92bd --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Pair.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.util.Objects; + +/** + * A generic class representing a pair of values. + * This class provides methods to create and access pairs of values. + * + * @param the type of the left value + * @param the type of the right value + */ +public record Pair(L left, R right) { + + /** + * Creates a new {@link Pair} with the specified left and right values. + * + * @param left the left value + * @param right the right value + * @param the type of the left value + * @param the type of the right value + * @return a new {@link Pair} with the specified values + */ + public static Pair of(final L left, final R right) { + return new Pair<>(left, right); + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + Pair pair = (Pair) object; + return Objects.equals(left, pair.left) && Objects.equals(right, pair.right); + } + + @Override + public int hashCode() { + return Objects.hash(this.left, this.right); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/RandomUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/RandomUtils.java new file mode 100644 index 0000000..2a983b0 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/RandomUtils.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Utility class for generating random values. + */ +public class RandomUtils { + + private final Random random; + + private RandomUtils() { + random = ThreadLocalRandom.current(); + } + + /** + * Static inner class to hold the singleton instance of RandomUtils. + */ + private static class SingletonHolder { + private static final RandomUtils INSTANCE = new RandomUtils(); + } + + /** + * Retrieves the singleton instance of RandomUtils. + * + * @return the singleton instance + */ + private static RandomUtils getInstance() { + return SingletonHolder.INSTANCE; + } + + /** + * Generates a random integer between the specified minimum and maximum values (inclusive). + * + * @param min the minimum value + * @param max the maximum value + * @return a random integer between min and max (inclusive) + */ + public static int generateRandomInt(int min, int max) { + return getInstance().random.nextInt(max - min + 1) + min; + } + + /** + * Generates a random double between the specified minimum and maximum values (inclusive). + * + * @param min the minimum value + * @param max the maximum value + * @return a random double between min and max (inclusive) + */ + public static double generateRandomDouble(double min, double max) { + return min + (max - min) * getInstance().random.nextDouble(); + } + + /** + * Generates a random float between the specified minimum and maximum values (inclusive). + * + * @param min the minimum value + * @param max the maximum value + * @return a random float between min and max (inclusive) + */ + public static float generateRandomFloat(float min, float max) { + return min + (max - min) * getInstance().random.nextFloat(); + } + + /** + * Generates a random boolean value. + * + * @return a random boolean value + */ + public static boolean generateRandomBoolean() { + return getInstance().random.nextBoolean(); + } + + /** + * Selects a random element from the specified array. + * + * @param array the array to select a random element from + * @param the type of the elements in the array + * @return a random element from the array + */ + public static T getRandomElementFromArray(T[] array) { + int index = getInstance().random.nextInt(array.length); + return array[index]; + } + + /** + * Generates a random value based on a triangular distribution. + * + * @param mode the mode (peak) of the distribution + * @param deviation the deviation from the mode + * @return a random value based on a triangular distribution + */ + public static double triangle(double mode, double deviation) { + return mode + deviation * (generateRandomDouble(0,1) - generateRandomDouble(0,1)); + } + + /** + * Selects a specified number of random elements from the given array. + * + * @param array the array to select random elements from + * @param count the number of random elements to select + * @param the type of the elements in the array + * @return an array containing the selected random elements + * @throws IllegalArgumentException if the count is greater than the array length + */ + public static T[] getRandomElementsFromArray(T[] array, int count) { + if (count > array.length) { + throw new IllegalArgumentException("Count cannot be greater than array length"); + } + + @SuppressWarnings("unchecked") + T[] result = (T[]) new Object[count]; + + for (int i = 0; i < count; i++) { + int index = getInstance().random.nextInt(array.length - i); + result[i] = array[index]; + array[index] = array[array.length - i - 1]; + } + + return result; + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java new file mode 100644 index 0000000..ef1b2e9 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/ReflectionUtils.java @@ -0,0 +1,323 @@ +package net.momirealms.customnameplates.common.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ReflectionUtils { + + public static Class getClazz(String... classes) { + for (String className : classes) { + Class clazz = getClazz(className); + if (clazz != null) { + return clazz; + } + } + return null; + } + + public static Class getClazz(String clazz) { + try { + return Class.forName(clazz); + } catch (Throwable e) { + return null; + } + } + + public static boolean classExists(@NotNull final String clazz) { + try { + Class.forName(clazz); + return true; + } catch (Throwable e) { + return false; + } + } + + public static boolean methodExists(@NotNull final Class clazz, @NotNull final String method, @NotNull final Class... parameterTypes) { + try { + clazz.getMethod(method, parameterTypes); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + @Nullable + public static Field getDeclaredField(final Class clazz, final String field) { + try { + return setAccessible(clazz.getDeclaredField(field)); + } catch (NoSuchFieldException e) { + return null; + } + } + + @NotNull + public static Field getDeclaredField(@NotNull Class clazz, @NotNull String... possibleNames) { + List possibleNameList = Arrays.asList(possibleNames); + for (Field field : clazz.getDeclaredFields()) { + if (possibleNameList.contains(field.getName())) { + return field; + } + } + throw new RuntimeException("Class " + clazz.getName() + " does not contain a field with possible names " + Arrays.toString(possibleNames)); + } + + @Nullable + public static Field getDeclaredField(final Class clazz, final int index) { + int i = 0; + for (final Field field : clazz.getDeclaredFields()) { + if (index == i) { + return setAccessible(field); + } + i++; + } + return null; + } + + @Nullable + public static Field getInstanceDeclaredField(final Class clazz, final int index) { + int i = 0; + for (final Field field : clazz.getDeclaredFields()) { + if (!Modifier.isStatic(field.getModifiers())) { + if (index == i) { + return setAccessible(field); + } + i++; + } + } + return null; + } + + @Nullable + public static Field getDeclaredField(final Class clazz, final Class type, int index) { + int i = 0; + for (final Field field : clazz.getDeclaredFields()) { + if (field.getType() == type) { + if (index == i) { + return setAccessible(field); + } + i++; + } + } + return null; + } + + @Nullable + public static Field getInstanceDeclaredField(@NotNull Class clazz, final Class type, int index) { + int i = 0; + for (final Field field : clazz.getDeclaredFields()) { + if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) { + if (index == i) { + return setAccessible(field); + } + i++; + } + } + return null; + } + + @NotNull + public static List getDeclaredFields(final Class clazz) { + List fields = new ArrayList<>(); + for (Field field : clazz.getDeclaredFields()) { + fields.add(setAccessible(field)); + } + return fields; + } + + @NotNull + public static List getInstanceDeclaredFields(@NotNull Class clazz) { + List list = new ArrayList<>(); + for (Field field : clazz.getDeclaredFields()) { + if (!Modifier.isStatic(field.getModifiers())) { + list.add(setAccessible(field)); + } + } + return list; + } + + @NotNull + public static List getDeclaredFields(@NotNull final Class clazz, @NotNull final Class type) { + List fields = new ArrayList<>(); + for (Field field : clazz.getDeclaredFields()) { + if (field.getType() == type) { + fields.add(setAccessible(field)); + } + } + return fields; + } + + @NotNull + public static List getInstanceDeclaredFields(@NotNull Class clazz, @NotNull Class type) { + List list = new ArrayList<>(); + for (Field field : clazz.getDeclaredFields()) { + if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) { + list.add(setAccessible(field)); + } + } + return list; + } + + @Nullable + public static Method getMethod(final Class clazz, final String[] possibleMethodNames, final Class... parameterTypes) { + outer: + for (Method method : clazz.getMethods()) { + if (method.getParameterCount() != parameterTypes.length) { + continue; + } + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) { + if (types[i] != parameterTypes[i]) { + continue outer; + } + } + for (String name : possibleMethodNames) { + if (name.equals(method.getName())) return method; + } + } + return null; + } + + @Nullable + public static Method getMethod(final Class clazz, Class returnType, final Class... parameterTypes) { + outer: + for (Method method : clazz.getMethods()) { + if (method.getParameterCount() != parameterTypes.length) { + continue; + } + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) { + if (types[i] != parameterTypes[i]) { + continue outer; + } + } + if (returnType.isAssignableFrom(method.getReturnType())) return method; + } + return null; + } + + @Nullable + public static Method getStaticMethod(final Class clazz, Class returnType, final Class... parameterTypes) { + outer: + for (Method method : clazz.getMethods()) { + if (method.getParameterCount() != parameterTypes.length) { + continue; + } + if (!Modifier.isStatic(method.getModifiers())) { + continue; + } + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) { + if (types[i] != parameterTypes[i]) { + continue outer; + } + } + if (returnType.isAssignableFrom(method.getReturnType())) return method; + } + return null; + } + + public static Method getStaticMethod(final Class clazz, int index) { + int i = 0; + for (Method method : clazz.getMethods()) { + if (Modifier.isStatic(method.getModifiers())) { + if (i == index) { + return setAccessible(method); + } + i++; + } + } + return null; + } + + @Nullable + public static Method getMethod(final Class clazz, int index) { + int i = 0; + for (Method method : clazz.getMethods()) { + if (i == index) { + return setAccessible(method); + } + i++; + } + return null; + } + + public static Method getMethodOrElseThrow(final Class clazz, final String[] possibleMethodNames, final Class[] parameterTypes) throws NoSuchMethodException { + Method method = getMethod(clazz, possibleMethodNames, parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(possibleMethodNames) + " with parameters " + + Arrays.toString(parameterTypes) + " in class " + clazz.getName()); + } + return method; + } + + @NotNull + public static List getMethods(@NotNull Class clazz, @NotNull Class returnType, @NotNull Class... parameterTypes) { + List list = new ArrayList<>(); + for (Method method : clazz.getMethods()) { + if (!returnType.isAssignableFrom(method.getReturnType()) // check type + || method.getParameterCount() != parameterTypes.length // check length + ) continue; + Class[] types = method.getParameterTypes(); + outer: { + for (int i = 0; i < types.length; i++) { + if (types[i] != parameterTypes[i]) { + break outer; + } + } + list.add(method); + } + } + return list; + } + + @NotNull + public static T setAccessible(@NotNull final T o) { + o.setAccessible(true); + return o; + } + + @Nullable + public static Constructor getConstructor(Class clazz, Class... parameterTypes) { + try { + return clazz.getConstructor(parameterTypes); + } catch (NoSuchMethodException | SecurityException ignore) { + return null; + } + } + + @Nullable + public static Constructor getDeclaredConstructor(Class clazz, Class... parameterTypes) { + try { + return setAccessible(clazz.getDeclaredConstructor(parameterTypes)); + } catch (NoSuchMethodException | SecurityException ignore) { + return null; + } + } + + @Nullable + public static Constructor getConstructor(Class clazz, int index) { + try { + Constructor[] constructors = clazz.getDeclaredConstructors(); + if (index < 0 || index >= constructors.length) { + throw new IndexOutOfBoundsException("Invalid constructor index: " + index); + } + return constructors[index]; + } catch (SecurityException e) { + return null; + } + } + + @NotNull + public static Constructor getTheOnlyConstructor(Class clazz) { + Constructor[] constructors = clazz.getConstructors(); + if (constructors.length != 1) { + throw new RuntimeException("This class is expected to have only one constructor but it has " + constructors.length); + } + return constructors[0]; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/SerializationUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/SerializationUtils.java new file mode 100644 index 0000000..3a2188a --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/SerializationUtils.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class SerializationUtils { + + private SerializationUtils() { + } + + static class ClassLoaderAwareObjectInputStream extends ObjectInputStream { + private static final Map> PRIMITIVE_TYPES = new HashMap<>(); + + static { + PRIMITIVE_TYPES.put("byte", byte.class); + PRIMITIVE_TYPES.put("short", short.class); + PRIMITIVE_TYPES.put("int", int.class); + PRIMITIVE_TYPES.put("long", long.class); + PRIMITIVE_TYPES.put("float", float.class); + PRIMITIVE_TYPES.put("double", double.class); + PRIMITIVE_TYPES.put("boolean", boolean.class); + PRIMITIVE_TYPES.put("char", char.class); + PRIMITIVE_TYPES.put("void", void.class); + } + + private final ClassLoader classLoader; + + ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { + super(in); + this.classLoader = classLoader; + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String className = desc.getName(); + try { + return Class.forName(className, false, classLoader); + } catch (ClassNotFoundException e) { + Class primitiveClass = PRIMITIVE_TYPES.get(className); + if (primitiveClass != null) { + return primitiveClass; + } + return super.resolveClass(desc); + } + } + } + + public static T clone(T object) { + if (object == null) { + return null; + } + byte[] objectData = serialize(object); + try (ByteArrayInputStream bais = new ByteArrayInputStream(objectData); + ClassLoaderAwareObjectInputStream ois = new ClassLoaderAwareObjectInputStream(bais, object.getClass().getClassLoader())) { + @SuppressWarnings("unchecked") + T clonedObject = (T) ois.readObject(); + return clonedObject; + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException("Exception occurred while cloning object", e); + } + } + + public static T deserialize(byte[] objectData) { + Objects.requireNonNull(objectData); + return deserialize(new ByteArrayInputStream(objectData)); + } + + public static T deserialize(InputStream inputStream) { + Objects.requireNonNull(inputStream); + try (ObjectInputStream ois = new ObjectInputStream(inputStream)) { + @SuppressWarnings("unchecked") + T obj = (T) ois.readObject(); + return obj; + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException("Exception occurred while deserializing object", e); + } + } + + public static byte[] serialize(Serializable obj) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(obj); + return baos.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("Exception occurred while serializing object", e); + } + } +} diff --git a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/TagMode.java b/common/src/main/java/net/momirealms/customnameplates/common/util/TriConsumer.java similarity index 80% rename from api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/TagMode.java rename to common/src/main/java/net/momirealms/customnameplates/common/util/TriConsumer.java index 6cdce17..9c92f2e 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/mechanic/nameplate/TagMode.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/TriConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,10 +15,8 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.api.mechanic.nameplate; +package net.momirealms.customnameplates.common.util; -public enum TagMode { - TEAM, - UNLIMITED, - DISABLE -} +public interface TriConsumer { + void accept(K k, V v, S s); +} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/ItemsAdderImageImpl.java b/common/src/main/java/net/momirealms/customnameplates/common/util/TriFunction.java similarity index 59% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/ItemsAdderImageImpl.java rename to common/src/main/java/net/momirealms/customnameplates/common/util/TriFunction.java index 4e26f02..74601fe 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/ItemsAdderImageImpl.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/TriFunction.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,15 +15,18 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bubble.image; +package net.momirealms.customnameplates.common.util; -import dev.lone.itemsadder.api.FontImages.FontImageWrapper; -import org.bukkit.entity.Player; +import java.util.Objects; +import java.util.function.Function; -public class ItemsAdderImageImpl implements ImageParser{ +public interface TriFunction { + R apply(T var1, U var2, V var3); - @Override - public String parse(Player player, String text) { - return FontImageWrapper.replaceFontImages(player, text).replace("§f","").replace("§r",""); + default TriFunction andThen(Function after) { + Objects.requireNonNull(after); + return (t, u, v) -> { + return after.apply(this.apply(t, u, v)); + }; } -} +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/Tristate.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Tristate.java new file mode 100644 index 0000000..e01271a --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Tristate.java @@ -0,0 +1,98 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customnameplates.common.util; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents three different states of a setting. + * + *

Possible values:

+ *

+ *
    + *
  • {@link #TRUE} - a positive setting
  • + *
  • {@link #FALSE} - a negative (negated) setting
  • + *
  • {@link #UNDEFINED} - a non-existent setting
  • + *
+ */ +public enum Tristate { + + /** + * A value indicating a positive setting + */ + TRUE(true), + + /** + * A value indicating a negative (negated) setting + */ + FALSE(false), + + /** + * A value indicating a non-existent setting + */ + UNDEFINED(false); + + /** + * Returns a {@link Tristate} from a boolean + * + * @param val the boolean value + * @return {@link #TRUE} or {@link #FALSE}, if the value is true or false, respectively. + */ + public static @NotNull Tristate of(boolean val) { + return val ? TRUE : FALSE; + } + + /** + * Returns a {@link Tristate} from a nullable boolean. + * + *

Unlike {@link #of(boolean)}, this method returns {@link #UNDEFINED} + * if the value is null.

+ * + * @param val the boolean value + * @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value + * is null, true or false, respectively. + */ + public static @NotNull Tristate of(Boolean val) { + return val == null ? UNDEFINED : val ? TRUE : FALSE; + } + + private final boolean booleanValue; + + Tristate(boolean booleanValue) { + this.booleanValue = booleanValue; + } + + /** + * Returns the value of the Tristate as a boolean. + * + *

A value of {@link #UNDEFINED} converts to false.

+ * + * @return a boolean representation of the Tristate. + */ + public boolean asBoolean() { + return this.booleanValue; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/Tuple.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Tuple.java new file mode 100644 index 0000000..ec21604 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Tuple.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.util.Objects; + +/** + * A generic class representing a tuple with three values. + * This class provides methods for creating and accessing tuples with three values. + * + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value + */ +public record Tuple(L left, M mid, R right) { + + /** + * Creates a new {@link Tuple} with the specified left, middle, and right values. + * + * @param left the left value + * @param mid the middle value + * @param right the right value + * @param the type of the left value + * @param the type of the middle value + * @param the type of the right value + * @return a new {@link Tuple} with the specified values + */ + public static Tuple of(final L left, final M mid, final R right) { + return new Tuple<>(left, mid, right); + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + Tuple tuple = (Tuple) object; + return Objects.equals(mid, tuple.mid) && Objects.equals(left, tuple.left) && Objects.equals(right, tuple.right); + } + + @Override + public int hashCode() { + return Objects.hash(left, mid, right); + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/UUIDUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/UUIDUtils.java new file mode 100644 index 0000000..66c43fd --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/UUIDUtils.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.math.BigInteger; +import java.util.UUID; + +/** + * Utility class for handling operations related to UUIDs. + * Provides methods for converting between different UUID formats and representations. + */ +public class UUIDUtils { + + /** + * Converts a UUID string without dashes to a {@link UUID} object. + * + * @param id the UUID string without dashes + * @return the corresponding {@link UUID} object, or null if the input string is null + */ + public static UUID fromUnDashedUUID(String id) { + return id == null ? null : new UUID( + new BigInteger(id.substring(0, 16), 16).longValue(), + new BigInteger(id.substring(16, 32), 16).longValue() + ); + } + + /** + * Converts a {@link UUID} object to a string without dashes. + * + * @param uuid the {@link UUID} object + * @return the UUID string without dashes + */ + public static String toUnDashedUUID(UUID uuid) { + return uuid.toString().replace("-", ""); + } + + /** + * Converts an integer array to a {@link UUID} object. + * The array must contain exactly four integers. + * + * @param array the integer array + * @return the corresponding {@link UUID} object + * @throws IllegalArgumentException if the array length is not four + */ + public static UUID uuidFromIntArray(int[] array) { + return new UUID((long)array[0] << 32 | (long)array[1] & 4294967295L, (long)array[2] << 32 | (long)array[3] & 4294967295L); + } + + /** + * Converts a {@link UUID} object to an integer array. + * The resulting array contains exactly four integers. + * + * @param uuid the {@link UUID} object + * @return the integer array representation of the UUID + */ + public static int[] uuidToIntArray(UUID uuid) { + long l = uuid.getMostSignificantBits(); + long m = uuid.getLeastSignificantBits(); + return leastMostToIntArray(l, m); + } + + /** + * Converts the most significant and least significant bits of a UUID to an integer array. + * + * @param uuidMost the most significant bits of the UUID + * @param uuidLeast the least significant bits of the UUID + * @return the integer array representation of the UUID bits + */ + private static int[] leastMostToIntArray(long uuidMost, long uuidLeast) { + return new int[]{(int)(uuidMost >> 32), (int)uuidMost, (int)(uuidLeast >> 32), (int)uuidLeast}; + } +} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/WeightUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/WeightUtils.java new file mode 100644 index 0000000..e06d29d --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/WeightUtils.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Utility class for selecting random items based on weights. + */ +@SuppressWarnings("DuplicatedCode") +public class WeightUtils { + + private WeightUtils() {} + + /** + * Get a random item from a list of pairs, each associated with a weight. + * + * @param pairs A list of pairs where the left element is the item and the right element is its weight. + * @param The type of items in the list. + * @return A randomly selected item from the list, or null if no item was selected. + */ + public static T getRandom(List> pairs) { + List available = new ArrayList<>(); + double[] weights = new double[pairs.size()]; + int index = 0; + for (Pair pair : pairs){ + double weight = pair.right(); + T key = pair.left(); + if (weight <= 0) continue; + available.add(key); + weights[index++] = weight; + } + return getRandom(weights, available, index); + } + + /** + * Get a random item from a map where each entry is associated with a weight. + * + * @param map A map where each entry's key is an item, and the value is its weight. + * @param The type of items in the map. + * @return A randomly selected item from the map, or null if no item was selected. + */ + public static T getRandom(Map map) { + List available = new ArrayList<>(); + double[] weights = new double[map.size()]; + int index = 0; + for (Map.Entry entry : map.entrySet()){ + double weight = entry.getValue(); + T key = entry.getKey(); + if (weight <= 0) continue; + available.add(key); + weights[index++] = weight; + } + return getRandom(weights, available, index); + } + + /** + * Get a random item from a list of items with associated weights. + * + * @param weights An array of weights corresponding to the available items. + * @param available A list of available items. + * @param effectiveSize The effective size of the array and list after filtering out items with non-positive weights. + * @param The type of items. + * @return A randomly selected item from the list, or null if no item was selected. + */ + private static T getRandom(double[] weights, List available, int effectiveSize) { + if (available.isEmpty()) return null; + double total = Arrays.stream(weights).sum(); + double[] weightRatios = new double[effectiveSize]; + for (int i = 0; i < effectiveSize; i++){ + weightRatios[i] = weights[i]/total; + } + double[] weightRange = new double[effectiveSize]; + double startPos = 0; + for (int i = 0; i < effectiveSize; i++) { + weightRange[i] = startPos + weightRatios[i]; + startPos += weightRatios[i]; + } + double random = Math.random(); + int pos = Arrays.binarySearch(weightRange, random); + + if (pos < 0) { + pos = -pos - 1; + } + if (pos < weightRange.length && random < weightRange[pos]) { + return available.get(pos); + } + return null; + } +} diff --git a/common/src/main/resources/custom-nameplates.properties b/common/src/main/resources/custom-nameplates.properties new file mode 100644 index 0000000..a050956 --- /dev/null +++ b/common/src/main/resources/custom-nameplates.properties @@ -0,0 +1,31 @@ +builder=${builder} +git=${git_version} +config=${config_version} +asm=${asm_version} +asm-commons=${asm_commons_version} +jar-relocator=${jar_relocator_version} +cloud-core=${cloud_core_version} +cloud-brigadier=${cloud_brigadier_version} +cloud-services=${cloud_services_version} +cloud-bukkit=${cloud_bukkit_version} +cloud-paper=${cloud_paper_version} +cloud-minecraft-extras=${cloud_minecraft_extras_version} +boosted-yaml=${boosted_yaml_version} +bstats-base=${bstats_version} +geantyref=${geantyref_version} +gson=${gson_version} +caffeine=${caffeine_version} +exp4j=${exp4j_version} +slf4j=${slf4j_version} +mongodb-driver-core=${mongodb_driver_version} +mariadb-java-client=${mariadb_driver_version} +mysql-connector-j=${mysql_driver_version} +hikari-cp=${hikari_version} +commons-pool=${commons_pool_version} +jedis=${jedis_version} +h2-driver=${h2_driver_version} +sqlite-driver=${sqlite_driver_version} +fontbox=${fontbox_version} +pdfbox-io=${pdfbox_io_version} +byte-buddy=${byte_buddy_version} +commons-io=${commons_io_version} \ No newline at end of file diff --git a/common/src/main/resources/translations/en.yml b/common/src/main/resources/translations/en.yml new file mode 100644 index 0000000..b53c669 --- /dev/null +++ b/common/src/main/resources/translations/en.yml @@ -0,0 +1,80 @@ +# Don't change this +config-version: "36" + +exception.invalid_syntax: "Invalid syntax. Correct syntax: " +exception.invalid_argument: "Invalid argument. Reason: " +exception.invalid_sender: " is not allowed to execute that command. Must be of type " +exception.unexpected: "An internal error occurred while attempting to perform this command" +exception.no_permission: "I'm sorry, but you do not have permission to perform this command" +exception.no_such_command: "Unknown command." +argument.entity.notfound.player: "" +argument.entity.notfound.entity: "" +argument.parse.failure.time: "'' is not a valid time format" +argument.parse.failure.material: "'' is not a valid material name" +argument.parse.failure.enchantment: "'' is not a valid enchantment" +argument.parse.failure.offlineplayer: "No player found for input ''" +argument.parse.failure.player: "No player found for input ''" +argument.parse.failure.world: "'' is not a valid Minecraft world" +argument.parse.failure.location.invalid_format: "'' is not a valid location. Required format is ' " +argument.parse.failure.location.mixed_local_absolute: "Cannot mix local and absolute coordinates. (either all coordinates use '^' or none do)" +argument.parse.failure.namespacedkey.namespace: "Invalid namespace ''. Must be [a-z0-9._-]" +argument.parse.failure.namespacedkey.key: "Invalid key ''. Must be [a-z0-9/._-]" +argument.parse.failure.namespacedkey.need_namespace: "Invalid input '', requires an explicit namespace" +argument.parse.failure.boolean: "Could not parse boolean from ''" +argument.parse.failure.number: "'' is not a valid number in the range to " +argument.parse.failure.char: "'' is not a valid character" +argument.parse.failure.string: "'' is not a valid string of type " +argument.parse.failure.uuid: "'' is not a valid UUID" +argument.parse.failure.enum: "'' is not one of the following: " +argument.parse.failure.regex: "'' does not match ''" +argument.parse.failure.flag.unknown: "Unknown flag ''" +argument.parse.failure.flag.duplicate_flag: "Duplicate flag ''" +argument.parse.failure.flag.no_flag_started: "No flag started. Don't know what to do with ''" +argument.parse.failure.flag.missing_argument: "Missing argument for ''" +argument.parse.failure.flag.no_permission: "You don't have permission to use ''" +argument.parse.failure.color: "'' is not a valid color" +argument.parse.failure.duration: "'' is not a duration format" +argument.parse.failure.aggregate.missing: "Missing component ''" +argument.parse.failure.aggregate.failure: "Invalid component '': " +argument.parse.failure.either: "Could not resolve or from ''" +argument.parse.failure.namedtextcolor: "'' is not a named text color" +command.reload.success: "Reloaded. Took ms." +command.reload.generating: "Generating resource packs..." +command.reload.generated: "Generated. Took ms." +command.debug.performance: + - "Average thread load in the past minute: % (/50ms)" + - "Check conditions:ms" + - "Update placeholders:ms" +command.nameplates.equip.failure.not_exists: " doesn't exist" +command.nameplates.equip.failure.permission: "You don't have permission to use this nameplate" +command.nameplates.equip.failure.no_change: "Nothing changed as you have already equipped this nameplate" +command.nameplates.equip.success: "Successfully equipped [click to preview]" # use for display name +command.nameplates.unequip.failure.not_equip: "You are not equipping any nameplate" +command.nameplates.unequip.success: "You removed your nameplate" +command.nameplates.preview.failure.cooldown: "Previewing is still Ongoing!" +command.nameplates.preview.success: "Now previewing your nameplate (Go to third person to view)!" +command.nameplates.list.failure.none: "You don't have any nameplate yet" +command.nameplates.list.success: "Available nameplates: " # use for display names +command.nameplates.list.delimiter: ", " +command.nameplates.force_equip.failure.not_exists: "Failed to equip nameplate for because doesn't exist" +command.nameplates.force_equip.success: "Forced to equip " # use for display name +command.nameplates.force_unequip.success: "Removed 's nameplate" +command.nameplates.force_preview.success: "Forced to preview nameplates" +command.bubbles.equip.failure.not_exists: " doesn't exist" +command.bubbles.equip.failure.permission: "You don't have permission to use this bubble" +command.bubbles.equip.failure.no_change: "Nothing changed as you have already equipped this bubble" +command.bubbles.equip.success: "Successfully equipped " # use for display name +command.bubbles.unequip.failure.not_equip: "You don't have any bubble enabled" +command.bubbles.unequip.success: "You removed your bubbles" +command.bubbles.list.failure.none: "You don't have any bubble yet" +command.bubbles.list.success: "Available bubbles: " # use for display names +command.bubbles.list.delimiter: ", " +command.bubbles.force_equip.failure.not_exists: "Failed to apply bubbles to because doesn't exist" +command.bubbles.force_equip.success: "Forced to apply to " # use for display name +command.bubbles.force_unequip.success: "Removed 's bubbles" + + + + + + diff --git a/common/src/main/resources/translations/zh_cn.yml b/common/src/main/resources/translations/zh_cn.yml new file mode 100644 index 0000000..3f4be76 --- /dev/null +++ b/common/src/main/resources/translations/zh_cn.yml @@ -0,0 +1,74 @@ +# 别动这个 +config-version: "36" + +exception.invalid_syntax: "无效语法. 正确语法:" +exception.invalid_argument: "无效参数. 原因:" +exception.invalid_sender: " 不允许执行该命令. 执行者必须是 " +exception.unexpected: "执行该命令时发生内部错误" +exception.no_permission: "抱歉, 您没有权限执行该命令" +exception.no_such_command: "未知命令" +argument.entity.notfound.player: "找不到玩家 ''" +argument.entity.notfound.entity: "找不到实体 ''" +argument.parse.failure.time: "'' 不是有效的时间格式" +argument.parse.failure.material: "'' 不是有效的材料" +argument.parse.failure.enchantment: "'' 不是有效的魔咒" +argument.parse.failure.offlineplayer: "输入的玩家 '' 已离线" +argument.parse.failure.player: "找不到输入的玩家 ''" +argument.parse.failure.world: "'' 不是有效的 Minecraft 世界名称" +argument.parse.failure.location.invalid_format: "'' 不是有效的位置格式.必须格式为 ' '" +argument.parse.failure.location.mixed_local_absolute: "不能混用相对和绝对坐标.坐标要么全部使用 '^',要么全部不用" +argument.parse.failure.namespacedkey.namespace: "无效的命名空间 ''.必须为 [a-z0-9._-]" +argument.parse.failure.namespacedkey.key: "无效的键 ''.必须为 [a-z0-9/._-]" +argument.parse.failure.namespacedkey.need_namespace: "无效的输入 '', 需要显式指定命名空间" +argument.parse.failure.boolean: "无法解析布尔值 ''" +argument.parse.failure.number: "'' 不是从 范围内的有效数字" +argument.parse.failure.char: "'' 不是有效的字符" +argument.parse.failure.string: "'' 不是类型为 的有效字符串" +argument.parse.failure.uuid: "'' 不是有效的 UUID" +argument.parse.failure.enum: "'' 不是以下任何一种情况之一: " +argument.parse.failure.regex: "'' 不匹配 ''" +argument.parse.failure.flag.unknown: "未知标志 ''" +argument.parse.failure.flag.duplicate_flag: "重复的标志 ''" +argument.parse.failure.flag.no_flag_started: "没有开始标志. 不知道如何处理 ''" +argument.parse.failure.flag.missing_argument: "缺少 '' 参数" +argument.parse.failure.flag.no_permission: "您没有权限使用 ''" +argument.parse.failure.color: "'' 不是有效的颜色" +argument.parse.failure.duration: "'' 不是有效的持续时间格式" +argument.parse.failure.aggregate.missing: "缺少组件 ''" +argument.parse.failure.aggregate.failure: "无效的组件 '': " +argument.parse.failure.either: "无法从 '' 解析 " +argument.parse.failure.namedtextcolor: "'' 不是颜色代码" +command.reload.success: "重新加载完成. 耗时 毫秒" +command.reload.generating: "生成资源包中..." +command.reload.generated: "生成完毕. 耗时 毫秒" +command.debug.performance: + - "过去一分钟的线程负载: % (/50ms)" + - "刷新条件耗时:ms" + - "更新变量耗时:ms" +command.nameplates.equip.failure.not_exists: "铭牌 [] 不存在" +command.nameplates.equip.failure.permission: "你尚未解锁这个铭牌" +command.nameplates.equip.failure.no_change: "你已经佩戴这个铭牌了" +command.nameplates.equip.success: "你佩戴上了 [点击预览]" +command.nameplates.unequip.failure.not_equip: "你当前未佩戴任何铭牌" +command.nameplates.unequip.success: "你移除了你的铭牌" +command.nameplates.preview.failure.cooldown: "上一轮预览尚未结束" +command.nameplates.preview.success: "你正在预览你的铭牌 (前往第三人称查看)!" +command.nameplates.list.failure.none: "你尚未拥有任何铭牌" +command.nameplates.list.success: "可用的铭牌: " +command.nameplates.list.delimiter: ", " +command.nameplates.force_equip.failure.not_exists: "由于 不存在,无法为 佩戴铭牌" +command.nameplates.force_equip.success: "强制 佩戴了铭牌 " +command.nameplates.force_unequip.success: "移除了 的铭牌" +command.nameplates.force_preview.success: "强制 预览他的铭牌" +command.bubbles.equip.failure.not_exists: "气泡 不存在" +command.bubbles.equip.failure.permission: "你尚未解锁这个气泡" +command.bubbles.equip.failure.no_change: "你已经应用这个气泡了" +command.bubbles.equip.success: "你正在使用气泡 " +command.bubbles.unequip.failure.not_equip: "你尚未应用气泡" +command.bubbles.unequip.success: "你移除了你的气泡" +command.bubbles.list.failure.none: "你尚未拥有任何气泡" +command.bubbles.list.success: "可用的气泡: " +command.bubbles.list.delimiter: ", " +command.bubbles.force_equip.failure.not_exists: "由于 不存在,无法为 应用气泡" +command.bubbles.force_equip.success: "强制 应用了气泡 " +command.bubbles.force_unequip.success: "移除了 的气泡" \ No newline at end of file diff --git a/compatibility/build.gradle.kts b/compatibility/build.gradle.kts new file mode 100644 index 0000000..54b6949 --- /dev/null +++ b/compatibility/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + id("io.github.goooler.shadow") version "8.1.8" +} + +repositories { + maven("https://papermc.io/repo/repository/maven-public/") // paper + maven("https://repo.william278.net/releases/") // husk + maven("https://jitpack.io/") + maven("https://repo.oraxen.com/releases/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // papi + maven("https://repo.essentialsx.net/releases/") // ess + maven("https://repo.md-5.net/content/groups/public/") // disguise + maven("https://repo.opencollab.dev/main/") // geyser +} + +dependencies { + compileOnly(project(":api")) + compileOnly(project(":common")) + // Platform + compileOnly("dev.folia:folia-api:${rootProject.properties["paper_version"]}-R0.1-SNAPSHOT") + // Chat + compileOnly(files("libs/VentureChat-3.7.1.jar")) + compileOnly(files("libs/TrChat-2.0.11.jar")) + compileOnly(files("libs/carbonchat-paper-3.0.0-beta.27.jar")) + compileOnly(files("libs/AdvancedChat-1.3.7.jar")) + compileOnly(files("libs/CMIAPI-9.7.4.1.jar")) + compileOnly("net.william278.huskchat:huskchat-bukkit:3.0.4") + compileOnly("net.essentialsx:EssentialsX:2.20.1") + compileOnly("net.essentialsx:EssentialsXChat:2.20.1") + // Emoji + compileOnly("com.github.LoneDev6:api-itemsadder:3.6.3-beta-14") + compileOnly("io.th0rgal:oraxen:1.182.0") + // PAPI + compileOnly("me.clip:placeholderapi:${rootProject.properties["placeholder_api_version"]}") + // Disguise + compileOnly("LibsDisguises:LibsDisguises:10.0.44") + // Geyser + compileOnly("org.geysermc.geyser:api:2.4.2-SNAPSHOT") + // Floodgate + compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT") +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +tasks.withType { + options.encoding = "UTF-8" + options.release.set(17) + dependsOn(tasks.clean) +} \ No newline at end of file diff --git a/paper/libs/AdvancedChat-1.3.7.jar b/compatibility/libs/AdvancedChat-1.3.7.jar similarity index 100% rename from paper/libs/AdvancedChat-1.3.7.jar rename to compatibility/libs/AdvancedChat-1.3.7.jar diff --git a/compatibility/libs/CMIAPI-9.7.4.1.jar b/compatibility/libs/CMIAPI-9.7.4.1.jar new file mode 100644 index 0000000..62f67a5 Binary files /dev/null and b/compatibility/libs/CMIAPI-9.7.4.1.jar differ diff --git a/paper/libs/TrChat-2.0.11.jar b/compatibility/libs/TrChat-2.0.11.jar similarity index 100% rename from paper/libs/TrChat-2.0.11.jar rename to compatibility/libs/TrChat-2.0.11.jar diff --git a/paper/libs/VentureChat-3.7.1.jar b/compatibility/libs/VentureChat-3.7.1.jar similarity index 100% rename from paper/libs/VentureChat-3.7.1.jar rename to compatibility/libs/VentureChat-3.7.1.jar diff --git a/compatibility/libs/carbonchat-paper-3.0.0-beta.27.jar b/compatibility/libs/carbonchat-paper-3.0.0-beta.27.jar new file mode 100644 index 0000000..599fbaa Binary files /dev/null and b/compatibility/libs/carbonchat-paper-3.0.0-beta.27.jar differ diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/NameplatesExpansion.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/NameplatesExpansion.java new file mode 100644 index 0000000..d8ef8c6 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/NameplatesExpansion.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import me.clip.placeholderapi.expansion.Relational; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.TickStampData; +import net.momirealms.customnameplates.api.placeholder.Placeholder; +import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +public class NameplatesExpansion extends PlaceholderExpansion implements Relational { + + private final CustomNameplates plugin; + + public NameplatesExpansion(CustomNameplates plugin) { + this.plugin = plugin; + } + + @Override + public @NotNull String getIdentifier() { + return "nameplates"; + } + + @Override + public @NotNull String getAuthor() { + return "XiaoMoMi"; + } + + @Override + public @NotNull String getVersion() { + return "3.0"; + } + + @Override + public boolean persist() { + return true; + } + + @Override + public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { + Placeholder placeholder = plugin.getPlaceholderManager().getRegisteredPlaceholder("%np_" + params + "%"); + if (placeholder instanceof PlayerPlaceholder playerPlaceholder) { + CNPlayer cnPlayer = player == null ? null : plugin.getPlayer(player.getUniqueId()); + if (placeholder.children().isEmpty()) { + return playerPlaceholder.request(cnPlayer); + } + if (cnPlayer != null) { + cnPlayer.forceUpdate(Set.of(placeholder), Collections.emptySet()); + return cnPlayer.getData(placeholder); + } else { + try { + return playerPlaceholder.request(null); + } catch (NullPointerException e) { + plugin.getPluginLogger().warn("%nameplates_" + params + "% contains a placeholder that requires a player as the parameter"); + } + } + } + return null; + } + + @Override + public String onPlaceholderRequest(Player p1, Player p2, String params) { + CNPlayer cnPlayer1 = p1 == null ? null : plugin.getPlayer(p1.getUniqueId()); + CNPlayer cnPlayer2 = p2 == null ? null : plugin.getPlayer(p2.getUniqueId()); + if (p1 == null || p2 == null) { + return null; + } + Placeholder placeholder = plugin.getPlaceholderManager().getRegisteredPlaceholder("%rel_np_" + params + "%"); + if (placeholder != null) { + cnPlayer1.forceUpdate(Set.of(placeholder), Set.of(cnPlayer2)); + return Optional.ofNullable(cnPlayer1.getRelationalValue(placeholder, cnPlayer2)).map(TickStampData::data).orElse(placeholder.id()); + } + return null; + } +} diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/bedrock/FloodGateUtils.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/bedrock/FloodGateUtils.java new file mode 100644 index 0000000..89c44db --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/bedrock/FloodGateUtils.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.bedrock; + +import org.geysermc.floodgate.api.FloodgateApi; + +import java.util.UUID; + +public class FloodGateUtils { + + public static boolean isBedrockPlayer(UUID uuid) { + return FloodgateApi.getInstance().isFloodgatePlayer(uuid); + } +} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/GeyserUtils.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/bedrock/GeyserUtils.java similarity index 92% rename from paper/src/main/java/net/momirealms/customnameplates/paper/util/GeyserUtils.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/bedrock/GeyserUtils.java index c28e54c..4b57960 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/GeyserUtils.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/bedrock/GeyserUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.util; +package net.momirealms.customnameplates.bukkit.compatibility.bedrock; import org.geysermc.api.Geyser; diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/AdvancedChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/AdvancedChatProvider.java similarity index 50% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/AdvancedChatProvider.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/AdvancedChatProvider.java index 7ec5dad..c297ae3 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/AdvancedChatProvider.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/AdvancedChatProvider.java @@ -15,40 +15,45 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; +package net.momirealms.customnameplates.bukkit.compatibility.chat; import net.advancedplugins.chat.api.AdvancedChannelChatEvent; import net.advancedplugins.chat.api.AdvancedChatAPI; import net.advancedplugins.chat.channel.ChatChannel; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; -import java.util.List; +import java.util.Objects; -public class AdvancedChatProvider extends AbstractChatProvider { +public class AdvancedChatProvider extends AbstractChatMessageProvider implements Listener { - public AdvancedChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); + public AdvancedChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); + } + + @EventHandler(ignoreCancelled = true) + public void onAdvancedChat(AdvancedChannelChatEvent event) { + String channel = event.getChannel().getSectionName(); + if (event.getSender() instanceof Player player) { + if (!player.isOnline()) return; + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) return; + plugin.getScheduler().async().execute(() -> { + manager.onChat(cnPlayer, event.getMessage(), channel); + }); + } } @Override - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); - } - - @Override - public void unregister() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - ChatChannel chatChannel = AdvancedChatAPI.getApi().getPlayerChatChannel(player.getUniqueId()); + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + ChatChannel chatChannel = AdvancedChatAPI.getApi().getPlayerChatChannel(player.uuid()); if (chatChannel == null) { return false; } @@ -56,33 +61,26 @@ public class AdvancedChatProvider extends AbstractChatProvider { } @Override - public boolean canJoinChannel(Player player, String channelID) { - List channelList = AdvancedChatAPI.getApi().getAllowedChatChannels(player); - for (ChatChannel chatChannel : channelList) { - if (chatChannel.getSectionName().equals(channelID)) { - return true; - } + public boolean canJoinChannel(CNPlayer player, String channelID) { + ChatChannel chatChannel = AdvancedChatAPI.getApi().getPlayerChatChannel(player.uuid()); + if (chatChannel == null) { + return false; } - return false; + return chatChannel.getSectionName().equals(channelID); } @Override - public boolean isIgnoring(Player sender, Player receiver) { - return AdvancedChatAPI.getApi().getIgnoredPlayers(receiver).contains(sender.getName()); + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + return AdvancedChatAPI.getApi().getIgnoredPlayers((Player) receiver.player()).contains(sender.name()); } - @EventHandler (ignoreCancelled = true) - public void onAdvancedChat(AdvancedChannelChatEvent event) { - String channel = event.getChannel().getSectionName(); - for (String black : chatBubblesManager.getBlacklistChannels()) { - if (channel.equals(black)) return; - } - if (event.getSender() instanceof Player player) { - if (!player.isOnline()) - return; - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> { - chatBubblesManager.onChat(player, event.getMessage(), channel); - }); - } + @Override + public void register() { + Bukkit.getPluginManager().registerEvents(this, Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("CustomNameplates"))); + } + + @Override + public void unregister() { + HandlerList.unregisterAll(this); } } \ No newline at end of file diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/AsyncChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/AsyncChatProvider.java new file mode 100644 index 0000000..789e05d --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/AsyncChatProvider.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.chat; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.Objects; + +public class AsyncChatProvider extends AbstractChatMessageProvider implements Listener { + + public AsyncChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); + } + + // This event is not async sometimes + @EventHandler + public void onChat(AsyncPlayerChatEvent event) { + if (!ConfigManager.chatUnsafe() && event.isCancelled()) return; + CNPlayer player = plugin.getPlayer(event.getPlayer().getUniqueId()); + if (player == null) return; + plugin.getScheduler().async().execute(() -> { + manager.onChat(player, event.getMessage(), "global"); + }); + } + + @Override + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + return true; + } + + @Override + public boolean canJoinChannel(CNPlayer player, String channelID) { + return true; + } + + @Override + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + return false; + } + + @Override + public void register() { + Bukkit.getPluginManager().registerEvents(this, Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("CustomNameplates"))); + } + + @Override + public void unregister() { + HandlerList.unregisterAll(this); + } +} \ No newline at end of file diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/CarbonChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/CarbonChatProvider.java new file mode 100644 index 0000000..534ce1d --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/CarbonChatProvider.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.chat; + +import net.draycia.carbon.api.CarbonChat; +import net.draycia.carbon.api.channels.ChannelRegistry; +import net.draycia.carbon.api.channels.ChatChannel; +import net.draycia.carbon.api.event.CarbonEventSubscription; +import net.draycia.carbon.api.event.events.CarbonChatEvent; +import net.draycia.carbon.api.users.CarbonPlayer; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +public class CarbonChatProvider extends AbstractChatMessageProvider { + + private final CarbonChat api; + private CarbonEventSubscription subscription; + private final Method originalMessageMethod; + private final Method channelKeyMethod; + private final Method getChannelByKeyMethod; + private final Method getKeyAsStringMethod; + private final Method getKeyFromStringMethod; + private final Object miniMessageInstance; + private final Method serializeComponentMethod; + + public CarbonChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); + this.api = net.draycia.carbon.api.CarbonChatProvider.carbonChat(); + try { + this.originalMessageMethod = CarbonChatEvent.class.getMethod("originalMessage"); + this.channelKeyMethod = ChatChannel.class.getMethod("key"); + Class keyClass = Class.forName("net{}kyori{}adventure{}key{}Key".replace("{}", ".")); + this.getKeyAsStringMethod = keyClass.getMethod("asString"); + this.getKeyFromStringMethod = keyClass.getMethod("key", String.class); + this.getChannelByKeyMethod = ChannelRegistry.class.getMethod("channel", keyClass); + Class miniMessageClass = Class.forName("net{}kyori{}adventure{}text{}minimessage{}MiniMessage".replace("{}", ".")); + Method miniMessageInstanceGetMethod = miniMessageClass.getMethod("miniMessage"); + this.miniMessageInstance = miniMessageInstanceGetMethod.invoke(null); + Class componentClass = Class.forName("net{}kyori{}adventure{}text{}Component".replace("{}", ".")); + this.serializeComponentMethod = miniMessageClass.getMethod("serialize", componentClass); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public void register() { + subscription = api.eventHandler().subscribe(CarbonChatEvent.class, event -> { + if (event.cancelled()) + return; + try { + ChatChannel chatChannel = event.chatChannel(); + Object key = getChannelKey(chatChannel); + if (key == null) return; + String channel = (String) getKeyAsStringMethod.invoke(key); + Player player = Bukkit.getPlayer(event.sender().uuid()); + if (player == null || !player.isOnline()) + return; + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) return; + Object component = getComponentFromEvent(event); + String message = (String) serializeComponentMethod.invoke(miniMessageInstance, component); + plugin.getScheduler().async().execute(() -> { + manager.onChat(cnPlayer, message, channel); + }); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }); + } + + @Override + public void unregister() { + if (subscription != null) { + subscription.dispose(); + } + } + + @Nullable + private CarbonPlayer carbonPlayer(UUID uuid) { + CarbonPlayer carbonPlayer = null; + for (CarbonPlayer cPlayer : api.server().players()) { + if (cPlayer.uuid().equals(uuid)) { + carbonPlayer = cPlayer; + break; + } + } + return carbonPlayer; + } + + private Object getChannelKey(ChatChannel channel) { + try { + return this.channelKeyMethod.invoke(channel); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private Object getComponentFromEvent(CarbonChatEvent event) { + try { + return this.originalMessageMethod.invoke(event); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + CarbonPlayer cPlayer = null; + for (CarbonPlayer carbonPlayer : api.server().players()) { + if (carbonPlayer.uuid().equals(player.uuid())) { + cPlayer = carbonPlayer; + break; + } + } + if (cPlayer == null) { + return false; + } + try { + ChatChannel selectedChannel = cPlayer.selectedChannel(); + ChatChannel currentChannel = selectedChannel != null ? selectedChannel : api.channelRegistry().defaultChannel(); + Object key = getChannelKey(currentChannel); + String str = (String) getKeyAsStringMethod.invoke(key); + return str.equals(channelID); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean canJoinChannel(CNPlayer player, String channelID) { + ChannelRegistry registry = api.channelRegistry(); + Object key; + try { + key = getKeyFromStringMethod.invoke(null, channelID); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + if (key == null) { + return false; + } + ChatChannel channel; + try { + channel = (ChatChannel) getChannelByKeyMethod.invoke(registry, key); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + if (channel == null) { + return false; + } + String perm = channel.permission(); + if (perm == null) { + return true; + } + return player.hasPermission(perm); + } + + @Override + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + CarbonPlayer sPlayer = carbonPlayer(sender.uuid()); + CarbonPlayer rPlayer = carbonPlayer(receiver.uuid()); + if (sPlayer == null || rPlayer == null) { + return false; + } + return rPlayer.ignoring(sPlayer.uuid()); + } +} \ No newline at end of file diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/EssentialsChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/EssentialsChatProvider.java new file mode 100644 index 0000000..569e9a8 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/EssentialsChatProvider.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.chat; + +import net.essentialsx.api.v2.events.chat.ChatEvent; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +import java.util.Objects; + +public class EssentialsChatProvider extends AbstractChatMessageProvider implements Listener { + + public EssentialsChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); + } + + @Override + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + return true; + } + + @Override + public boolean canJoinChannel(CNPlayer player, String channelID) { + return true; + } + + @Override + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + return false; + } + + @EventHandler(ignoreCancelled = true) + public void onEssChat(ChatEvent event) { + String channel = event.getChatType().key(); + Player player = Bukkit.getPlayer(event.getPlayer().getUniqueId()); + if (player == null || !player.isOnline()) + return; + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) + return; + plugin.getScheduler().async().execute(() -> manager.onChat(cnPlayer, event.getMessage(), channel)); + } + + @Override + public void register() { + Bukkit.getPluginManager().registerEvents(this, Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("CustomNameplates"))); + } + + @Override + public void unregister() { + HandlerList.unregisterAll(this); + } +} diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/HuskChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/HuskChatProvider.java new file mode 100644 index 0000000..d7cae81 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/HuskChatProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.chat; + +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; +import net.william278.huskchat.BukkitHuskChat; +import net.william278.huskchat.api.BukkitHuskChatAPI; +import net.william278.huskchat.channel.Channel; +import net.william278.huskchat.event.ChatMessageEvent; +import net.william278.huskchat.user.OnlineUser; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +import java.util.Objects; +import java.util.Optional; + +public class HuskChatProvider extends AbstractChatMessageProvider implements Listener { + + private final BukkitHuskChat huskChat; + + public HuskChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); + this.huskChat = (BukkitHuskChat) Bukkit.getPluginManager().getPlugin("HuskChat"); + } + + @EventHandler(ignoreCancelled = true) + public void onHuskChat(ChatMessageEvent event) { + String channel = event.getChannelId(); + Player player = Bukkit.getPlayer(event.getSender().getUuid()); + if (player == null || !player.isOnline()) + return; + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) { + return; + } + plugin.getScheduler().async().execute(() -> { + manager.onChat(cnPlayer, event.getMessage(), channel); + }); + } + + @Override + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + OnlineUser onlineUser = BukkitHuskChatAPI.getInstance().adaptPlayer((Player) player.player()); + Optional channel = BukkitHuskChatAPI.getInstance().getPlayerChannel(onlineUser); + return channel.map(s -> s.equals(channelID)).orElse(false); + } + + @Override + public boolean canJoinChannel(CNPlayer player, String channelID) { + Optional channel = huskChat.getChannels().getChannel(channelID); + if (channel.isEmpty()) { + return false; + } + Optional optionalReceivePerm = channel.get().getPermissions().getReceive(); + return optionalReceivePerm.map(player::hasPermission).orElse(true); + } + + @Override + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + return false; + } + + @Override + public void register() { + Bukkit.getPluginManager().registerEvents(this, Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("CustomNameplates"))); + } + + @Override + public void unregister() { + HandlerList.unregisterAll(this); + } +} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/TrChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/TrChatProvider.java similarity index 61% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/TrChatProvider.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/TrChatProvider.java index f72559f..61b620a 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/TrChatProvider.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/TrChatProvider.java @@ -15,78 +15,81 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; +package net.momirealms.customnameplates.bukkit.compatibility.chat; import me.arasple.mc.trchat.TrChat; import me.arasple.mc.trchat.api.event.TrChatEvent; import me.arasple.mc.trchat.module.display.channel.Channel; import me.arasple.mc.trchat.module.internal.filter.FilteredObject; import me.arasple.mc.trchat.taboolib.platform.BukkitAdapter; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import net.momirealms.customnameplates.api.util.LogUtils; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; -public class TrChatProvider extends AbstractChatProvider { +import java.util.Objects; + +public class TrChatProvider extends AbstractChatMessageProvider implements Listener { private final BukkitAdapter adapter; - public TrChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); + public TrChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); this.adapter = new BukkitAdapter(); } + @EventHandler(ignoreCancelled = true) + public void onTrChat(TrChatEvent event) { + if (!event.getForward()) return; + Channel channel = event.getChannel(); + String channelName = channel.getId(); + Player player = event.getSession().getPlayer(); + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null || !cnPlayer.isOnline()) { + return; + } + FilteredObject object = TrChat.INSTANCE.api().getFilterManager().filter(event.getMessage(), adapter.adaptPlayer(event.getPlayer()), true); + plugin.getScheduler().async().execute(() -> { + manager.onChat(cnPlayer, object.getFiltered(), channelName); + }); + } + + @Override + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + if (TrChat.INSTANCE.api().getChannelManager().getChannel(channelID) instanceof Channel channel) { + return channel.getListeners().contains(player.name()); + } else { + return false; + } + } + + @Override + public boolean canJoinChannel(CNPlayer player, String channelID) { + if (TrChat.INSTANCE.api().getChannelManager().getChannel(channelID) instanceof Channel channel) { + String perm = channel.getSettings().getJoinPermission(); + return player.hasPermission(perm); + } else { + return false; + } + } + + @Override + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + return false; + } + @Override public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); + Bukkit.getPluginManager().registerEvents(this, Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("CustomNameplates"))); } @Override public void unregister() { HandlerList.unregisterAll(this); } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - if (TrChat.INSTANCE.api().getChannelManager().getChannel(channelID) instanceof Channel channel) { - return channel.getListeners().contains(player.getName()); - } else { - LogUtils.warn("Channel " + channelID + " doesn't exist."); - return false; - } - } - - @Override - public boolean canJoinChannel(Player player, String channelID) { - if (TrChat.INSTANCE.api().getChannelManager().getChannel(channelID) instanceof Channel channel) { - String perm = channel.getSettings().getJoinPermission(); - return player.hasPermission(perm); - } else { - LogUtils.warn("Channel " + channelID + " doesn't exist."); - return false; - } - } - - @Override - public boolean isIgnoring(Player sender, Player receiver) { - return false; - } - - @EventHandler (ignoreCancelled = true) - public void onTrChat(TrChatEvent event) { - if (!event.getForward()) return; - Channel channel = event.getChannel(); - String channelName = channel.getId(); - for (String black : chatBubblesManager.getBlacklistChannels()) { - if (channelName.equals(black)) return; - } - FilteredObject object = TrChat.INSTANCE.api().getFilterManager().filter(event.getMessage(), adapter.adaptPlayer(event.getPlayer()), true); - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> { - chatBubblesManager.onChat(event.getSession().getPlayer(), object.getFiltered(), channelName); - }); - } } \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/VentureChatProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/VentureChatProvider.java similarity index 56% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/VentureChatProvider.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/VentureChatProvider.java index e06f773..cb8fe78 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/VentureChatProvider.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/chat/VentureChatProvider.java @@ -15,48 +15,57 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; +package net.momirealms.customnameplates.bukkit.compatibility.chat; import mineverse.Aust1n46.chat.api.MineverseChatAPI; import mineverse.Aust1n46.chat.api.MineverseChatPlayer; import mineverse.Aust1n46.chat.api.events.VentureChatEvent; import mineverse.Aust1n46.chat.channel.ChatChannel; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import net.momirealms.customnameplates.api.util.LogUtils; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.bubble.chat.AbstractChatMessageProvider; +import net.momirealms.customnameplates.api.feature.bubble.chat.ChatManager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; -public class VentureChatProvider extends AbstractChatProvider { +import java.util.Objects; - public VentureChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); +public class VentureChatProvider extends AbstractChatMessageProvider implements Listener { + + public VentureChatProvider(CustomNameplates plugin, ChatManager manager) { + super(plugin, manager); + } + + @EventHandler(ignoreCancelled = true) + public void onVentureChat(VentureChatEvent event) { + String channelName = event.getChannel().getName(); + final MineverseChatPlayer chatPlayer = event.getMineverseChatPlayer(); + if (chatPlayer == null) { + return; + } + Player player = chatPlayer.getPlayer(); + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer == null) { + return; + } + plugin.getScheduler().async().execute(() -> { + manager.onChat(cnPlayer, event.getChat(), channelName); + }); } @Override - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); - } - - @Override - public void unregister() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - MineverseChatPlayer mcp = MineverseChatAPI.getOnlineMineverseChatPlayer(player); + public boolean hasJoinedChannel(CNPlayer player, String channelID) { + MineverseChatPlayer mcp = MineverseChatAPI.getOnlineMineverseChatPlayer((Player) player.player()); return mcp.getCurrentChannel().getName().equals(channelID); } @Override - public boolean canJoinChannel(Player player, String channelID) { + public boolean canJoinChannel(CNPlayer player, String channelID) { ChatChannel channel = ChatChannel.getChannel(channelID); if (channel == null) { - LogUtils.warn("Channel " + channelID + " doesn't exist."); return false; } if (channel.hasPermission()) { @@ -66,22 +75,18 @@ public class VentureChatProvider extends AbstractChatProvider { } @Override - public boolean isIgnoring(Player sender, Player receiver) { - return false; + public boolean isIgnoring(CNPlayer sender, CNPlayer receiver) { + MineverseChatPlayer mcp = MineverseChatAPI.getOnlineMineverseChatPlayer((Player) receiver.player()); + return mcp.getIgnores().contains(sender.uuid()); } - @EventHandler (ignoreCancelled = true) - public void onVentureChat(VentureChatEvent event) { - String channelName = event.getChannel().getName(); - for (String channel : chatBubblesManager.getBlacklistChannels()) { - if (channelName.equals(channel)) return; - } - final MineverseChatPlayer chatPlayer = event.getMineverseChatPlayer(); - if (chatPlayer == null) { - return; - } - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> { - chatBubblesManager.onChat(chatPlayer.getPlayer(), event.getChat(), channelName); - }); + @Override + public void register() { + Bukkit.getPluginManager().registerEvents(this, Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("CustomNameplates"))); + } + + @Override + public void unregister() { + HandlerList.unregisterAll(this); } } diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/DisguiseUtils.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/disguise/DisguiseUtils.java similarity index 95% rename from paper/src/main/java/net/momirealms/customnameplates/paper/util/DisguiseUtils.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/disguise/DisguiseUtils.java index 04f63a2..84cd724 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/DisguiseUtils.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/disguise/DisguiseUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.util; +package net.momirealms.customnameplates.bukkit.compatibility.disguise; import me.libraryaddict.disguise.DisguiseAPI; import me.libraryaddict.disguise.disguisetypes.Disguise; diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/CMIProvider.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/emoji/ItemsAdderEmojiProvider.java similarity index 51% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/CMIProvider.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/emoji/ItemsAdderEmojiProvider.java index 0fecb48..da2dd7a 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/CMIProvider.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/emoji/ItemsAdderEmojiProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,18 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.team.provider; +package net.momirealms.customnameplates.bukkit.compatibility.emoji; -import com.Zrips.CMI.CMI; +import dev.lone.itemsadder.api.FontImages.FontImageWrapper; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.feature.bubble.emoji.EmojiProvider; import org.bukkit.entity.Player; -import org.bukkit.scoreboard.Team; -public class CMIProvider implements TeamProvider { +public class ItemsAdderEmojiProvider implements EmojiProvider { @Override - public String getTeam(Player player, Player ignore) { - Team team = CMI.getInstance().getSB().getPlayerTeam(player); - if (team == null) return null; - return team.getName(); + public String replace(CNPlayer player, String text) { + try { + return FontImageWrapper.replaceFontImages((Player) player.player(), text).replace("§f","").replace("§r",""); + } catch (NoSuchMethodError ignore) { + return text; + } } } diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/OraxenImageImpl.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/emoji/OraxenEmojiProvider.java similarity index 65% rename from paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/OraxenImageImpl.java rename to compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/emoji/OraxenEmojiProvider.java index 2b21c2a..c1fe438 100644 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/image/OraxenImageImpl.java +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/emoji/OraxenEmojiProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) <2022> + * Copyright (C) <2024> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,28 +15,30 @@ * along with this program. If not, see . */ -package net.momirealms.customnameplates.paper.mechanic.bubble.image; +package net.momirealms.customnameplates.bukkit.compatibility.emoji; import io.th0rgal.oraxen.OraxenPlugin; import io.th0rgal.oraxen.font.FontManager; import io.th0rgal.oraxen.font.Glyph; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.feature.bubble.emoji.EmojiProvider; import org.bukkit.entity.Player; import java.util.Map; -public class OraxenImageImpl implements ImageParser{ +public class OraxenEmojiProvider implements EmojiProvider { private final FontManager fontManager; - public OraxenImageImpl() { + public OraxenEmojiProvider() { this.fontManager = OraxenPlugin.get().getFontManager(); } @Override - public String parse(Player player, String text) { + public String replace(CNPlayer player, String text) { for (Map.Entry entry : this.fontManager.getGlyphByPlaceholderMap().entrySet()) { - if (entry.getValue().hasPermission(player)) { - text = text.replace(entry.getKey(), "" + entry.getValue().getCharacter() + ""); + if (entry.getValue().hasPermission((Player) player.player())) { + text = text.replace(entry.getKey(), "" + entry.getValue().getCharacter() + ""); } } return text; diff --git a/gradle.properties b/gradle.properties index e69de29..c65022b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -0,0 +1,52 @@ +# Project settings +# Rule: [major update].[feature update].[bug fix] +project_version=3.0.0 +config_version=36 +project_group=net.momirealms + +# Dependency settings +paper_version=1.20.4 +jetbrains_annotations_version=24.0.0 +slf4j_version=2.0.13 +log4j_version=2.23.1 +gson_version=2.10.1 +asm_version=9.7 +asm_commons_version=9.7 +jar_relocator_version=1.7 +h2_driver_version=2.2.224 +sqlite_driver_version=3.46.1.0 +adventure_bundle_version=4.17.0 +adventure_platform_version=4.3.4 +cloud_core_version=2.0.0 +cloud_services_version=2.0.0 +cloud_brigadier_version=2.0.0-beta.10 +cloud_bukkit_version=2.0.0-beta.10 +cloud_paper_version=2.0.0-beta.10 +cloud_minecraft_extras_version=2.0.0-beta.10 +boosted_yaml_version=1.3.7 +byte_buddy_version=1.14.18 +mojang_brigadier_version=1.0.18 +mongodb_driver_version=5.1.4 +mariadb_driver_version=3.4.1 +mysql_driver_version=9.0.0 +hikari_version=5.1.0 +commons_pool_version=2.12.0 +bstats_version=3.0.2 +geantyref_version=1.3.15 +caffeine_version=3.1.8 +jedis_version=5.1.5 +exp4j_version=0.4.8 +placeholder_api_version=2.11.6 +vault_version=1.7 +guava_version=33.2.0-jre +fontbox_version=3.0.3 +pdfbox_io_version=3.0.3 +commons_io_version=2.17.0 + +# Proxy settings +systemProp.socks.proxyHost=127.0.0.1 +systemProp.socks.proxyPort=7890 +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f4f4ea9..3f4b8b3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/paper/.gitignore b/paper/.gitignore deleted file mode 100644 index b63da45..0000000 --- a/paper/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts deleted file mode 100644 index 76d3d02..0000000 --- a/paper/build.gradle.kts +++ /dev/null @@ -1,74 +0,0 @@ -dependencies { - // server - compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") - - // command - compileOnly("dev.jorel:commandapi-bukkit-core:9.5.3") - - // packet - compileOnly("com.comphenix.protocol:ProtocolLib:5.2.0-SNAPSHOT") - - // papi - compileOnly("me.clip:placeholderapi:2.11.6") - - // config - compileOnly("dev.dejvokep:boosted-yaml:1.3.6") - - // bStats - compileOnly("org.bstats:bstats-bukkit:3.0.2") - - // team - compileOnly("me.neznamy:tab-api:4.0.2") - compileOnly(files("libs/CMI-API-9.6.5.0.jar")) - - // Gson - compileOnly("com.google.code.gson:gson:2.10.1") - - // database - compileOnly("org.xerial:sqlite-jdbc:3.45.3.0") - compileOnly("com.h2database:h2:2.2.224") - compileOnly("org.mongodb:mongodb-driver-sync:5.0.1") - compileOnly("com.zaxxer:HikariCP:5.0.1") - compileOnly("redis.clients:jedis:5.1.3") - - // others - compileOnly("com.github.LoneDev6:api-itemsadder:3.5.0c-r5") - compileOnly("io.th0rgal:oraxen:1.165.0") - compileOnly("com.github.FrancoBM12:API-MagicCosmetics:2.2.5") - compileOnly("commons-io:commons-io:2.15.1") - compileOnly("org.geysermc.geyser:api:2.2.0-SNAPSHOT") - compileOnly("LibsDisguises:LibsDisguises:10.0.42") - - // chat channels - compileOnly(files("libs/VentureChat-3.7.1.jar")) - compileOnly(files("libs/TrChat-2.0.11.jar")) - compileOnly(files("libs/carbonchat-paper-3.0.0-beta.26.jar")) - compileOnly(files("libs/AdvancedChat-1.3.7.jar")) - compileOnly("net.william278:huskchat:2.7.1") - - // api module - implementation(project(":api")) - implementation(project(":common")) - implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.35") - - // adventure - implementation("net.kyori:adventure-api:4.17.0") - implementation("net.kyori:adventure-platform-bukkit:4.3.3") -} - -tasks { - shadowJar { - relocate ("net.kyori", "net.momirealms.customnameplates.libraries") - relocate ("org.bstats", "net.momirealms.customnameplates.libraries.bstats") - relocate ("net.momirealms.sparrow.heart", "net.momirealms.customnameplates.libraries.sparrow") - relocate ("org.apache.commons.pool2", "net.momirealms.customnameplates.libraries.commonspool2") - relocate ("com.mysql", "net.momirealms.customnameplates.libraries.mysql") - relocate ("org.mariadb", "net.momirealms.customnameplates.libraries.mariadb") - relocate ("com.zaxxer.hikari", "net.momirealms.customnameplates.libraries.hikari") - relocate ("redis.clients.jedis", "net.momirealms.customnameplates.libraries.jedis") - relocate ("com.mongodb", "net.momirealms.customnameplates.libraries.mongodb") - relocate ("org.bson", "net.momirealms.customnameplates.libraries.bson") - relocate ("dev.jorel.commandapi", "net.momirealms.customnameplates.libraries.commandapi") - relocate ("dev.dejvokep.boostedyaml", "net.momirealms.customnameplates.libraries.boostedyaml") - } -} diff --git a/paper/libs/BiomeAPI.jar b/paper/libs/BiomeAPI.jar deleted file mode 100644 index 59096aa..0000000 Binary files a/paper/libs/BiomeAPI.jar and /dev/null differ diff --git a/paper/libs/CMI-API-9.6.5.0.jar b/paper/libs/CMI-API-9.6.5.0.jar deleted file mode 100644 index 9653148..0000000 Binary files a/paper/libs/CMI-API-9.6.5.0.jar and /dev/null differ diff --git a/paper/libs/carbonchat-paper-3.0.0-beta.26.jar b/paper/libs/carbonchat-paper-3.0.0-beta.26.jar deleted file mode 100644 index fb7e3de..0000000 Binary files a/paper/libs/carbonchat-paper-3.0.0-beta.26.jar and /dev/null differ diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/CustomNameplatesPluginImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/CustomNameplatesPluginImpl.java deleted file mode 100644 index 1356e6d..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/CustomNameplatesPluginImpl.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.event.CustomNameplatesReloadEvent; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.customnameplates.paper.command.CommandManager; -import net.momirealms.customnameplates.paper.libraries.classpath.ReflectionClassPathAppender; -import net.momirealms.customnameplates.paper.libraries.dependencies.Dependency; -import net.momirealms.customnameplates.paper.libraries.dependencies.DependencyManager; -import net.momirealms.customnameplates.paper.libraries.dependencies.DependencyManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.actionbar.ActionBarManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.background.BackGroundManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.bossbar.BossBarManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.bubble.BubbleManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.font.WidthManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.image.ImageManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.misc.CoolDownManager; -import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager; -import net.momirealms.customnameplates.paper.mechanic.misc.VersionManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.pack.ResourcePackManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.placeholder.PlaceholderManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.requirement.RequirementManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.team.TeamManagerImpl; -import net.momirealms.customnameplates.paper.scheduler.SchedulerImpl; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.setting.CNLocale; -import net.momirealms.customnameplates.paper.storage.StorageManagerImpl; -import net.momirealms.customnameplates.paper.util.Migration; -import net.momirealms.customnameplates.paper.util.ReflectionUtils; -import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.scoreboard.Team; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implements Listener { - - private CoolDownManager coolDownManager; - private PacketManager packetManager; - private DependencyManager dependencyManager; - - @Override - public void onLoad() { - this.versionManager = new VersionManagerImpl(this); - this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader())); - this.dependencyManager.loadDependencies(new ArrayList<>( - List.of( - Dependency.GSON, - Dependency.SLF4J_API, - Dependency.SLF4J_SIMPLE, - versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API, - Dependency.BOOSTED_YAML, - Dependency.MYSQL_DRIVER, - Dependency.MARIADB_DRIVER, - Dependency.MONGODB_DRIVER_SYNC, - Dependency.MONGODB_DRIVER_CORE, - Dependency.MONGODB_DRIVER_BSON, - Dependency.JEDIS, - Dependency.COMMONS_POOL_2, - Dependency.H2_DRIVER, - Dependency.SQLITE_DRIVER, - Dependency.BSTATS_BASE, - Dependency.HIKARI, - Dependency.BSTATS_BUKKIT - ) - )); - ReflectionUtils.load(); - } - - @Override - public void onEnable() { - if (Migration.check()) { - LogUtils.warn("Please read /CustomNameplates/README.txt to finish the migration."); - Bukkit.getPluginManager().disablePlugin(this); - return; - } - this.adventureManager = new AdventureManagerImpl(this); - this.scheduler = new SchedulerImpl(this); - this.storageManager = new StorageManagerImpl(this); - this.requirementManager = new RequirementManagerImpl(this); - this.bossBarManager = new BossBarManagerImpl(this); - this.imageManager = new ImageManagerImpl(this); - this.placeholderManager = new PlaceholderManagerImpl(this); - this.backGroundManager = new BackGroundManagerImpl(this); - this.resourcePackManager = new ResourcePackManagerImpl(this); - this.nameplateManager = new NameplateManagerImpl(this); - this.teamManager = new TeamManagerImpl(this); - this.widthManager = new WidthManagerImpl(this); - this.bubbleManager = new BubbleManagerImpl(this); - this.actionBarManager = new ActionBarManagerImpl(this); - this.coolDownManager = new CoolDownManager(this); - this.packetManager = new PacketManager(this); - this.reload(); - new CommandManager(this).load(); - this.versionManager.checkUpdate().thenAccept(outDated -> { - if (!outDated) this.getAdventure().sendConsoleMessage("[CustomNameplates] You are using the latest version."); - else this.getAdventure().sendConsoleMessage("[CustomNameplates] Update is available: https://polymart.org/resource/2543"); - }); - this.getServer().getPluginManager().registerEvents((VersionManagerImpl) versionManager, this); - if (CNConfig.generatePackOnStart) - this.resourcePackManager.generateResourcePack(); - if (CNConfig.metrics) - new Metrics(this, 16649); - } - - @Override - public void onDisable() { - if (scheduler != null) ((SchedulerImpl) this.scheduler).shutdown(); - if (actionBarManager != null) ((ActionBarManagerImpl) actionBarManager).unload(); - if (nameplateManager != null) ((NameplateManagerImpl) this.nameplateManager).disable(); - if (teamManager != null) ((TeamManagerImpl) this.teamManager).disable(); - if (bossBarManager != null) ((BossBarManagerImpl) this.bossBarManager).unload(); - if (imageManager != null) ((ImageManagerImpl) this.imageManager).unload(); - if (backGroundManager != null) ((BackGroundManagerImpl) this.backGroundManager).unload(); - if (placeholderManager != null) ((PlaceholderManagerImpl) this.placeholderManager).unload(); - if (bubbleManager != null) ((BubbleManagerImpl) this.bubbleManager).unload(); - if (requirementManager != null) ((RequirementManagerImpl) this.requirementManager).unload(); - if (resourcePackManager != null) ((ResourcePackManagerImpl) this.resourcePackManager).unload(); - if (widthManager != null) ((WidthManagerImpl) this.widthManager).unload(); - if (storageManager != null) ((StorageManagerImpl) this.storageManager).disable(); - if (adventureManager != null) ((AdventureManagerImpl) this.adventureManager).close(); - if (versionManager != null) HandlerList.unregisterAll((VersionManagerImpl) versionManager); - for (Team team : Bukkit.getScoreboardManager().getMainScoreboard().getTeams()) { - if (team.getName().startsWith("CNP_")) { - team.unregister(); - } - } - } - - @Override - public void reload() { - CNConfig.load(); - CNLocale.load(); - ((SchedulerImpl) this.scheduler).reload(); - ((AdventureManagerImpl) this.adventureManager).reload(); - ((NameplateManagerImpl) this.nameplateManager).reload(); - ((BubbleManagerImpl) this.bubbleManager).reload(); - ((BackGroundManagerImpl) this.backGroundManager).reload(); - ((TeamManagerImpl) this.teamManager).reload(); - ((StorageManagerImpl) this.storageManager).reload(); - ((RequirementManagerImpl) this.requirementManager).reload(); - ((BossBarManagerImpl) this.bossBarManager).reload(); - ((ActionBarManagerImpl) actionBarManager).reload(); - ((ImageManagerImpl) this.imageManager).reload(); - ((PlaceholderManagerImpl) this.placeholderManager).reload(); - ((WidthManagerImpl) this.widthManager).reload(); - ((ResourcePackManagerImpl) this.resourcePackManager).reload(); - CustomNameplatesReloadEvent event = new CustomNameplatesReloadEvent(this); - this.getServer().getPluginManager().callEvent(event); - } - - @Override - public YamlConfiguration getConfig(String file) { - File config = new File(this.getDataFolder(), file); - if (!config.exists()) this.saveResource(file, false); - return YamlConfiguration.loadConfiguration(config); - } - - @Override - public void debug(String s) { - if (CNConfig.debug) { - LogUtils.info(s); - } - } - - public CoolDownManager getCoolDownManager() { - return coolDownManager; - } - - public PacketManager getPacketManager() { - return packetManager; - } - - public DependencyManager getDependencyManager() { - return dependencyManager; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/adventure/AdventureManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/adventure/AdventureManagerImpl.java deleted file mode 100644 index 0b58a42..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/adventure/AdventureManagerImpl.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.adventure; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import net.kyori.adventure.title.Title; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.AdventureManager; -import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.setting.CNLocale; -import net.momirealms.customnameplates.paper.util.ReflectionUtils; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.lang.reflect.InvocationTargetException; -import java.time.Duration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -public class AdventureManagerImpl implements AdventureManager { - - private final BukkitAudiences adventure; - private static AdventureManagerImpl instance; - private CacheSystem cacheSystem; - - public AdventureManagerImpl(CustomNameplatesPlugin plugin) { - this.adventure = BukkitAudiences.create(plugin); - instance = this; - } - - public static AdventureManagerImpl getInstance() { - return instance; - } - - public void close() { - if (adventure != null) - adventure.close(); - } - - public void reload() { - if (this.cacheSystem != null) this.cacheSystem.destroy(); - this.cacheSystem = new CacheSystem(CNConfig.cacheSize); - } - - public String getJsonComponentFromMiniMessage(String text) { - return cacheSystem.getJsonComponentFromCache(text); - } - - public Object getIChatComponentFromMiniMessage(String text) { - return cacheSystem.getIChatFromCache(text); - } - - @Override - public String stripTags(String text) { - return MiniMessage.miniMessage().stripTags(text); - } - - @Override - public Component getComponentFromMiniMessage(String text) { - if (text == null) { - return Component.empty(); - } - return cacheSystem.getComponentFromCache(text); - } - - @Override - public void sendMessage(CommandSender sender, String s) { - if (s == null) return; - if (sender instanceof Player player) sendPlayerMessage(player, s); - else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(s); - } - - @Override - public void sendMessageWithPrefix(CommandSender sender, String s) { - if (s == null || s.equals("")) return; - if (sender instanceof Player player) sendPlayerMessage(player, CNLocale.MSG_PREFIX + s); - else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(CNLocale.MSG_PREFIX + s); - } - - @Override - public void sendConsoleMessage(String s) { - if (s == null) return; - Audience au = adventure.sender(Bukkit.getConsoleSender()); - au.sendMessage(getComponentFromMiniMessage(s)); - } - - @Override - public void sendPlayerMessage(Player player, String s) { - if (s == null) return; - Audience au = adventure.player(player); - au.sendMessage(getComponentFromMiniMessage(s)); - } - - @Override - public void sendTitle(Player player, String title, String subtitle, int in, int duration, int out) { - Audience au = adventure.player(player); - Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out)); - au.showTitle(Title.title(getComponentFromMiniMessage(title), getComponentFromMiniMessage(subtitle), times)); - } - - @Override - public void sendTitle(Player player, Component title, Component subtitle, int in, int duration, int out) { - Audience au = adventure.player(player); - Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out)); - au.showTitle(Title.title(title, subtitle, times)); - } - - @Override - public void sendActionbar(Player player, String text) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.SET_ACTION_BAR_TEXT); - packet.getModifier().write(0, getIChatComponent(componentToJson(getComponentFromMiniMessage(text).append(Component.score().name("np").objective("ab").build())))); - PacketManager.getInstance().send(player, packet); - } - - @Override - public void sendActionbar(Player player, Component component) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.SET_ACTION_BAR_TEXT); - packet.getModifier().write(0, getIChatComponent(componentToJson(component))); - PacketManager.getInstance().send(player, packet); - } - - @Override - public void sendSound(Player player, Sound.Source source, Key key, float volume, float pitch) { - Sound sound = Sound.sound(key, source, volume, pitch); - Audience au = adventure.player(player); - au.playSound(sound); - } - - @Override - public void sendSound(Player player, Sound sound) { - Audience au = adventure.player(player); - au.playSound(sound); - } - - @Override - public String legacyToMiniMessage(String legacy) { - StringBuilder stringBuilder = new StringBuilder(); - char[] chars = legacy.toCharArray(); - for (int i = 0; i < chars.length; i++) { - if (!isColorCode(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(""); - case '1' -> stringBuilder.append(""); - case '2' -> stringBuilder.append(""); - case '3' -> stringBuilder.append(""); - case '4' -> stringBuilder.append(""); - case '5' -> stringBuilder.append(""); - case '6' -> stringBuilder.append(""); - case '7' -> stringBuilder.append(""); - case '8' -> stringBuilder.append(""); - case '9' -> stringBuilder.append(""); - case 'a' -> stringBuilder.append(""); - case 'b' -> stringBuilder.append(""); - case 'c' -> stringBuilder.append(""); - case 'd' -> stringBuilder.append(""); - case 'e' -> stringBuilder.append(""); - case 'f' -> stringBuilder.append(""); - case 'r' -> stringBuilder.append(""); - case 'l' -> stringBuilder.append(""); - case 'm' -> stringBuilder.append(""); - case 'o' -> stringBuilder.append(""); - case 'n' -> stringBuilder.append(""); - case 'k' -> stringBuilder.append(""); - case 'x' -> { - if (i + 13 >= chars.length - || !isColorCode(chars[i+2]) - || !isColorCode(chars[i+4]) - || !isColorCode(chars[i+6]) - || !isColorCode(chars[i+8]) - || !isColorCode(chars[i+10]) - || !isColorCode(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(); - } - - @Override - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean isColorCode(char c) { - return c == '§' || c == '&'; - } - - @Override - public int colorToDecimal(ChatColor color){ - switch (String.valueOf(color.getChar())){ - case "0" -> {return 0;} - case "c" -> {return 16733525;} - case "6" -> {return 16755200;} - case "4" -> {return 11141120;} - case "e" -> {return 16777045;} - case "2" -> {return 43520;} - case "a" -> {return 5635925;} - case "b" -> {return 5636095;} - case "3" -> {return 43690;} - case "1" -> {return 170;} - case "9" -> {return 5592575;} - case "d" -> {return 16733695;} - case "5" -> {return 11141290;} - case "8" -> {return 5592405;} - case "7" -> {return 11184810;} - default -> {return 16777215;} - } - } - - @Override - public String componentToLegacy(Component component) { - return LegacyComponentSerializer.legacySection().serialize(component); - } - - @Override - public String componentToJson(Component component) { - return GsonComponentSerializer.gson().serialize(component); - } - - @Override - public String getMiniMessageFormat(Component component) { - return MiniMessage.miniMessage().serialize(component); - } - - @Override - public Object getIChatComponent(String json) { - try { - return ReflectionUtils.getiChatComponentMethod().invoke(null, json); - } catch (InvocationTargetException | IllegalAccessException exception) { - exception.printStackTrace(); - return ReflectionUtils.getEmptyComponent(); - } - } - - public Audience audience(Player player) { - return adventure.player(player); - } - - public class CacheSystem { - - private final LoadingCache miniMessageToIChatComponentCache; - private final LoadingCache miniMessageToComponentCache; - private final LoadingCache miniMessageToJsonChatComponentCache; - - public CacheSystem(int size) { - miniMessageToIChatComponentCache = CacheBuilder.newBuilder() - .maximumSize(size) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build( - new CacheLoader<>() { - @NotNull - @Override - public Object load(@NotNull String text) { - return fetchIChatData(text); - } - }); - miniMessageToComponentCache = CacheBuilder.newBuilder() - .maximumSize(size) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build( - new CacheLoader<>() { - @NotNull - @Override - public Component load(@NotNull String text) { - return fetchComponent(text); - } - }); - miniMessageToJsonChatComponentCache = CacheBuilder.newBuilder() - .maximumSize(size) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build( - new CacheLoader<>() { - @NotNull - @Override - public String load(@NotNull String text) { - return fetchJsonChatComponent(text); - } - }); - } - - public void destroy() { - miniMessageToComponentCache.cleanUp(); - miniMessageToIChatComponentCache.cleanUp(); - } - - @NotNull - private Object fetchIChatData(String text) { - Component component = getComponentFromMiniMessage(text); - return getIChatComponent(GsonComponentSerializer.gson().serialize(component)); - } - - @NotNull - private Component fetchComponent(String text) { - if (CNConfig.legacyColorSupport) { - return MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text)); - } else { - return MiniMessage.miniMessage().deserialize(text); - } - } - - @NotNull - private String fetchJsonChatComponent(String text) { - if (CNConfig.legacyColorSupport) { - return GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text))); - } else { - return GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(text)); - } - } - - public Object getIChatFromCache(String text) { - try { - return miniMessageToIChatComponentCache.get(text); - } catch (ExecutionException e) { - e.printStackTrace(); - return ReflectionUtils.getEmptyComponent(); - } - } - - public Component getComponentFromCache(String text) { - try { - return miniMessageToComponentCache.get(text); - } catch (ExecutionException e) { - e.printStackTrace(); - return Component.empty(); - } - } - - public String getJsonComponentFromCache(String text) { - try { - return miniMessageToJsonChatComponentCache.get(text); - } catch (ExecutionException e) { - e.printStackTrace(); - return "{\"text\":\"\"}"; - } - } - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/command/CommandManager.java b/paper/src/main/java/net/momirealms/customnameplates/paper/command/CommandManager.java deleted file mode 100644 index 6bd2dcd..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/command/CommandManager.java +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.command; - -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import dev.jorel.commandapi.CommandAPI; -import dev.jorel.commandapi.CommandAPIBukkitConfig; -import dev.jorel.commandapi.CommandAPICommand; -import dev.jorel.commandapi.arguments.ArgumentSuggestions; -import dev.jorel.commandapi.arguments.BooleanArgument; -import dev.jorel.commandapi.arguments.PlayerArgument; -import dev.jorel.commandapi.arguments.StringArgument; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.DataStorageInterface; -import net.momirealms.customnameplates.api.data.LegacyDataStorageInterface; -import net.momirealms.customnameplates.api.data.OnlineUser; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.mechanic.bubble.Bubble; -import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate; -import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer; -import net.momirealms.customnameplates.api.util.CompletableFutures; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.setting.CNLocale; -import net.momirealms.customnameplates.paper.storage.method.database.sql.MariaDBImpl; -import net.momirealms.customnameplates.paper.storage.method.database.sql.MySQLImpl; -import net.momirealms.customnameplates.paper.storage.method.file.YAMLImpl; -import org.bukkit.entity.Player; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -@SuppressWarnings("DuplicatedCode") -public class CommandManager { - - private final CustomNameplatesPluginImpl plugin; - - public CommandManager(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - if (!CommandAPI.isLoaded()) - CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true)); - } - - public void load() { - var command1 = new CommandAPICommand("customnameplates") - .withAliases("nameplates", "cnameplates") - .withSubcommands( - NameplatesCommands.getReloadCommand(), - NameplatesCommands.getAboutCommand(), - NameplatesCommands.getDataCommand() - ); - if (CNConfig.nameplateModule) { - command1.withSubcommands( - NameplatesCommands.getEquipCommand(), - NameplatesCommands.getUnEquipCommand(), - NameplatesCommands.getPreviewCommand(), - NameplatesCommands.getListCommand(), - NameplatesCommands.getForceEquipCommand(), - NameplatesCommands.getForceUnEquipCommand(), - NameplatesCommands.getForcePreviewCommand() - ); - } - command1.register(); - if (CNConfig.bubbleModule) - new CommandAPICommand("bubbles") - .withSubcommands( - BubblesCommands.getListCommand(), - BubblesCommands.getEquipCommand(), - BubblesCommands.getUnEquipCommand(), - BubblesCommands.getForceEquipCommand(), - BubblesCommands.getForceUnEquipCommand() - ) - .register(); - } - - public static class BubblesCommands { - - public static CommandAPICommand getListCommand() { - return new CommandAPICommand("list") - .withPermission("bubbles.command.list") - .executesPlayer((player, args) -> { - if (!CNConfig.bubbleModule) return; - List bubbles = CustomNameplatesPlugin.get().getBubbleManager().getAvailableBubblesDisplayNames(player); - if (bubbles.size() != 0) { - StringJoiner stringJoiner = new StringJoiner(", "); - for (String availableBubble : bubbles) { - stringJoiner.add(availableBubble); - } - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_AVAILABLE_BUBBLE.replace("{Bubble}", stringJoiner.toString()).replace("{Bubbles}", stringJoiner.toString())); - } else { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_HAVE_NO_BUBBLE); - } - }); - } - - public static CommandAPICommand getEquipCommand() { - return new CommandAPICommand("equip") - .withPermission("bubbles.command.equip") - .withArguments(new StringArgument("bubble").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getBubbleManager().getAvailableBubbles((Player) commandSenderSuggestionInfo.sender()).toArray(new String[0])))) - .executesPlayer((player, args) -> { - if (!CNConfig.bubbleModule) return; - String bubble = (String) args.get("bubble"); - if (!CustomNameplatesPlugin.get().getBubbleManager().containsBubble(bubble)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_BUBBLE_NOT_EXIST); - return; - } - - if (!CustomNameplatesPlugin.get().getBubbleManager().hasBubble(player, bubble)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_BUBBLE_NOT_AVAILABLE); - return; - } - - CustomNameplatesPlugin.get().getBubbleManager().equipBubble(player, bubble); - Bubble bubbleInstance = CustomNameplatesPlugin.get().getBubbleManager().getBubble(bubble); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_EQUIP_BUBBLE.replace("{Bubble}", bubbleInstance.getDisplayName())); - }); - } - - public static CommandAPICommand getUnEquipCommand() { - return new CommandAPICommand("unequip") - .withPermission("bubbles.command.unequip") - .executesPlayer((player, args) -> { - if (!CNConfig.bubbleModule) return; - CustomNameplatesPlugin.get().getBubbleManager().unEquipBubble(player); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_UNEQUIP_BUBBLE); - }); - } - - public static CommandAPICommand getForceEquipCommand() { - return new CommandAPICommand("force-equip") - .withPermission("customnameplates.admin") - .withArguments(new PlayerArgument("player")) - .withArguments(new StringArgument("bubble").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getBubbleManager().getBubbleKeys().toArray(new String[0])))) - .executes((sender, args) -> { - if (!CNConfig.bubbleModule) return; - Player player = (Player) args.get("player"); - String bubble = (String) args.get("bubble"); - if (player == null) return; - if (!CustomNameplatesPlugin.get().getBubbleManager().equipBubble(player, bubble)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_BUBBLE_NOT_EXIST); - return; - } - Bubble bubbleInstance = CustomNameplatesPlugin.get().getBubbleManager().getBubble(bubble); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_FORCE_EQUIP_BUBBLE.replace("{Bubble}", bubbleInstance.getDisplayName()).replace("{Player}", player.getName())); - }); - } - - public static CommandAPICommand getForceUnEquipCommand() { - return new CommandAPICommand("force-unequip") - .withPermission("customnameplates.admin") - .withArguments(new PlayerArgument("player")) - .executes((sender, args) -> { - if (!CNConfig.bubbleModule) return; - Player player = (Player) args.get("player"); - if (player == null) return; - CustomNameplatesPlugin.get().getBubbleManager().unEquipBubble(player); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_FORCE_UNEQUIP_BUBBLE.replace("{Player}", player.getName())); - }); - } - - } - - public static class NameplatesCommands { - public static CommandAPICommand getForceEquipCommand() { - return new CommandAPICommand("force-equip") - .withPermission("customnameplates.admin") - .withArguments(new PlayerArgument("player")) - .withArguments(new StringArgument("nameplate").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getNameplateManager().getNameplateKeys().toArray(new String[0])))) - .executes((sender, args) -> { - if (!CNConfig.nameplateModule) return; - Player player = (Player) args.get("player"); - String nameplate = (String) args.get("nameplate"); - if (player == null) return; - if (!CustomNameplatesPlugin.get().getNameplateManager().equipNameplate(player, nameplate, false)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_NAMEPLATE_NOT_EXISTS); - return; - } - Nameplate nameplateInstance = CustomNameplatesPlugin.get().getNameplateManager().getNameplate(nameplate); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_FORCE_EQUIP_NAMEPLATE.replace("{Nameplate}", nameplateInstance.getDisplayName()).replace("{Player}", player.getName())); - }); - } - - public static CommandAPICommand getForceUnEquipCommand() { - return new CommandAPICommand("force-unequip") - .withPermission("customnameplates.admin") - .withArguments(new PlayerArgument("player")) - .executes((sender, args) -> { - if (!CNConfig.nameplateModule) return; - Player player = (Player) args.get("player"); - if (player == null) return; - CustomNameplatesPlugin.get().getNameplateManager().unEquipNameplate(player, false); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_FORCE_UNEQUIP_NAMEPLATE.replace("{Player}", player.getName())); - }); - } - - public static CommandAPICommand getPreviewCommand() { - return new CommandAPICommand("preview") - .withPermission("nameplates.command.preview") - .executesPlayer((player, args) -> { - if (!CNConfig.nameplateModule) return; - NameplatePlayer nameplatePlayer = CustomNameplatesPlugin.get().getNameplateManager().getNameplatePlayer(player.getUniqueId()); - if (nameplatePlayer == null) { - LogUtils.warn(player.getName() + " failed to preview because no tag is created"); - return; - } - if (nameplatePlayer.isPreviewing()) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_PREVIEW_COOLDOWN); - return; - } - nameplatePlayer.setPreview(true); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_PREVIEW_START); - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> { - nameplatePlayer.setPreview(false); - }, CustomNameplatesPlugin.get().getNameplateManager().getPreviewDuration(), TimeUnit.SECONDS); - }); - } - - public static CommandAPICommand getForcePreviewCommand() { - return new CommandAPICommand("force-preview") - .withPermission("customnameplates.admin") - .withArguments(new PlayerArgument("player")) - .withOptionalArguments(new StringArgument("nameplate").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getNameplateManager().getNameplateKeys().toArray(new String[0])))) - .executes((sender, args) -> { - if (!CNConfig.nameplateModule) return; - Player player = (Player) args.get("player"); - String nameplate = (String) args.getOrDefault("nameplate", ""); - if (player == null) return; - NameplatePlayer nameplatePlayer = CustomNameplatesPlugin.get().getNameplateManager().getNameplatePlayer(player.getUniqueId()); - if (nameplatePlayer == null) { - LogUtils.warn(player.getName() + " failed to preview because no tag is created"); - return; - } - if (nameplatePlayer.isPreviewing()) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_PREVIEW_COOLDOWN); - return; - } - Optional user = CustomNameplatesPlugin.get().getStorageManager().getOnlineUser(player.getUniqueId()); - if (user.isEmpty()) { - LogUtils.warn(player.getName() + " failed to preview because data not loaded"); - return; - } - if (!nameplate.equals("")) { - String previous = user.get().getNameplateKey(); - if (!CustomNameplatesPlugin.get().getNameplateManager().equipNameplate(player, nameplate, true)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_NAMEPLATE_NOT_EXISTS); - return; - } - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_FORCE_PREVIEW.replace("{Player}", player.getName())); - nameplatePlayer.setPreview(true); - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> { - nameplatePlayer.setPreview(false); - if (previous.equals("none")) { - CustomNameplatesPlugin.get().getNameplateManager().unEquipNameplate(player, true); - } else { - CustomNameplatesPlugin.get().getNameplateManager().equipNameplate(player, previous, true); - } - }, CustomNameplatesPlugin.get().getNameplateManager().getPreviewDuration(), TimeUnit.SECONDS); - } else { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_FORCE_PREVIEW.replace("{Player}", player.getName())); - nameplatePlayer.setPreview(true); - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> { - nameplatePlayer.setPreview(false); - }, CustomNameplatesPlugin.get().getNameplateManager().getPreviewDuration(), TimeUnit.SECONDS); - } - }); - } - - public static CommandAPICommand getEquipCommand() { - return new CommandAPICommand("equip") - .withPermission("nameplates.command.equip") - .withArguments(new StringArgument("nameplate").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getNameplateManager().getAvailableNameplates((Player) commandSenderSuggestionInfo.sender()).toArray(new String[0])))) - .executesPlayer((player, args) -> { - if (!CNConfig.nameplateModule) return; - String nameplate = (String) args.get("nameplate"); - - if (!CustomNameplatesPlugin.get().getNameplateManager().containsNameplate(nameplate)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_NAMEPLATE_NOT_EXISTS); - return; - } - - if (!CustomNameplatesPlugin.get().getNameplateManager().hasNameplate(player, nameplate)) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_NAMEPLATE_NOT_AVAILABLE); - return; - } - - CustomNameplatesPlugin.get().getNameplateManager().equipNameplate(player, nameplate, false); - Nameplate nameplateInstance = CustomNameplatesPlugin.get().getNameplateManager().getNameplate(nameplate); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_EQUIP_NAMEPLATE.replace("{Nameplate}", nameplateInstance.getDisplayName())); - }); - } - - public static CommandAPICommand getUnEquipCommand() { - return new CommandAPICommand("unequip") - .withPermission("nameplates.command.unequip") - .executesPlayer((player, args) -> { - if (!CNConfig.nameplateModule) return; - CustomNameplatesPlugin.get().getNameplateManager().unEquipNameplate(player, false); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_UNEQUIP_NAMEPLATE); - }); - } - - public static CommandAPICommand getListCommand() { - return new CommandAPICommand("list") - .withPermission("nameplates.command.list") - .executesPlayer((player, args) -> { - if (!CNConfig.nameplateModule) return; - List nameplates = CustomNameplatesPlugin.get().getNameplateManager().getAvailableNameplateDisplayNames(player); - if (nameplates.size() != 0) { - StringJoiner stringJoiner = new StringJoiner(", "); - for (String availableNameplate : nameplates) { - stringJoiner.add(availableNameplate); - } - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_AVAILABLE_NAMEPLATE.replace("{Nameplates}", stringJoiner.toString()).replace("{Nameplate}", stringJoiner.toString())); - } else { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_HAVE_NO_NAMEPLATE); - } - }); - } - - public static CommandAPICommand getReloadCommand() { - return new CommandAPICommand("reload") - .withPermission("customnameplates.admin") - .executes((sender, args) -> { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Usage: /nameplates reload all/pack/config"); - }) - .withSubcommands( - new CommandAPICommand("all") - .withOptionalArguments(new BooleanArgument("async-pack-generation")) - .executes((sender, args) -> { - boolean async = (boolean) args.getOrDefault("async-pack-generation", false); - long time = System.currentTimeMillis(); - CustomNameplatesPlugin.get().reload(); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_RELOAD.replace("{time}", String.valueOf(System.currentTimeMillis()-time))); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_GENERATING); - if (async) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> { - CustomNameplatesPlugin.get().getResourcePackManager().generateResourcePack(); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_PACK_GENERATED); - }); - } else { - CustomNameplatesPlugin.get().getResourcePackManager().generateResourcePack(); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_PACK_GENERATED); - } - }), - new CommandAPICommand("pack") - .withOptionalArguments(new BooleanArgument("async-pack-generation")) - .executes((sender, args) -> { - boolean async = (boolean) args.getOrDefault("async-pack-generation", false); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_GENERATING); - if (async) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> { - CustomNameplatesPlugin.get().getResourcePackManager().generateResourcePack(); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_PACK_GENERATED); - }); - } else { - CustomNameplatesPlugin.get().getResourcePackManager().generateResourcePack(); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_PACK_GENERATED); - } - }), - new CommandAPICommand("config") - .executes((sender, args) -> { - long time = System.currentTimeMillis(); - CustomNameplatesPlugin.get().reload(); - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, CNLocale.MSG_RELOAD.replace("{time}", String.valueOf(System.currentTimeMillis()-time))); - }) - ); - } - - public static CommandAPICommand getAboutCommand() { - return new CommandAPICommand("about") - .withPermission("customnameplates.admin") - .executes((sender, args) -> { - AdventureManagerImpl.getInstance().sendMessage(sender, "<#3CB371>⚓ CustomNameplates - <#98FB98>" + CustomNameplatesPlugin.getInstance().getVersionManager().getPluginVersion()); - AdventureManagerImpl.getInstance().sendMessage(sender, "<#7FFFAA>A plugin that provides adjustable images for texts"); - AdventureManagerImpl.getInstance().sendMessage(sender, "<#DA70D6>\uD83E\uDDEA Author: <#FFC0CB>XiaoMoMi"); - AdventureManagerImpl.getInstance().sendMessage(sender, "<#FF7F50>\uD83D\uDD25 Contributors: <#FFA07A>TopOrigin"); - AdventureManagerImpl.getInstance().sendMessage(sender, "<#FFD700>⭐ Document <#A9A9A9>| <#FAFAD2>⛏ Github <#A9A9A9>| <#48D1CC>\uD83D\uDD14 Polymart"); - }); - } - - public static CommandAPICommand getDataCommand() { - return new CommandAPICommand("data") - .withPermission("customnameplates.admin") - .withSubcommands( - new CommandAPICommand("export") - .executes((sender, args) -> { - CustomNameplatesPlugin plugin = CustomNameplatesPlugin.get(); - plugin.getScheduler().runTaskAsync(() -> { - - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Starting export."); - DataStorageInterface dataStorageInterface = plugin.getStorageManager().getDataSource(); - - Set uuids = dataStorageInterface.getUniqueUsers(false); - Set> futures = new HashSet<>(); - AtomicInteger userCount = new AtomicInteger(0); - Map out = Collections.synchronizedMap(new TreeMap<>()); - - int amount = uuids.size(); - for (UUID uuid : uuids) { - futures.add(dataStorageInterface.getPlayerData(uuid).thenAccept(it -> { - if (it.isPresent()) { - out.put(uuid, plugin.getStorageManager().toJson(it.get())); - userCount.incrementAndGet(); - } - })); - } - - CompletableFuture overallFuture = CompletableFutures.allOf(futures); - - while (true) { - try { - overallFuture.get(3, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - break; - } catch (TimeoutException e) { - LogUtils.info("Progress: " + userCount.get() + "/" + amount); - continue; - } - break; - } - - JsonObject outJson = new JsonObject(); - for (Map.Entry entry : out.entrySet()) { - outJson.addProperty(entry.getKey().toString(), entry.getValue()); - } - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); - String formattedDate = formatter.format(new Date()); - File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz"); - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) { - new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer); - } catch (IOException e) { - e.printStackTrace(); - } - - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Completed."); - }); - }), - new CommandAPICommand("export-legacy") - .withArguments(new StringArgument("method") - .replaceSuggestions(ArgumentSuggestions.strings("MySQL", "MariaDB", "YAML"))) - .executes((sender, args) -> { - String arg = (String) args.get("method"); - if (arg == null) return; - CustomNameplatesPlugin plugin = CustomNameplatesPlugin.get(); - plugin.getScheduler().runTaskAsync(() -> { - - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Starting export."); - - LegacyDataStorageInterface dataStorageInterface; - switch (arg) { - case "MySQL" -> dataStorageInterface = new MySQLImpl(plugin); - case "MariaDB" -> dataStorageInterface = new MariaDBImpl(plugin); - case "YAML" -> dataStorageInterface = new YAMLImpl(plugin); - default -> { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "No such legacy storage method."); - return; - } - } - - dataStorageInterface.initialize(); - Set uuids = dataStorageInterface.getUniqueUsers(true); - Set> futures = new HashSet<>(); - AtomicInteger userCount = new AtomicInteger(0); - Map out = Collections.synchronizedMap(new TreeMap<>()); - - for (UUID uuid : uuids) { - futures.add(dataStorageInterface.getLegacyPlayerData(uuid).thenAccept(it -> { - if (it.isPresent()) { - out.put(uuid, plugin.getStorageManager().toJson(it.get())); - userCount.incrementAndGet(); - } - })); - } - - CompletableFuture overallFuture = CompletableFutures.allOf(futures); - - while (true) { - try { - overallFuture.get(3, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - break; - } catch (TimeoutException e) { - LogUtils.info("Progress: " + userCount.get() + "/" + uuids.size()); - continue; - } - break; - } - - JsonObject outJson = new JsonObject(); - for (Map.Entry entry : out.entrySet()) { - outJson.addProperty(entry.getKey().toString(), entry.getValue()); - } - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); - String formattedDate = formatter.format(new Date()); - File outFile = new File(plugin.getDataFolder(), "exported-" + formattedDate + ".json.gz"); - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(outFile.toPath())), StandardCharsets.UTF_8))) { - new GsonBuilder().disableHtmlEscaping().create().toJson(outJson, writer); - } catch (IOException e) { - e.printStackTrace(); - } - - dataStorageInterface.disable(); - - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Completed."); - }); - }) - , - new CommandAPICommand("import") - .withArguments(new StringArgument("file")) - .executes((sender, args) -> { - String fileName = (String) args.get("file"); - if (fileName == null) return; - CustomNameplatesPlugin plugin = CustomNameplatesPlugin.get(); - File file = new File(plugin.getDataFolder(), fileName); - if (!file.exists()) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "File not exists."); - return; - } - if (!file.getName().endsWith(".json.gz")) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Invalid file."); - return; - } - plugin.getScheduler().runTaskAsync(() -> { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Starting import."); - JsonObject data; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file.toPath())), StandardCharsets.UTF_8))) { - data = new GsonBuilder().disableHtmlEscaping().create().fromJson(reader, JsonObject.class); - } catch (IOException e) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Error occurred when reading the backup file."); - e.printStackTrace(); - return; - } - DataStorageInterface dataStorageInterface = plugin.getStorageManager().getDataSource(); - var entrySet = data.entrySet(); - int amount = entrySet.size(); - AtomicInteger userCount = new AtomicInteger(0); - Set> futures = new HashSet<>(); - - for (Map.Entry entry : entrySet) { - UUID uuid = UUID.fromString(entry.getKey()); - if (entry.getValue() instanceof JsonPrimitive primitive) { - PlayerData playerData = plugin.getStorageManager().fromJson(primitive.getAsString()); - futures.add(dataStorageInterface.updateOrInsertPlayerData(uuid, playerData).thenAccept(it -> userCount.incrementAndGet())); - } - } - CompletableFuture overallFuture = CompletableFutures.allOf(futures); - while (true) { - try { - overallFuture.get(3, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - break; - } catch (TimeoutException e) { - LogUtils.info("Progress: " + userCount.get() + "/" + amount); - continue; - } - break; - } - AdventureManagerImpl.getInstance().sendMessageWithPrefix(sender, "Completed."); - }); - }) - ); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/Dependency.java b/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/Dependency.java deleted file mode 100644 index 2004c9b..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/dependencies/Dependency.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customnameplates.paper.libraries.dependencies; - -import com.google.common.collect.ImmutableList; -import net.momirealms.customnameplates.paper.libraries.dependencies.relocation.Relocation; -import org.jetbrains.annotations.Nullable; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.Locale; - -/** - * The dependencies used by CustomNameplates. - */ -public enum Dependency { - - ASM( - "org.ow2.asm", - "asm", - "9.7", - null, - "asm" - ), - ASM_COMMONS( - "org.ow2.asm", - "asm-commons", - "9.7", - null, - "asm-commons" - ), - JAR_RELOCATOR( - "me.lucko", - "jar-relocator", - "1.7", - null, - "jar-relocator" - ), - COMMAND_API( - "dev{}jorel", - "commandapi-bukkit-shade", - "9.5.3", - null, - "commandapi-bukkit", - Relocation.of("commandapi", "dev{}jorel{}commandapi") - ), - COMMAND_API_MOJMAP( - "dev{}jorel", - "commandapi-bukkit-shade-mojang-mapped", - "9.5.3", - null, - "commandapi-bukkit-mojang-mapped", - Relocation.of("commandapi", "dev{}jorel{}commandapi") - ), - MARIADB_DRIVER( - "org{}mariadb{}jdbc", - "mariadb-java-client", - "3.3.3", - null, - "mariadb-java-client", - Relocation.of("mariadb", "org{}mariadb") - ), - BOOSTED_YAML( - "dev{}dejvokep", - "boosted-yaml", - "1.3.6", - null, - "boosted-yaml", - Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") - ), - MYSQL_DRIVER( - "com{}mysql", - "mysql-connector-j", - "9.0.0", - null, - "mysql-connector-j", - Relocation.of("mysql", "com{}mysql") - ), - H2_DRIVER( - "com.h2database", - "h2", - "2.2.224", - null, - "h2database" - ), - SQLITE_DRIVER( - "org.xerial", - "sqlite-jdbc", - "3.46.0.1", - null, - "sqlite-jdbc" - ), - HIKARI( - "com{}zaxxer", - "HikariCP", - "5.1.0", - null, - "HikariCP", - Relocation.of("hikari", "com{}zaxxer{}hikari") - ), - SLF4J_SIMPLE( - "org.slf4j", - "slf4j-simple", - "2.0.12", - null, - "slf4j-simple" - ), - SLF4J_API( - "org.slf4j", - "slf4j-api", - "2.0.12", - null, - "slf4j-api" - ), - MONGODB_DRIVER_CORE( - "org{}mongodb", - "mongodb-driver-core", - "5.1.0", - null, - "mongodb-driver-core", - Relocation.of("mongodb", "com{}mongodb"), - Relocation.of("bson", "org{}bson") - ), - MONGODB_DRIVER_SYNC( - "org{}mongodb", - "mongodb-driver-sync", - "5.1.0", - null, - "mongodb-driver-sync", - Relocation.of("mongodb", "com{}mongodb"), - Relocation.of("bson", "org{}bson") - ), - MONGODB_DRIVER_BSON( - "org{}mongodb", - "bson", - "5.1.0", - null, - "mongodb-bson", - Relocation.of("mongodb", "com{}mongodb"), - Relocation.of("bson", "org{}bson") - ), - JEDIS( - "redis{}clients", - "jedis", - "5.1.2", - null, - "jedis", - Relocation.of("jedis", "redis{}clients{}jedis"), - Relocation.of("commonspool2", "org{}apache{}commons{}pool2") - ), - BSTATS_BASE( - "org{}bstats", - "bstats-base", - "3.0.2", - null, - "bstats-base", - Relocation.of("bstats", "org{}bstats") - ), - BSTATS_BUKKIT( - "org{}bstats", - "bstats-bukkit", - "3.0.2", - null, - "bstats-bukkit", - Relocation.of("bstats", "org{}bstats") - ), - COMMONS_POOL_2( - "org{}apache{}commons", - "commons-pool2", - "2.12.0", - null, - "commons-pool2", - Relocation.of("commonspool2", "org{}apache{}commons{}pool2") - ), - GSON( - "com.google.code.gson", - "gson", - "2.10.1", - null, - "gson" - ); - - private final String mavenRepoPath; - private final String version; - private final List relocations; - private final String repo; - private final String artifact; - - private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; - - Dependency(String groupId, String artifactId, String version, String repo, String artifact) { - this(groupId, artifactId, version, repo, artifact, new Relocation[0]); - } - - Dependency(String groupId, String artifactId, String version, String repo, String artifact, Relocation... relocations) { - this.mavenRepoPath = String.format(MAVEN_FORMAT, - rewriteEscaping(groupId).replace(".", "/"), - rewriteEscaping(artifactId), - version, - rewriteEscaping(artifactId), - version - ); - this.version = version; - this.relocations = ImmutableList.copyOf(relocations); - this.repo = repo; - this.artifact = artifact; - } - - private static String rewriteEscaping(String s) { - return s.replace("{}", "."); - } - - public String getFileName(String classifier) { - String name = artifact.toLowerCase(Locale.ROOT).replace('_', '-'); - String extra = classifier == null || classifier.isEmpty() - ? "" - : "-" + classifier; - - return name + "-" + this.version + extra + ".jar"; - } - - String getMavenRepoPath() { - return this.mavenRepoPath; - } - - public List getRelocations() { - return this.relocations; - } - - /** - * Creates a {@link MessageDigest} suitable for computing the checksums - * of dependencies. - * - * @return the digest - */ - public static MessageDigest createDigest() { - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - @Nullable - public String getRepo() { - return repo; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/JarInJarClassLoader.java b/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/JarInJarClassLoader.java deleted file mode 100644 index 345f840..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/libraries/loader/JarInJarClassLoader.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customnameplates.paper.libraries.loader; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -/** - * Classloader that can load a jar from within another jar file. - * - *

The "loader" jar contains the loading code & public API classes, - * and is class-loaded by the platform.

- * - *

The inner "plugin" jar contains the plugin itself, and is class-loaded - * by the loading code & this classloader.

- */ -public class JarInJarClassLoader extends URLClassLoader { - static { - ClassLoader.registerAsParallelCapable(); - } - - /** - * Creates a new jar-in-jar class loader. - * - * @param loaderClassLoader the loader plugin's classloader (setup and created by the platform) - * @param jarResourcePath the path to the jar-in-jar resource within the loader jar - * @throws LoadingException if something unexpectedly bad happens - */ - public JarInJarClassLoader(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { - super(new URL[]{extractJar(loaderClassLoader, jarResourcePath)}, loaderClassLoader); - } - - public void addJarToClasspath(URL url) { - addURL(url); - } - - public void deleteJarResource() { - URL[] urls = getURLs(); - if (urls.length == 0) { - return; - } - - try { - Path path = Paths.get(urls[0].toURI()); - Files.deleteIfExists(path); - } catch (Exception e) { - // ignore - } - } - - /** - * Creates a new plugin instance. - * - * @param bootstrapClass the name of the bootstrap plugin class - * @param loaderPluginType the type of the loader plugin, the only parameter of the bootstrap - * plugin constructor - * @param loaderPlugin the loader plugin instance - * @param the type of the loader plugin - * @return the instantiated bootstrap plugin - */ - public LoaderBootstrap instantiatePlugin(String bootstrapClass, Class loaderPluginType, T loaderPlugin) throws LoadingException { - Class plugin; - try { - plugin = loadClass(bootstrapClass).asSubclass(LoaderBootstrap.class); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to load bootstrap class", e); - } - - Constructor constructor; - try { - constructor = plugin.getConstructor(loaderPluginType); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to get bootstrap constructor", e); - } - - try { - return constructor.newInstance(loaderPlugin); - } catch (ReflectiveOperationException e) { - throw new LoadingException("Unable to create bootstrap plugin instance", e); - } - } - - /** - * Extracts the "jar-in-jar" from the loader plugin into a temporary file, - * then returns a URL that can be used by the {@link JarInJarClassLoader}. - * - * @param loaderClassLoader the classloader for the "host" loader plugin - * @param jarResourcePath the inner jar resource path - * @return a URL to the extracted file - */ - private static URL extractJar(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { - // get the jar-in-jar resource - URL jarInJar = loaderClassLoader.getResource(jarResourcePath); - if (jarInJar == null) { - throw new LoadingException("Could not locate jar-in-jar"); - } - - // create a temporary file - // on posix systems by default this is only read/writable by the process owner - Path path; - try { - path = Files.createTempFile("customnameplates-jarinjar", ".jar.tmp"); - } catch (IOException e) { - throw new LoadingException("Unable to create a temporary file", e); - } - - // mark that the file should be deleted on exit - path.toFile().deleteOnExit(); - - // copy the jar-in-jar to the temporary file path - try (InputStream in = jarInJar.openStream()) { - Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new LoadingException("Unable to copy jar-in-jar to temporary path", e); - } - - try { - return path.toUri().toURL(); - } catch (MalformedURLException e) { - throw new LoadingException("Unable to get URL from path", e); - } - } - -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarConfig.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarConfig.java deleted file mode 100644 index facc508..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarConfig.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.actionbar; - -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.paper.mechanic.bossbar.BarColor; -import net.momirealms.customnameplates.paper.mechanic.bossbar.Overlay; -import net.momirealms.customnameplates.paper.mechanic.misc.TimeLimitText; - -public class ActionBarConfig { - - private int checkFrequency; - private Requirement[] requirements; - private TimeLimitText[] textDisplayOrder; - - private ActionBarConfig() { - checkFrequency = 1; - requirements = new Requirement[0]; - textDisplayOrder = new TimeLimitText[0]; - } - - public ActionBarConfig( - Overlay overlay, - BarColor barColor, - int checkFrequency, - Requirement[] requirements, - TimeLimitText[] textDisplayOrder - ) { - this.checkFrequency = checkFrequency; - this.requirements = requirements; - this.textDisplayOrder = textDisplayOrder; - } - - public int getCheckFrequency() { - return checkFrequency; - } - - public Requirement[] getRequirements() { - return requirements; - } - - public TimeLimitText[] getTextDisplayOrder() { - return textDisplayOrder; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final ActionBarConfig config; - - public Builder() { - this.config = new ActionBarConfig(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder checkFrequency(int checkFrequency) { - config.checkFrequency = checkFrequency; - return this; - } - - public Builder requirement(Requirement[] requirements) { - config.requirements = requirements; - return this; - } - - public Builder displayOrder(TimeLimitText[] textDisplayOrder) { - config.textDisplayOrder = textDisplayOrder; - return this; - } - - public ActionBarConfig build() { - return config; - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarManagerImpl.java deleted file mode 100644 index b6608dd..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarManagerImpl.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.actionbar; - -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.wrappers.EnumWrappers; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TranslatableComponent; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.ActionBarManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.ActionBarListener; -import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.ChatMessageListener; -import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.SystemChatListener; -import net.momirealms.customnameplates.paper.mechanic.misc.DisplayController; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.ConfigUtils; -import net.momirealms.customnameplates.paper.util.ReflectionUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class ActionBarManagerImpl implements ActionBarManager, Listener { - - private final ConcurrentHashMap receiverMap; - private final CustomNameplatesPlugin plugin; - private final ActionBarListener actionBarListener; - private ActionBarConfig config; - private ChatMessageListener chatMessageListener; - private SystemChatListener systemChatListener; - private final MiniMessage miniMessage; - - public ActionBarManagerImpl(CustomNameplatesPlugin plugin) { - this.receiverMap = new ConcurrentHashMap<>(); - this.plugin = plugin; - this.actionBarListener = new ActionBarListener(this); - this.miniMessage = MiniMessage.builder() - .strict(true) - .build(); - if (plugin.getVersionManager().isVersionNewerThan1_19()) { - this.systemChatListener = new SystemChatListener(this); - } else { - this.chatMessageListener = new ChatMessageListener(this); - } - } - - public void load() { - if (!CNConfig.actionBarModule) return; - this.loadConfigs(); - for (Player player : Bukkit.getOnlinePlayers()) { - createActionBarFor(player); - } - - Bukkit.getPluginManager().registerEvents(this, plugin); - if (CNConfig.catchOtherActionBar) { - if (actionBarListener != null) ProtocolLibrary.getProtocolManager().addPacketListener(actionBarListener); - if (systemChatListener != null) ProtocolLibrary.getProtocolManager().addPacketListener(systemChatListener); - if (chatMessageListener != null) ProtocolLibrary.getProtocolManager().addPacketListener(chatMessageListener); - } - } - - public void unload() { - for (ActionBarReceiver receiver : receiverMap.values()) { - receiver.cancelTask(); - receiver.destroy(); - } - receiverMap.clear(); - - HandlerList.unregisterAll(this); - if (actionBarListener != null) ProtocolLibrary.getProtocolManager().removePacketListener(actionBarListener); - if (systemChatListener != null) ProtocolLibrary.getProtocolManager().removePacketListener(systemChatListener); - if (chatMessageListener != null) ProtocolLibrary.getProtocolManager().removePacketListener(chatMessageListener); - } - - public void reload() { - unload(); - load(); - } - - private void loadConfigs() { - YamlConfiguration config = plugin.getConfig("configs" + File.separator + "actionbar.yml"); - boolean temp = false; - for (Map.Entry barEntry : config.getValues(false).entrySet()) { - if (!(barEntry.getValue() instanceof ConfigurationSection section)) - return; - - if (temp) { - LogUtils.warn("You can create at most 1 actionbar in actionbar.yml. Actionbar " + barEntry.getKey() + " would not work."); - continue; - } - - this.config = ActionBarConfig.builder() - .checkFrequency(section.getInt("check-frequency", 10)) - .requirement(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"))) - .displayOrder(ConfigUtils.getTimeLimitTexts(section.getConfigurationSection("text-display-order"))) - .build(); - temp = true; - } - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - final Player player = event.getPlayer(); - if (CNConfig.sendDelay == 0) { - createActionBarFor(player); - return; - } - this.plugin.getScheduler().runTaskAsyncLater(() -> { - createActionBarFor(player); - }, CNConfig.sendDelay * 50L, TimeUnit.MILLISECONDS); - } - - private void createActionBarFor(Player player) { - if (player == null || !player.isOnline()) { - return; - } - ActionBarReceiver receiver = new ActionBarReceiver(plugin, player, new DisplayController( - player, config.getCheckFrequency(), config.getRequirements(), config.getTextDisplayOrder() - )); - receiver.arrangeTask(); - this.putReceiverToMap(player.getUniqueId(), receiver); - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - ActionBarReceiver receiver = receiverMap.remove(event.getPlayer().getUniqueId()); - if (receiver != null) { - receiver.cancelTask(); - } - } - - private void putReceiverToMap(UUID uuid, ActionBarReceiver actionBarReceiver) { - ActionBarReceiver previous = this.receiverMap.put(uuid, actionBarReceiver); - if (previous != null) { - LogUtils.warn("Unexpected error: Duplicated actionbar created"); - previous.cancelTask(); - } - } - - private ActionBarReceiver getReceiver(UUID uuid) { - return this.receiverMap.get(uuid); - } - - // 1.19+ - public void onReceiveSystemChatPacket(PacketEvent event) { - PacketContainer packet = event.getPacket(); - Boolean overlay = packet.getBooleans().readSafely(0); - if (overlay != null && overlay) { - ActionBarReceiver receiver = getReceiver(event.getPlayer().getUniqueId()); - if (receiver != null) { - event.setCancelled(true); - String json = packet.getStrings().readSafely(0); - if (json != null && !json.isEmpty()) { - Component component = GsonComponentSerializer.gson().deserialize(json); - if (component instanceof TranslatableComponent) { - // We can't get TranslatableComponent's width :( - return; - } - receiver.setOtherPluginText(miniMessage.serialize(component), System.currentTimeMillis()); - } else { - WrappedChatComponent wrappedChatComponent = packet.getChatComponents().readSafely(0); - if (wrappedChatComponent != null) { - json = wrappedChatComponent.getJson(); - Component component = GsonComponentSerializer.gson().deserialize(json); - if (component instanceof TranslatableComponent) { - return; - } - receiver.setOtherPluginText(miniMessage.serialize(component), System.currentTimeMillis()); - } - } - } - } - } - - // lower version - public void onReceiveChatMessagePacket(PacketEvent event) { - PacketContainer packet = event.getPacket(); - EnumWrappers.ChatType type = packet.getChatTypes().readSafely(0); - if (type == EnumWrappers.ChatType.GAME_INFO) { - ActionBarReceiver receiver = getReceiver(event.getPlayer().getUniqueId()); - if (receiver != null) { - event.setCancelled(true); - WrappedChatComponent wrappedChatComponent = packet.getChatComponents().read(0); - if (wrappedChatComponent != null) { - String json = wrappedChatComponent.getJson(); - Component component = GsonComponentSerializer.gson().deserialize(json); - if (component instanceof TranslatableComponent) { - // We can't get TranslatableComponent's width :( - return; - } - receiver.setOtherPluginText(miniMessage.serialize(component), System.currentTimeMillis()); - } - } - } - } - - public void onReceiveActionBarPacket(PacketEvent event) { - PacketContainer packet = event.getPacket(); - ActionBarReceiver receiver = getReceiver(event.getPlayer().getUniqueId()); - if (receiver != null) { - WrappedChatComponent wrappedChatComponent = packet.getChatComponents().read(0); - if (wrappedChatComponent != null) { - String strJson = wrappedChatComponent.getJson(); - // for better performance - if (strJson.contains("\"name\":\"np\",\"objective\":\"ab\"")) { - return; - } - event.setCancelled(true); - receiver.setOtherPluginText( - miniMessage.serialize(GsonComponentSerializer.gson().deserialize(strJson)), System.currentTimeMillis() - ); - } else if (ReflectionUtils.isPaper()) { - Object adventureComponent = packet.getModifier().readSafely(1); - if (adventureComponent != null) { - String other = ReflectionUtils.getMiniMessageTextFromNonShadedComponent(adventureComponent); - receiver.setOtherPluginText(other, System.currentTimeMillis()); - } - } - } - } - - @NotNull - @Override - public String getOtherPluginActionBar(Player player) { - ActionBarReceiver receiver = getReceiver(player.getUniqueId()); - if (receiver != null) { - return receiver.getOtherPluginText(); - } - return ""; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarReceiver.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarReceiver.java deleted file mode 100644 index a2437ff..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/ActionBarReceiver.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.actionbar; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.misc.DisplayController; -import org.bukkit.entity.Player; - -import java.util.concurrent.TimeUnit; - -public class ActionBarReceiver { - - private final CustomNameplatesPlugin plugin; - private final Player player; - private CancellableTask actionBarTask; - private final DisplayController controller; - private long lastUpdateTime; - private String otherPluginText; - private long expireTime; - - public ActionBarReceiver(CustomNameplatesPlugin plugin, Player player, DisplayController controller) { - this.plugin = plugin; - this.player = player; - this.controller = controller; - this.otherPluginText = ""; - } - - public void timer() { - if (System.currentTimeMillis() > expireTime) { - this.otherPluginText = ""; - } - Condition condition = new Condition(player); - switch (controller.stateCheck(condition)) { - case KEEP -> { - if (!controller.isShown()) { - return; - } - long current = System.currentTimeMillis(); - if (controller.updateText(condition) || shouldSendBeatPack(current)) { - AdventureManagerImpl.getInstance().sendActionbar(player, controller.getLatestContent()); - lastUpdateTime = current; - } - } - case UPDATE -> { - if (controller.isShown()) { - controller.initialize(condition); - AdventureManagerImpl.getInstance().sendActionbar(player, controller.getLatestContent()); - } else { - AdventureManagerImpl.getInstance().sendActionbar(player, ""); - } - } - } - } - - public void arrangeTask() { - if (this.actionBarTask != null && !this.actionBarTask.isCancelled()) { - LogUtils.warn("There's already an ActionBar task running"); - return; - } - this.actionBarTask = this.plugin.getScheduler().runTaskAsyncTimer(() -> { - try { - timer(); - } catch (Exception e) { - LogUtils.severe( - "Error occurred when sending ActionBars. " + - "This might not be a bug in CustomNameplates. Please report " + - "to the Plugin on the top of the following " + - "stack trace." - ); - e.printStackTrace(); - } - }, 50, 50, TimeUnit.MILLISECONDS); - } - - public void cancelTask() { - if (this.actionBarTask == null) { - LogUtils.warn("ActionBar task has been already cancelled"); - return; - } - this.actionBarTask.cancel(); - this.actionBarTask = null; - } - - public boolean shouldSendBeatPack(long current) { - return current - lastUpdateTime > 1700; - } - - public void setOtherPluginText(String text, long current) { - this.otherPluginText = text; - this.expireTime = current + 3000; - } - - public String getOtherPluginText() { - return otherPluginText; - } - - public void destroy() { - AdventureManagerImpl.getInstance().sendActionbar(player, ""); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/ActionBarListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/ActionBarListener.java deleted file mode 100644 index 7518d23..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/ActionBarListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.actionbar.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.actionbar.ActionBarManagerImpl; - -public class ActionBarListener extends PacketAdapter { - - private final ActionBarManagerImpl actionBarManager; - - public ActionBarListener(ActionBarManagerImpl actionBarManager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.SET_ACTION_BAR_TEXT); - this.actionBarManager = actionBarManager; - } - - public void onPacketSending(PacketEvent event) { - actionBarManager.onReceiveActionBarPacket(event); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/ChatMessageListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/ChatMessageListener.java deleted file mode 100644 index ccfbcea..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/ChatMessageListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.actionbar.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.actionbar.ActionBarManagerImpl; - -public class ChatMessageListener extends PacketAdapter { - - private final ActionBarManagerImpl actionBarManager; - - public ChatMessageListener(ActionBarManagerImpl actionBarManager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.CHAT); - this.actionBarManager = actionBarManager; - } - - public void onPacketSending(PacketEvent event) { - actionBarManager.onReceiveChatMessagePacket(event); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/SystemChatListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/SystemChatListener.java deleted file mode 100644 index 04ac7bb..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/actionbar/listener/SystemChatListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.actionbar.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.actionbar.ActionBarManagerImpl; - -public class SystemChatListener extends PacketAdapter { - - private final ActionBarManagerImpl actionBarManager; - - public SystemChatListener(ActionBarManagerImpl actionBarManager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.SYSTEM_CHAT); - this.actionBarManager = actionBarManager; - } - - public void onPacketSending(PacketEvent event) { - actionBarManager.onReceiveSystemChatPacket(event); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/background/BackGroundManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/background/BackGroundManagerImpl.java deleted file mode 100644 index e7a95d9..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/background/BackGroundManagerImpl.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.background; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BackGroundManager; -import net.momirealms.customnameplates.api.mechanic.background.BackGround; -import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; - -public class BackGroundManagerImpl implements BackGroundManager { - - private final HashMap backGroundMap; - private final CustomNameplatesPlugin plugin; - - public BackGroundManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.backGroundMap = new HashMap<>(); - } - - public void reload() { - unload(); - load(); - } - - public void load() { - if (!CNConfig.backgroundModule) return; - this.loadConfigs(); - } - - public void unload() { - this.backGroundMap.clear(); - } - - private void loadConfigs() { - File bgFolder = new File(plugin.getDataFolder(),"contents" + File.separator + "backgrounds"); - if (!bgFolder.exists() && bgFolder.mkdirs()) { - saveDefaultBackgrounds(); - } - File[] bgConfigFiles = bgFolder.listFiles(file -> file.getName().endsWith(".yml")); - if (bgConfigFiles == null) return; - Arrays.sort(bgConfigFiles, Comparator.comparing(File::getName)); - for (File bgConfigFile : bgConfigFiles) { - String key = bgConfigFile.getName().substring(0, bgConfigFile.getName().length() - 4); - - YamlConfiguration config = YamlConfiguration.loadConfiguration(bgConfigFile); - int height = config.getInt("middle.height", 14); - int ascent = config.getInt("middle.ascent", 8); - if (config.contains("middle.descent")) ascent = height - config.getInt("middle.descent"); - - var background = BackGround.builder() - .leftMargin(config.getInt("left-margin", 1)) - .rightMargin(config.getInt("right-margin", 1)) - .left( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("left.image")) - .height(config.getInt("left.height")) - .ascent(config.getInt("left.ascent")) - .width(config.getInt("left.width")) - .build() - ) - .offset_1( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.1")) - .height(height) - .ascent(ascent) - .width(1) - .build() - ) - .offset_2( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.2")) - .height(height) - .ascent(ascent) - .width(2) - .build() - ) - .offset_4( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.4")) - .height(height) - .ascent(ascent) - .width(4) - .build() - ) - .offset_8( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.8")) - .height(height) - .ascent(ascent) - .width(8) - .build() - ) - .offset_16( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.16")) - .height(height) - .ascent(ascent) - .width(16) - .build() - ) - .offset_32( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.32")) - .height(height) - .ascent(ascent) - .width(32) - .build() - ) - .offset_64( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.64")) - .height(height) - .ascent(ascent) - .width(64) - .build() - ) - .offset_128( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.128")) - .height(height) - .ascent(ascent) - .width(128) - .build() - ) - .right( - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("right.image")) - .height(config.getInt("right.height")) - .ascent(config.getInt("right.ascent")) - .width(config.getInt("right.width")) - .build() - ) - .build(); - - if (!registerBackGround(key, background)) { - LogUtils.warn("Found duplicated background: " + key); - } - } - } - - @Override - public BackGround getBackGround(@NotNull String key) { - return backGroundMap.get(key); - } - - @Override - public Collection getBackGrounds() { - return backGroundMap.values(); - } - - @Override - public boolean registerBackGround(@NotNull String key, @NotNull BackGround backGround) { - if (backGroundMap.containsKey(key)) { - return false; - } - backGroundMap.put(key, backGround); - return true; - } - - @Override - public boolean unregisterBackGround(@NotNull String key) { - return backGroundMap.remove(key) != null; - } - - private void saveDefaultBackgrounds() { - String[] bg_list = new String[]{"b0", "b1", "b2", "b4", "b8", "b16","b32","b64","b128"}; - for (String bg : bg_list) { - plugin.saveResource("contents" + File.separator + "backgrounds" + File.separator + bg + ".png", false); - } - String[] config_list = new String[]{"bedrock_1", "bedrock_2"}; - for (String config : config_list) { - plugin.saveResource("contents" + File.separator + "backgrounds" + File.separator + config + ".yml", false); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBar.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBar.java deleted file mode 100644 index 2312a1f..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBar.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bossbar; - -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.sparrow.heart.SparrowHeart; -import net.momirealms.sparrow.heart.feature.bossbar.BossBarColor; -import net.momirealms.sparrow.heart.feature.bossbar.BossBarOverlay; -import org.bukkit.entity.Player; - -import java.util.UUID; - -public class BossBar { - - private final Overlay overlay; - private final BarColor barColor; - private final UUID uuid; - private final Player owner; - private String latestMiniMessage; - private boolean visible; - - public BossBar(Player owner, Overlay overlay, BarColor barColor) { - this.owner = owner; - this.overlay = overlay; - this.barColor = barColor; - this.uuid = UUID.randomUUID(); - this.visible = false; - } - - public void show() { - SparrowHeart.getInstance().createBossBar(owner, uuid, AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(latestMiniMessage), BossBarColor.valueOf(barColor.name()), BossBarOverlay.valueOf(overlay.name()), 0f, false, false, false); - this.visible = true; - } - - public void hide() { - SparrowHeart.getInstance().removeBossBar(owner, uuid); - this.visible = false; - } - - public void update() { - SparrowHeart.getInstance().updateBossBarName(owner, uuid, AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(latestMiniMessage)); - } - - public boolean isVisible() { - return visible; - } - - public void setMiniMessageText(String text) { - latestMiniMessage = text; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarConfig.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarConfig.java deleted file mode 100644 index 3cea6f5..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarConfig.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bossbar; - -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.paper.mechanic.misc.TimeLimitText; - -public class BossBarConfig { - - private Overlay overlay; - private BarColor barColor; - private int checkFrequency; - private Requirement[] requirements; - private TimeLimitText[] textDisplayOrder; - - private BossBarConfig() { - overlay = Overlay.PROGRESS; - barColor = BarColor.YELLOW; - checkFrequency = 1; - requirements = new Requirement[0]; - textDisplayOrder = new TimeLimitText[0]; - } - - public BossBarConfig( - Overlay overlay, - BarColor barColor, - int checkFrequency, - Requirement[] requirements, - TimeLimitText[] textDisplayOrder - ) { - this.overlay = overlay; - this.barColor = barColor; - this.checkFrequency = checkFrequency; - this.requirements = requirements; - this.textDisplayOrder = textDisplayOrder; - } - - public Overlay getOverlay() { - return overlay; - } - - public BarColor getBarColor() { - return barColor; - } - - public int getCheckFrequency() { - return checkFrequency; - } - - public Requirement[] getRequirements() { - return requirements; - } - - public TimeLimitText[] getTextDisplayOrder() { - return textDisplayOrder; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final BossBarConfig config; - - public Builder() { - this.config = new BossBarConfig(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder overlay(Overlay overlay) { - config.overlay = overlay; - return this; - } - - public Builder barColor(BarColor barColor) { - config.barColor = barColor; - return this; - } - - public Builder checkFrequency(int checkFrequency) { - config.checkFrequency = checkFrequency; - return this; - } - - public Builder requirement(Requirement[] requirements) { - config.requirements = requirements; - return this; - } - - public Builder displayOrder(TimeLimitText[] textDisplayOrder) { - config.textDisplayOrder = textDisplayOrder; - return this; - } - - public BossBarConfig build() { - return config; - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarManagerImpl.java deleted file mode 100644 index c04e5e3..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarManagerImpl.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bossbar; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BossBarManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.Pair; -import net.momirealms.customnameplates.paper.mechanic.misc.DisplayController; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -import java.io.File; -import java.util.ArrayList; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class BossBarManagerImpl implements BossBarManager, Listener { - - private final CustomNameplatesPlugin plugin; - private final ConcurrentHashMap receiverMap; - private BossBarConfig[] configs; - - public void load() { - if (!CNConfig.bossBarModule) return; - this.loadConfigs(); - Bukkit.getPluginManager().registerEvents(this, plugin); - for (Player player : Bukkit.getOnlinePlayers()) { - createBossBarFor(player); - } - } - - public void unload() { - HandlerList.unregisterAll(this); - for (BossBarReceiver receiver : receiverMap.values()) { - receiver.cancelTask(); - receiver.destroy(); - } - receiverMap.clear(); - } - - public void reload() { - unload(); - load(); - } - - public BossBarManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.receiverMap = new ConcurrentHashMap<>(); - this.configs = new BossBarConfig[0]; - } - - private void loadConfigs() { - ArrayList configs = new ArrayList<>(); - - YamlConfiguration config = plugin.getConfig("configs" + File.separator + "bossbar.yml"); - for (Map.Entry barEntry : config.getValues(false).entrySet()) { - if (!(barEntry.getValue() instanceof ConfigurationSection section)) - return; - - var barConfig = BossBarConfig.Builder.of() - .barColor(BarColor.getColor(section.getString("color", "YELLOW"))) - .overlay(Overlay.getOverlay(section.getString("overlay", "PROGRESS"))) - .checkFrequency(section.getInt("check-frequency", 10)) - .requirement(plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"))) - .displayOrder(ConfigUtils.getTimeLimitTexts(section.getConfigurationSection("text-display-order"))) - .build(); - - configs.add(barConfig); - } - - this.configs = configs.toArray(new BossBarConfig[0]); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - final Player player = event.getPlayer(); - if (CNConfig.sendDelay == 0) { - createBossBarFor(player); - return; - } - this.plugin.getScheduler().runTaskAsyncLater(() -> { - createBossBarFor(player); - }, CNConfig.sendDelay * 50L, TimeUnit.MILLISECONDS); - } - - private void createBossBarFor(Player player) { - if (player == null || !player.isOnline()) { - return; - } - - Pair[] pairs = new Pair[configs.length]; - for (int i = 0; i < configs.length; i++) { - var config = configs[i]; - pairs[i] = Pair.of( - new DisplayController(player, config.getCheckFrequency(), config.getRequirements(), config.getTextDisplayOrder()), - new BossBar(player, config.getOverlay(), config.getBarColor()) - ); - } - - BossBarReceiver bossBarReceiver = new BossBarReceiver(plugin, player, pairs); - bossBarReceiver.arrangeTask(); - this.putReceiverToMap(player.getUniqueId(), bossBarReceiver); - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - BossBarReceiver receiver = receiverMap.remove(event.getPlayer().getUniqueId()); - if (receiver != null) { - receiver.cancelTask(); - } - } - - private void putReceiverToMap(UUID uuid, BossBarReceiver bossBarReceiver) { - BossBarReceiver previous = this.receiverMap.put(uuid, bossBarReceiver); - if (previous != null) { - LogUtils.warn("Unexpected error: Duplicated bossbar created"); - previous.cancelTask(); - } - } - - private BossBarReceiver getReceiver(UUID uuid) { - return this.receiverMap.get(uuid); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarReceiver.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarReceiver.java deleted file mode 100644 index 7612d32..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bossbar/BossBarReceiver.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bossbar; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.Pair; -import net.momirealms.customnameplates.paper.mechanic.misc.DisplayController; -import org.bukkit.entity.Player; - -import java.util.concurrent.TimeUnit; - -public class BossBarReceiver { - - private final CustomNameplatesPlugin plugin; - private final Player player; - private final int bossBarSize; - private final Pair[] bossBars; - private CancellableTask bossBarTask; - - public BossBarReceiver(CustomNameplatesPlugin plugin, Player player, Pair[] bossBars) { - this.player = player; - this.plugin = plugin; - this.bossBars = bossBars; - this.bossBarSize = bossBars.length; - } - - public void arrangeTask() { - if (this.bossBarTask != null && !this.bossBarTask.isCancelled()) { - LogUtils.warn("There's already a BossBar task running"); - return; - } - this.bossBarTask = this.plugin.getScheduler().runTaskAsyncTimer(() -> { - try { - timer(); - } catch (Exception e) { - LogUtils.severe( - "Error occurred when sending BossBars. " + - "This might not be a bug in CustomNameplates. Please report " + - "to the Plugin on the top of the following " + - "stack trace." - ); - e.printStackTrace(); - } - }, 50, 50, TimeUnit.MILLISECONDS); - } - - public void cancelTask() { - if (this.bossBarTask == null) { - LogUtils.warn("BossBar task has been already cancelled"); - return; - } - this.bossBarTask.cancel(); - this.bossBarTask = null; - } - - public void destroy() { - for (Pair pair : bossBars) { - pair.right().hide(); - } - } - - public void timer() { - Condition condition = new Condition(this.player); - for (int i = 0; i < this.bossBarSize; i++) { - Pair pair = this.bossBars[i]; - switch (pair.left().stateCheck(condition)) { - case KEEP -> { - if (!pair.left().isShown()) { - continue; - } - if (pair.left().updateText(condition)) { - pair.right().setMiniMessageText(pair.left().getLatestContent()); - pair.right().update(); - } - } - case UPDATE -> { - var controller = pair.left(); - if (controller.isShown()) { - for (int j = i + 1; j < this.bossBarSize; j++) - if (this.bossBars[j].left().isShown()) - this.bossBars[j].right().hide(); - - controller.initialize(condition); - pair.right().setMiniMessageText(pair.left().getLatestContent()); - pair.right().show(); - - for (int j = i + 1; j < this.bossBarSize; j++) - if (this.bossBars[j].left().isShown()) - this.bossBars[j].right().show(); - } else { - pair.right().hide(); - } - } - } - } - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/BubbleManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/BubbleManagerImpl.java deleted file mode 100644 index c273cdd..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/BubbleManagerImpl.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bubble; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.OnlineUser; -import net.momirealms.customnameplates.api.event.BubblesSpawnEvent; -import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.manager.RequirementManager; -import net.momirealms.customnameplates.api.mechanic.bubble.Bubble; -import net.momirealms.customnameplates.api.mechanic.bubble.ChannelMode; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.EntityTagEntity; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextEntity; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextTagSetting; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.api.util.FontUtils; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.bubble.image.ImageParser; -import net.momirealms.customnameplates.paper.mechanic.bubble.image.ItemsAdderImageImpl; -import net.momirealms.customnameplates.paper.mechanic.bubble.image.OraxenImageImpl; -import net.momirealms.customnameplates.paper.mechanic.bubble.provider.*; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.plugin.PluginManager; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.TimeUnit; - -public class BubbleManagerImpl implements BubbleManager, Listener { - - private AbstractChatProvider chatProvider; - private AbstractChatProvider customProvider; - private ImageParser imageParser; - private final HashMap bubbleMap; - private final CustomNameplatesPluginImpl plugin; - private String defaultBubble; - private String prefix; - private String suffix; - private String startFormat; - private String endFormat; - private double lineSpace; - private double yOffset; - private int stayTime; - private int coolDown; - private int maxCharLength; - private int lengthPerLine; - private int subStringIndex; - private String[] blacklistChannels; - private ChannelMode channelMode; - private Requirement[] requirements; - - public BubbleManagerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - this.bubbleMap = new HashMap<>(); - } - - public void reload() { - unload(); - load(); - } - - public void load() { - if (!CNConfig.bubbleModule) return; - this.loadConfig(); - this.loadBubbles(); - this.registerChatProvider(); - this.registerImageParser(); - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - public void unload() { - this.imageParser = null; - this.bubbleMap.clear(); - if (chatProvider != null) chatProvider.unregister(); - HandlerList.unregisterAll(this); - } - - private void loadConfig() { - YamlConfiguration config = plugin.getConfig("configs" + File.separator + "bubble.yml"); - updateConfigFile(config); - defaultBubble = config.getString("default-bubbles", "chat"); - prefix = config.getString("text-prefix", ""); - suffix = config.getString("text-suffix", ""); - lineSpace = config.getDouble("line-spacing"); - yOffset = config.getDouble("bottom-line-Y-offset"); - stayTime = config.getInt("stay-time", 5); - coolDown = (int) (config.getDouble("cool-down", 1) * 1000); - maxCharLength = config.getInt("max-character-length", 100); - blacklistChannels = config.getStringList("blacklist-channels").toArray(new String[0]); - subStringIndex = config.getInt("sub-string-index", 0); - lengthPerLine = config.getInt("characters-per-line", 30); - startFormat = config.getString("default-format.start", ""); - endFormat = config.getString("default-format.end", ""); - channelMode = ChannelMode.valueOf(config.getString("channel-mode","all").toUpperCase(Locale.ENGLISH)); - requirements = plugin.getRequirementManager().getRequirements(config.getConfigurationSection("requirements")); - } - - private void updateConfigFile(YamlConfiguration config) { - try { - if (!config.contains("requirements")) { - ConfigurationSection section = config.createSection("requirements"); - section.set("!gamemode", "spectator"); - section.set("permission", "bubbles.use"); - section.set("self-disguised", false); - section.set("potion-effect", "INVISIBILITY<0"); - config.save(new File(plugin.getDataFolder(), "configs" + File.separator + "bubble.yml")); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void registerImageParser() { - PluginManager pluginManager = Bukkit.getPluginManager(); - if (pluginManager.isPluginEnabled("Oraxen")) { - this.imageParser = new OraxenImageImpl(); - } else if (pluginManager.isPluginEnabled("ItemsAdder")) { - this.imageParser = new ItemsAdderImageImpl(); - } - } - - private void registerChatProvider() { - if (this.customProvider != null) { - this.chatProvider = customProvider; - } else if (CNConfig.trChatChannel) { - this.chatProvider = new TrChatProvider(this); - } else if (CNConfig.ventureChatChannel) { - this.chatProvider = new VentureChatProvider(this); - } else if (CNConfig.huskChatChannel) { - this.chatProvider = new HuskChatProvider(this); - } else if (CNConfig.carbonChatChannel) { - this.chatProvider = new CarbonChatProvider(this); - } else if (CNConfig.advancedChatChannel) { - this.chatProvider = new AdvancedChatProvider(this); - } else { - try { - Class.forName("io.papermc.paper.event.player.AsyncChatEvent"); - this.chatProvider = new PaperAsyncChatProvider(this); - } catch (ClassNotFoundException e) { - this.chatProvider = new AsyncChatProvider(this); - } - } - this.chatProvider.register(); - } - - @Override - public boolean setCustomChatProvider(AbstractChatProvider provider) { - if (this.customProvider != null) - return false; - this.customProvider = provider; - if (chatProvider != null) chatProvider.unregister(); - this.registerChatProvider(); - return true; - } - - @Override - public boolean removeCustomChatProvider() { - if (this.customProvider != null) { - this.customProvider.unregister(); - this.customProvider = null; - this.registerChatProvider(); - return true; - } - return false; - } - - private void loadBubbles() { - File bubbleFolder = new File(plugin.getDataFolder(), "contents" + File.separator + "bubbles"); - if (!bubbleFolder.exists() && bubbleFolder.mkdirs()) { - saveDefaultBubbles(); - } - File[] bbConfigFiles = bubbleFolder.listFiles(file -> file.getName().endsWith(".yml")); - if (bbConfigFiles == null) return; - Arrays.sort(bbConfigFiles, Comparator.comparing(File::getName)); - for (File bbConfigFile : bbConfigFiles) { - - String key = bbConfigFile.getName().substring(0, bbConfigFile.getName().length() - 4); - if (key.equals("none")) { - LogUtils.severe("You can't use 'none' as bubble's key"); - continue; - } - - YamlConfiguration config = YamlConfiguration.loadConfiguration(bbConfigFile); - if (!registerBubble( - key, - Bubble.builder() - .displayName(config.getString("display-name", key)) - .startFormat(config.getString("text-format.start", "")) - .endFormat(config.getString("text-format.end", "")) - .left(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("left.image", key + "_left")) - .height(config.getInt("left.height", 16)) - .ascent(config.getInt("left.ascent", 12)) - .width(config.getInt("left.width", 16)) - .build()) - .right(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("right.image", key + "_right")) - .height(config.getInt("right.height", 16)) - .ascent(config.getInt("right.ascent", 12)) - .width(config.getInt("right.width", 16)) - .build()) - .middle(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.image", key + "_middle")) - .height(config.getInt("middle.height", 16)) - .ascent(config.getInt("middle.ascent", 12)) - .width(config.getInt("middle.width", 16)) - .build()) - .tail(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("tail.image", key + "_tail")) - .height(config.getInt("tail.height", 16)) - .ascent(config.getInt("tail.ascent", 12)) - .width(config.getInt("tail.width", 16)) - .build()) - .build()) - ) { - LogUtils.warn("Found duplicated bubble: " + key); - } - } - } - - @Override - public boolean registerBubble(String key, Bubble bubble) { - if (this.bubbleMap.containsKey(key)) return false; - this.bubbleMap.put(key, bubble); - return true; - } - - @Override - public boolean unregisterBubble(String key) { - return this.bubbleMap.remove(key) != null; - } - - @Override - public void onChat(Player player, String text) { - onChat(player, text, null); - } - - @Override - public void onChat(Player player, String text, String channel) { - if (!RequirementManager.isRequirementMet(new Condition(player), requirements)) { - return; - } - - if (Bukkit.isPrimaryThread()) { - String finalText = text; - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> onChat(player, finalText)); - return; - } - - var optionalUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); - if (optionalUser.isEmpty()) { - return; - } - - String bubble = optionalUser.get().getBubbleKey(); - - if (text.length() >= subStringIndex) { - text = text.substring(subStringIndex); - } - - text = imageParser != null ? imageParser.parse(player, text) : text; - - BubblesSpawnEvent bubblesEvent = new BubblesSpawnEvent(player, bubble, text); - Bukkit.getPluginManager().callEvent(bubblesEvent); - if (bubblesEvent.isCancelled()) { - return; - } - - bubble = bubblesEvent.getBubble(); - if (bubble.equals("none")) { - bubble = defaultBubble; - } - - Bubble bubbleConfig = getBubble(bubble); - if (bubbleConfig == null) { - if (!bubble.equals("none")) { - LogUtils.warn("Bubble " + bubble + " doesn't exist"); - return; - } - } - - if (plugin.getCoolDownManager().isCoolDown(player.getUniqueId(), "bubble", coolDown)) { - return; - } - - text = AdventureManagerImpl.getInstance().legacyToMiniMessage(bubblesEvent.getText()); - String strippedRawText = MiniMessage.miniMessage().stripTags(text); - if (strippedRawText.length() > maxCharLength) return; - String[] split = splitString(strippedRawText, lengthPerLine); - for (int i = 0; i < split.length; i++) { - int finalIndex = i; - String finalBubble = bubble; - plugin.getScheduler().runTaskAsyncLater( - () -> sendBubble(player, split[finalIndex], bubbleConfig, finalBubble, channel), - (long) i * 250 + 100, - TimeUnit.MILLISECONDS - ); - } - } - - private void sendBubble(Player player, String text, Bubble bubbleConfig, String key, @Nullable String channel) { - if (key.equals("none")) { - text = startFormat + PlaceholderAPI.setPlaceholders(player, prefix) + text + PlaceholderAPI.setPlaceholders(player, suffix) + endFormat; - } else { - text = bubbleConfig.getStartFormat() + PlaceholderAPI.setPlaceholders(player, prefix) + text + PlaceholderAPI.setPlaceholders(player, suffix) + bubbleConfig.getEndFormat(); - int width = FontUtils.getTextWidth(text); - text = bubbleConfig.getPrefixWithFont(width) + text + bubbleConfig.getSuffixWithFont(width); - } - - EntityTagEntity tagEntity = plugin.getNameplateManager().getUnlimitedTagManager().createOrGetTagForEntity(player); - if (tagEntity == null) - return; - - StaticTextEntity entity = tagEntity.addTag(StaticTextTagSetting.builder() - .leaveRule((receiver, owner) -> true) - .comeRule((receiver, owner) -> { - if ((owner instanceof Player sender) && chatProvider.isIgnoring(sender, receiver)) { - return false; - } - switch (channelMode) { - case ALL -> { - return true; - } - case JOINED -> { - return channel == null || chatProvider.hasJoinedChannel(receiver, channel); - } - case CAN_JOIN -> { - return channel == null || chatProvider.canJoinChannel(receiver, channel); - } - } - return false; - }) - .verticalOffset(yOffset) - .defaultText(text) - .id("bubble") - .build()); - - for (StaticTextEntity bubble : tagEntity.getStaticTags()) { - if (bubble.getID().equals("bubble")) { - bubble.setOffset(bubble.getOffset() + lineSpace); - } - } - - plugin.getScheduler().runTaskAsyncLater(() -> { - tagEntity.removeTag(entity); - }, stayTime, TimeUnit.SECONDS); - } - - private String[] splitString(String str, int len) { - int size = (int) Math.ceil((double) str.length() / (double) len); - String[] result = new String[size]; - int index = 0; - for (int i = 0; i < str.length(); i += len) { - if (i + len > str.length()) { - result[index++] = str.substring(i); - } else { - result[index++] = str.substring(i, i + len); - } - } - return result; - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) - public void onDataLoaded(NameplateDataLoadEvent event) { - OnlineUser data = event.getOnlineUser(); - String bubble = data.getBubbleKey(); - if (!bubble.equals("none") && !containsBubble(bubble)) { - if (bubble.equals(defaultBubble)) { - LogUtils.severe("Default nameplate doesn't exist"); - return; - } - - LogUtils.severe("Bubble " + bubble + " doesn't exist. To prevent bugs, player " + event.getUUID() + " 's bubble data is reset"); - data.setBubble("none"); - plugin.getStorageManager().saveOnlinePlayerData(event.getUUID()); - } - } - - @Override - public Collection getBubbleKeys() { - return bubbleMap.keySet(); - } - - @Nullable - @Override - public Bubble getBubble(String bubble) { - return bubbleMap.get(bubble); - } - - @Override - public boolean hasBubble(Player player, String bubble) { - return player.hasPermission("bubbles.equip." + bubble); - } - - @Override - public List getAvailableBubbles(Player player) { - List bubbles = new ArrayList<>(); - for (String nameplate : bubbleMap.keySet()) { - if (hasBubble(player, nameplate)) { - bubbles.add(nameplate); - } - } - return bubbles; - } - - @Override - public List getAvailableBubblesDisplayNames(Player player) { - List bubbles = new ArrayList<>(); - for (Map.Entry entry : bubbleMap.entrySet()) { - if (hasBubble(player, entry.getKey())) { - bubbles.add(entry.getValue().getDisplayName()); - } - } - return bubbles; - } - - @Override - public String[] getBlacklistChannels() { - return blacklistChannels; - } - - @Override - public Collection getBubbles() { - return bubbleMap.values(); - } - - @Override - public boolean containsBubble(String key) { - return bubbleMap.containsKey(key); - } - - @Override - public boolean equipBubble(Player player, String bubbleKey) { - Bubble bubble = getBubble(bubbleKey); - if (bubble == null && bubbleKey.equals("none")) { - return false; - } - plugin.getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(it -> { - if (it.getBubbleKey().equals(bubbleKey)) { - return; - } - it.setBubble(bubbleKey); - plugin.getStorageManager().saveOnlinePlayerData(player.getUniqueId()); - }, () -> { - LogUtils.severe("Player " + player.getName() + "'s data is not loaded."); - }); - return true; - } - - @Override - public void unEquipBubble(Player player) { - plugin.getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(it -> { - if (it.getBubbleKey().equals("none")) { - return; - } - it.setNameplate("none"); - plugin.getStorageManager().saveOnlinePlayerData(player.getUniqueId()); - }, () -> { - LogUtils.severe("Player " + player.getName() + "'s data is not loaded."); - }); - } - - @Override - public String getDefaultBubble() { - return defaultBubble; - } - - private void saveDefaultBubbles() { - String[] png_list = new String[]{"chat"}; - 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.saveResource("contents" + File.separator + "bubbles" + File.separatorChar + name + part, false); - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/AsyncChatProvider.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/AsyncChatProvider.java deleted file mode 100644 index 7102094..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/AsyncChatProvider.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.AsyncPlayerChatEvent; - -public class AsyncChatProvider extends AbstractChatProvider { - - public AsyncChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); - } - - @Override - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); - } - - @Override - public void unregister() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - return true; - } - - @Override - public boolean canJoinChannel(Player player, String channelID) { - return true; - } - - @Override - public boolean isIgnoring(Player sender, Player receiver) { - return false; - } - - // This event is not async sometimes - @EventHandler (ignoreCancelled = true) - public void onChat(AsyncPlayerChatEvent event) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(event.getPlayer(), event.getMessage())); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/CarbonChatProvider.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/CarbonChatProvider.java deleted file mode 100644 index 8b489e1..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/CarbonChatProvider.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; - -import net.draycia.carbon.api.CarbonChat; -import net.draycia.carbon.api.channels.ChannelRegistry; -import net.draycia.carbon.api.channels.ChatChannel; -import net.draycia.carbon.api.event.CarbonEventSubscription; -import net.draycia.carbon.api.event.events.CarbonChatEvent; -import net.draycia.carbon.api.users.CarbonPlayer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.util.ReflectionUtils; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.UUID; - -public class CarbonChatProvider extends AbstractChatProvider { - - private final CarbonChat api; - private CarbonEventSubscription subscription; - private Method originalMessageMethod; - private Method channelKeyMethod; - private Method getChannelByKeyMethod; - - public CarbonChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); - this.api = net.draycia.carbon.api.CarbonChatProvider.carbonChat(); - try { - this.originalMessageMethod = CarbonChatEvent.class.getMethod("originalMessage"); - this.channelKeyMethod = ChatChannel.class.getMethod("key"); - this.getChannelByKeyMethod = ChannelRegistry.class.getMethod("channel", ReflectionUtils.getKeyClass()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void register() { - subscription = api.eventHandler().subscribe(CarbonChatEvent.class, event -> { - if (event.cancelled()) - return; - ChatChannel chatChannel = event.chatChannel(); - Object key = getChannelKey(chatChannel); - if (key == null) return; - String channel = ReflectionUtils.getKeyAsString(key); - for (String black : chatBubblesManager.getBlacklistChannels()) { - if (channel.equals(black)) return; - } - Player player = Bukkit.getPlayer(event.sender().uuid()); - if (player == null || !player.isOnline()) - return; - Object component = getComponentFromEvent(event); - String message = ReflectionUtils.getMiniMessageTextFromNonShadedComponent(component); - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(player, message, channel)); - }); - } - - @Override - public void unregister() { - if (subscription != null) { - subscription.dispose(); - } - } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - CarbonPlayer cPlayer = null; - for (CarbonPlayer carbonPlayer : api.server().players()) { - if (carbonPlayer.uuid().equals(player.getUniqueId())) { - cPlayer = carbonPlayer; - break; - } - } - if (cPlayer == null) { - return false; - } - ChatChannel selectedChannel = cPlayer.selectedChannel(); - ChatChannel currentChannel = selectedChannel != null ? selectedChannel : api.channelRegistry().defaultChannel(); - Object key = getChannelKey(currentChannel); - String str = ReflectionUtils.getKeyAsString(key); - return str.equals(channelID); - } - - @Override - public boolean canJoinChannel(Player player, String channelID) { - ChannelRegistry registry = api.channelRegistry(); - Object key = ReflectionUtils.getKerFromString(channelID); - if (key == null) { - return false; - } - ChatChannel channel = null; - try { - channel = (ChatChannel) getChannelByKeyMethod.invoke(registry, key); - } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); - } - if (channel == null) { - LogUtils.warn("Channel " + channelID + " doesn't exist."); - return false; - } - String perm = channel.permission(); - if (perm == null) { - return true; - } - return player.hasPermission(perm); - } - - @Override - public boolean isIgnoring(Player sender, Player receiver) { - CarbonPlayer sPlayer = carbonPlayer(sender.getUniqueId()); - CarbonPlayer rPlayer = carbonPlayer(receiver.getUniqueId()); - if (sPlayer == null || rPlayer == null) { - return false; - } - return rPlayer.ignoring(sPlayer.uuid()); - } - - @Nullable - private CarbonPlayer carbonPlayer(UUID uuid) { - CarbonPlayer carbonPlayer = null; - for (CarbonPlayer cPlayer : api.server().players()) { - if (cPlayer.uuid().equals(uuid)) { - carbonPlayer = cPlayer; - break; - } - } - return carbonPlayer; - } - - private Object getChannelKey(ChatChannel channel) { - try { - return this.channelKeyMethod.invoke(channel); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - - private Object getComponentFromEvent(CarbonChatEvent event) { - try { - return this.originalMessageMethod.invoke(event); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } - return ReflectionUtils.getEmptyComponent(); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/HuskChatProvider.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/HuskChatProvider.java deleted file mode 100644 index ba7ab20..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/HuskChatProvider.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.william278.huskchat.bukkit.BukkitHuskChat; -import net.william278.huskchat.bukkit.event.ChatMessageEvent; -import net.william278.huskchat.channel.Channel; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; - -public class HuskChatProvider extends AbstractChatProvider { - - public HuskChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); - } - - @Override - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); - } - - @Override - public void unregister() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - String channel = BukkitHuskChat.getInstance().getPlayerCache().getPlayerChannel(player.getUniqueId()); - if (channel == null) { - LogUtils.warn("Channel " + channelID + " doesn't exist."); - return false; - } - return channel.equals(channelID); - } - - @Override - public boolean canJoinChannel(Player player, String channelID) { - Channel channel = BukkitHuskChat.getInstance().getSettings().getChannels().get(channelID); - if (channel == null) { - LogUtils.warn("Channel " + channelID + " doesn't exist."); - return false; - } - String receivePerm = channel.getReceivePermission(); - if (receivePerm == null) { - return true; - } - return player.hasPermission(receivePerm); - } - - @Override - public boolean isIgnoring(Player sender, Player receiver) { - return false; - } - - @EventHandler (ignoreCancelled = true) - public void onHuskChat(ChatMessageEvent event) { - String channel = event.getChannelId(); - for (String black : chatBubblesManager.getBlacklistChannels()) { - if (channel.equals(black)) return; - } - Player player = Bukkit.getPlayer(event.getSender().getUuid()); - if (player == null || !player.isOnline()) - return; - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> { - chatBubblesManager.onChat(player, event.getMessage(), channel); - }); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/PaperAsyncChatProvider.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/PaperAsyncChatProvider.java deleted file mode 100644 index 86d79b1..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/bubble/provider/PaperAsyncChatProvider.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.bubble.provider; - -import io.papermc.paper.event.player.AsyncChatEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.BubbleManager; -import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider; -import net.momirealms.customnameplates.paper.util.ReflectionUtils; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class PaperAsyncChatProvider extends AbstractChatProvider { - - private Method messageMethod; - - public PaperAsyncChatProvider(BubbleManager chatBubblesManager) { - super(chatBubblesManager); - try { - this.messageMethod = AsyncChatEvent.class.getMethod("message"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void register() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); - } - - @Override - public void unregister() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean hasJoinedChannel(Player player, String channelID) { - return true; - } - - @Override - public boolean canJoinChannel(Player player, String channelID) { - return true; - } - - @Override - public boolean isIgnoring(Player sender, Player receiver) { - return false; - } - - @EventHandler (ignoreCancelled = true) - public void onChat(AsyncChatEvent event) { - Object component = getComponentFromEvent(event); - String message = ReflectionUtils.getMiniMessageTextFromNonShadedComponent(component); - CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(event.getPlayer(), message)); - } - - private Object getComponentFromEvent(AsyncChatEvent event) { - try { - return this.messageMethod.invoke(event); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } - return ReflectionUtils.getEmptyComponent(); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/font/WidthManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/font/WidthManagerImpl.java deleted file mode 100644 index 213f421..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/font/WidthManagerImpl.java +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.font; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import me.clip.placeholderapi.PlaceholderAPI; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.internal.parser.node.ElementNode; -import net.kyori.adventure.text.minimessage.internal.parser.node.TagNode; -import net.kyori.adventure.text.minimessage.internal.parser.node.ValueNode; -import net.kyori.adventure.text.minimessage.tag.Inserting; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.WidthManager; -import net.momirealms.customnameplates.api.mechanic.background.BackGround; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.font.FontData; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate; -import net.momirealms.customnameplates.api.mechanic.placeholder.DescentText; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.Key; -import net.momirealms.customnameplates.common.Tuple; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.ConfigUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -public class WidthManagerImpl implements WidthManager { - - private final CustomNameplatesPlugin plugin; - private final HashMap fontDataMap; - private CacheSystem cacheSystem; - private FontData ASCII_DATA; - private FontData ASCII_SGA_DATA; - private FontData ACCENTED_DATA; - private FontData LLAGER_ASCII_DATA; - private FontData NONLATIN_EUROPEAN_DATA; - private FontData UNIFONT_DATA; - private FontData UNICODE_DATA; - private final HashSet bitMapChars; - - public WidthManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.fontDataMap = new HashMap<>(); - this.bitMapChars = new HashSet<>(); - this.saveFontImages(); - this.cacheImageWidthIfNotCached(); - this.loadCachedFontData(); - } - - public void reload() { - unload(); - load(); - } - - public void load() { - this.loadInternalConfigs(); - this.loadUserConfigs(); - this.cacheSystem = new CacheSystem(CNConfig.cacheSize); - } - - public void unload() { - if (this.cacheSystem != null) - this.cacheSystem.destroy(); - fontDataMap.clear(); - bitMapChars.clear(); - } - - private void loadInternalConfigs() { - FontData fontData = new FontData(8); - ArrayList chars = new ArrayList<>(plugin.getImageManager().getImages()); - for (Nameplate nameplate : plugin.getNameplateManager().getNameplates()) { - chars.add(nameplate.getLeft()); - chars.add(nameplate.getMiddle()); - chars.add(nameplate.getRight()); - } - for (BackGround backGround : plugin.getBackGroundManager().getBackGrounds()) { - chars.add(backGround.getLeft()); - chars.add(backGround.getOffset_1()); - chars.add(backGround.getOffset_2()); - chars.add(backGround.getOffset_4()); - chars.add(backGround.getOffset_8()); - chars.add(backGround.getOffset_16()); - chars.add(backGround.getOffset_32()); - chars.add(backGround.getOffset_64()); - chars.add(backGround.getOffset_128()); - chars.add(backGround.getRight()); - } - for (OffsetFont offsetFont : OffsetFont.values()) { - fontData.registerCharWidth(offsetFont.getCharacter(), offsetFont.getSpace() - 1); - } - for (ConfiguredChar configuredChar : chars) { - fontData.registerCharWidth(configuredChar.getCharacter(), configuredChar.getWidth()); - } - registerFontData( - Key.of(CNConfig.namespace, CNConfig.font), fontData - ); - - ArrayList ascentTexts = new ArrayList<>(); - ArrayList ascentUnicodes = new ArrayList<>(); - for (DescentText descentText : plugin.getPlaceholderManager().getDescentTexts()) { - if (descentText.isUnicode()) { - ascentUnicodes.add(descentText.getAscent()); - } else { - ascentTexts.add(descentText.getAscent()); - } - } - ascentTexts.removeAll(ascentUnicodes); - - for (int ascent : ascentTexts) { - FontData descentFont = new FontData(8); - descentFont.overrideWith(NONLATIN_EUROPEAN_DATA); - descentFont.overrideWith(ACCENTED_DATA); - descentFont.overrideWith(ASCII_DATA); - registerFontData( - Key.of(CNConfig.namespace, "ascent_" + ascent), descentFont - ); - } - for (int ascent : ascentUnicodes) { - FontData descentFont = new FontData(8); - descentFont.overrideWith(UNICODE_DATA); - descentFont.overrideWith(NONLATIN_EUROPEAN_DATA); - descentFont.overrideWith(ACCENTED_DATA); - descentFont.overrideWith(ASCII_DATA); - registerFontData( - Key.of(CNConfig.namespace, "ascent_" + ascent), descentFont - ); - } - - this.bitMapChars.addAll(ASCII_DATA.getWidthData().keySet()); - this.bitMapChars.addAll(NONLATIN_EUROPEAN_DATA.getWidthData().keySet()); - this.bitMapChars.addAll(ACCENTED_DATA.getWidthData().keySet()); - } - - private void loadUserConfigs() { - YamlConfiguration config = plugin.getConfig("configs" + File.separator + "font-width-data.yml"); - for (Map.Entry entry : config.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - FontData fontData = new FontData(innerSection.getInt("default", 8)); - for (String template : innerSection.getStringList("template-loading-sequence")) { - switch (template) { - case "ascii" -> fontData.overrideWith(ASCII_DATA); - case "ascii_sga" -> fontData.overrideWith(ASCII_SGA_DATA); - case "accented" -> fontData.overrideWith(ACCENTED_DATA); - case "asciillager" -> fontData.overrideWith(LLAGER_ASCII_DATA); - case "nonlatin_european" -> fontData.overrideWith(NONLATIN_EUROPEAN_DATA); - case "unicode" -> fontData.overrideWith(UNICODE_DATA); - case "unifont" -> fontData.overrideWith(UNIFONT_DATA); - } - } - boolean defaultFont = entry.getKey().equals("minecraft:default"); - ConfigurationSection customSection = innerSection.getConfigurationSection("values"); - if (customSection != null) - for (Map.Entry innerEntry : customSection.getValues(false).entrySet()) { - String key = innerEntry.getKey(); - if (key.contains("%") && !key.equals("%")) { - String stripped = AdventureManagerImpl.getInstance().stripTags(AdventureManagerImpl.getInstance().legacyToMiniMessage(PlaceholderAPI.setPlaceholders(null, key))); - if (stripped.length() != 1) { - LogUtils.warn(key + " is not a supported placeholder"); - continue; - } - fontData.registerCharWidth(stripped.charAt(0), (Integer) innerEntry.getValue()); - if (defaultFont) - bitMapChars.add((int) stripped.charAt(0)); - } else if (key.length() == 1) { - fontData.registerCharWidth(key.charAt(0), (Integer) innerEntry.getValue()); - if (defaultFont) - bitMapChars.add((int) key.charAt(0)); - } else { - LogUtils.warn("Illegal image format: " + key); - } - } - String[] split = entry.getKey().split(":"); - if (!registerFontData(Key.of(split[0], split[1]), fontData)) { - LogUtils.warn("Found duplicated font data: " + entry.getKey()); - } - } - } - } - - private void saveFontImages() { - for (int i = 0; i < 256; i++) { - var path = "font" + File.separator + "unicode_page_" + String.format("%02x", i) + ".png"; - File font_file = new File(plugin.getDataFolder(), path); - if (!font_file.exists()) { - ConfigUtils.saveResource(path); - } - } - for (String font : new String[]{"accented", "ascii", "ascii_sga", "asciillager", "nonlatin_european"}) { - ConfigUtils.saveResource("font" + File.separator + font + ".png"); - } - if (!new File(plugin.getDataFolder(), "font" + File.separator + "unifont.zip").exists()) { - ConfigUtils.saveResource("font" + File.separator + "unifont.zip"); - } - for (String font : new String[]{"default", "unicode"}) { - ConfigUtils.saveResource("font" + File.separator + font + ".json"); - } - } - - private void loadCachedFontData() { - File unifontCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "unifont.yml"); - YamlConfiguration unifontYML = YamlConfiguration.loadConfiguration(unifontCache); - UNIFONT_DATA = new FontData(8); - for (Map.Entry entry : unifontYML.getValues(false).entrySet()) { - char[] chars = ConfigUtils.convertUnicodeStringToChars(entry.getKey()); - int codePoint = chars.length == 2 ? Character.toCodePoint(chars[0], chars[1]) : chars[0]; - UNIFONT_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - - File unicodeCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "unicode.yml"); - YamlConfiguration unicodeYML = YamlConfiguration.loadConfiguration(unicodeCache); - UNICODE_DATA = new FontData(8); - for (Map.Entry entry : unicodeYML.getValues(false).entrySet()) { - int codePoint = ConfigUtils.convertUnicodeStringToChars(entry.getKey())[0]; - UNICODE_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - - File asciiCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "ascii.yml"); - YamlConfiguration asciiYML = YamlConfiguration.loadConfiguration(asciiCache); - ASCII_DATA = new FontData(5); - for (Map.Entry entry : asciiYML.getValues(false).entrySet()) { - int codePoint = ConfigUtils.convertUnicodeStringToChars(entry.getKey())[0]; - ASCII_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - - File asciillagerCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "asciillager.yml"); - YamlConfiguration asciillagerYML = YamlConfiguration.loadConfiguration(asciillagerCache); - LLAGER_ASCII_DATA = new FontData(5); - for (Map.Entry entry : asciillagerYML.getValues(false).entrySet()) { - int codePoint = ConfigUtils.convertUnicodeStringToChars(entry.getKey())[0]; - LLAGER_ASCII_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - - File asciiSgaCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "ascii_sga.yml"); - YamlConfiguration asciiSgaYML = YamlConfiguration.loadConfiguration(asciiSgaCache); - ASCII_SGA_DATA = new FontData(5); - for (Map.Entry entry : asciiSgaYML.getValues(false).entrySet()) { - int codePoint = ConfigUtils.convertUnicodeStringToChars(entry.getKey())[0]; - ASCII_SGA_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - - File accentedCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "accented.yml"); - YamlConfiguration accentedYML = YamlConfiguration.loadConfiguration(accentedCache); - ACCENTED_DATA = new FontData(5); - for (Map.Entry entry : accentedYML.getValues(false).entrySet()) { - int codePoint = ConfigUtils.convertUnicodeStringToChars(entry.getKey())[0]; - ACCENTED_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - - File nonlatinEuropeanCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "nonlatin_european.yml"); - YamlConfiguration nonlatinEuropeanYML = YamlConfiguration.loadConfiguration(nonlatinEuropeanCache); - NONLATIN_EUROPEAN_DATA = new FontData(5); - for (Map.Entry entry : nonlatinEuropeanYML.getValues(false).entrySet()) { - char[] chars = ConfigUtils.convertUnicodeStringToChars(entry.getKey()); - int codePoint = chars.length == 2 ? Character.toCodePoint(chars[0], chars[1]) : chars[0]; - NONLATIN_EUROPEAN_DATA.registerCharWidth(codePoint, (int) entry.getValue()); - } - } - - @SuppressWarnings("DuplicatedCode") - private void cacheImageWidthIfNotCached() { - File unifontCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "unifont.yml"); - if (!unifontCache.exists()) { - File unihex = new File(plugin.getDataFolder(), "font" + File.separator + "unifont.zip"); - if (unihex.exists()) { - try (ZipFile zipFile = new ZipFile(unihex)) { - Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - if (entry.getName().endsWith(".hex") && !entry.isDirectory()) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(zipFile.getInputStream(entry)))) { - YamlConfiguration yml = new YamlConfiguration(); - String line; - while ((line = reader.readLine()) != null) { - String[] parts = line.split(":"); - if (parts.length > 1) { - // get the unicode - int codePoint = Integer.parseInt(parts[0], 16); - char[] surrogatePair = Character.toChars(codePoint); - StringBuilder s = new StringBuilder(); - for (char c : surrogatePair) { - s.append(ConfigUtils.native2ascii(c)); - } - String unicode = s.toString(); - - // width in pixels - String hexData = parts[1]; - int width = hexData.length() / 4; - int high = 4; - int low = 4; - int splitInterval = width / 4; - - int x; - int n; - outer: - { - for (x = 0; x < splitInterval; x++) { - inner: - for (int y = 0; y < 16; y++) { - int pos = y * splitInterval + x; - char selectedHex = hexData.charAt(pos); - int decimal = Character.digit(selectedHex, 16); - for (int k = 0; k < low; k++) { - if ((decimal & (1 << (3 - k))) != 0) { - low = k; - if (low == 0) { - break outer; - } else { - continue inner; - } - } - } - } - if (low != 4) { - break outer; - } - } - yml.set(unicode, -1); - continue; - } - - outer: - for (n = splitInterval - 1; n >= x; n--) { - inner: - for (int y = 0; y < 16; y++) { - int pos = y * splitInterval + n; - char selectedHex = hexData.charAt(pos); - int decimal = Character.digit(selectedHex, 16); - for (int k = 0; k < high; k++) { - if ((decimal & (1 << k)) != 0) { - high = k; - if (high == 0) { - break outer; - } else { - continue inner; - } - } - } - } - if (high != 4) { - break; - } - } - yml.set(unicode, ((n - x + 1) * 4 - high - low) / 2); - } - } - for (int i = 0x3001; i <= 0x30FF; i++) { - char c = (char) i; - yml.set(ConfigUtils.native2ascii(c), 8); - } - for (int i = 0x3200; i <= 0x9FFF; i++) { - char c = (char) i; - yml.set(ConfigUtils.native2ascii(c), 8); - } - for (int i = 0xAC00; i <= 0xD7AF; i++) { - char c = (char) i; - yml.set(ConfigUtils.native2ascii(c), 7); - } - for (int i = 0xF900; i <= 0xFAFF; i++) { - char c = (char) i; - yml.set(ConfigUtils.native2ascii(c), 8); - } - for (int i = 0xFF01; i <= 0xFF5E; i++) { - char c = (char) i; - yml.set(ConfigUtils.native2ascii(c), 8); - } - yml.set("\\u0020", 3); - yml.save(unifontCache); - } - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - File asciiCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "ascii.yml"); - if (!asciiCache.exists()) { - File png = new File(plugin.getDataFolder(), "font" + File.separator + "ascii.png"); - if (png.exists()) { - try { - BufferedImage bufferedImage = ImageIO.read(png); - YamlConfiguration yml = new YamlConfiguration(); - int i; - int j = 0; - for (String line : new String[]{ - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0020\\u0021\\u0022\\u0023\\u0024\\u0025\\u0026\\u0027\\u0028\\u0029\\u002a\\u002b\\u002c\\u002d\\u002e\\u002f", - "\\u0030\\u0031\\u0032\\u0033\\u0034\\u0035\\u0036\\u0037\\u0038\\u0039\\u003a\\u003b\\u003c\\u003d\\u003e\\u003f", - "\\u0040\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\\u0047\\u0048\\u0049\\u004a\\u004b\\u004c\\u004d\\u004e\\u004f", - "\\u0050\\u0051\\u0052\\u0053\\u0054\\u0055\\u0056\\u0057\\u0058\\u0059\\u005a\\u005b\\u005c\\u005d\\u005e\\u005f", - "\\u0060\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\\u0067\\u0068\\u0069\\u006a\\u006b\\u006c\\u006d\\u006e\\u006f", - "\\u0070\\u0071\\u0072\\u0073\\u0074\\u0075\\u0076\\u0077\\u0078\\u0079\\u007a\\u007b\\u007c\\u007d\\u007e\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u00a3\\u0000\\u0000\\u0192", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u00aa\\u00ba\\u0000\\u0000\\u00ac\\u0000\\u0000\\u0000\\u00ab\\u00bb", - "\\u2591\\u2592\\u2593\\u2502\\u2524\\u2561\\u2562\\u2556\\u2555\\u2563\\u2551\\u2557\\u255d\\u255c\\u255b\\u2510", - "\\u2514\\u2534\\u252c\\u251c\\u2500\\u253c\\u255e\\u255f\\u255a\\u2554\\u2569\\u2566\\u2560\\u2550\\u256c\\u2567", - "\\u2568\\u2564\\u2565\\u2559\\u2558\\u2552\\u2553\\u256b\\u256a\\u2518\\u250c\\u2588\\u2584\\u258c\\u2590\\u2580", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u2205\\u2208\\u0000", - "\\u2261\\u00b1\\u2265\\u2264\\u2320\\u2321\\u00f7\\u2248\\u00b0\\u2219\\u0000\\u221a\\u207f\\u00b2\\u25a0\\u0000" - }) { - i = 0; - char[] chars = ConfigUtils.convertUnicodeStringToChars(line); - for (char c : chars) { - int width = bufferedImage.getWidth(); - int single = width / 16; - int x_final = i * single; - outer: - for (int x = i * single; x < (i+1) * single; x++) { - for (int y = j * single; y < (j+1) * single; y++) { - int rgb = bufferedImage.getRGB(x, y); - int alpha = (rgb >> 24) & 0xff; - if (alpha != 0) { - x_final = x; - continue outer; - } - } - } - int pixels = x_final - i * single + 1; - double times = (double) width / 128; - int charWidth = (int) (pixels / times); - yml.set(ConfigUtils.native2ascii(c), charWidth); - i++; - } - j++; - } - yml.set("\\u0020", 3); - yml.set("\\u0000", 0); - yml.save(asciiCache); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - File asciiSgaCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "ascii_sga.yml"); - if (!asciiSgaCache.exists()) { - File png = new File(plugin.getDataFolder(), "font" + File.separator + "ascii_sga.png"); - if (png.exists()) { - try { - BufferedImage bufferedImage = ImageIO.read(png); - YamlConfiguration yml = new YamlConfiguration(); - int i; - int j = 0; - for (String line : new String[]{ - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\\u0047\\u0048\\u0049\\u004A\\u004B\\u004C\\u004D\\u004E\\u004F", - "\\u0050\\u0051\\u0052\\u0053\\u0054\\u0055\\u0056\\u0057\\u0058\\u0059\\u005A\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0061\\u0062\\u0063\\u0064\\u0065\\u0066\\u0067\\u0068\\u0069\\u006A\\u006B\\u006C\\u006D\\u006E\\u006F", - "\\u0070\\u0071\\u0072\\u0073\\u0074\\u0075\\u0076\\u0077\\u0078\\u0079\\u007A\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000", - "\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" - }) { - i = 0; - char[] chars = ConfigUtils.convertUnicodeStringToChars(line); - for (char c : chars) { - int width = bufferedImage.getWidth(); - int single = width / 16; - int x_final = i * single; - outer: - for (int x = i * single; x < (i+1) * single; x++) { - for (int y = j * single; y < (j+1) * single; y++) { - int rgb = bufferedImage.getRGB(x, y); - int alpha = (rgb >> 24) & 0xff; - if (alpha != 0) { - x_final = x; - continue outer; - } - } - } - int pixels = x_final - i * single + 1; - double times = (double) width / 128; - int charWidth = (int) (pixels / times); - yml.set(ConfigUtils.native2ascii(c), charWidth); - i++; - } - j++; - } - yml.set("\\u0000", 0); - yml.save(asciiSgaCache); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - File asciillagerCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "asciillager.yml"); - if (!asciillagerCache.exists()) { - File png = new File(plugin.getDataFolder(), "font" + File.separator + "asciillager.png"); - if (png.exists()) { - try { - BufferedImage bufferedImage = ImageIO.read(png); - YamlConfiguration yml = new YamlConfiguration(); - int i; - int j = 0; - for (String line : new String[]{ - "\\u0021\\u002C\\u002D\\u002E\\u0030\\u0031\\u0032\\u0033\\u0034\\u0035\\u0036\\u0037\\u0038\\u0039\\u003F\\u0061", - "\\u0062\\u0063\\u0064\\u0065\\u0066\\u0067\\u0068\\u0069\\u006A\\u006B\\u006C\\u006D\\u006E\\u006F\\u0070\\u0071", - "\\u0072\\u0073\\u0074\\u0075\\u0076\\u0077\\u0078\\u0079\\u007A\\u0041\\u0042\\u0043\\u0044\\u0045\\u0046\\u0047", - "\\u0048\\u0049\\u004A\\u004B\\u004C\\u004D\\u004E\\u004F\\u0050\\u0051\\u0052\\u0053\\u0054\\u0055\\u0056\\u0057", - "\\u0058\\u0059\\u005A\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" - }) { - i = 0; - char[] chars = ConfigUtils.convertUnicodeStringToChars(line); - for (char c : chars) { - int width = bufferedImage.getWidth(); - int single = width / 16; - int x_final = i * single; - outer: - for (int x = i * single; x < (i+1) * single; x++) { - for (int y = j * single; y < (j+1) * single; y++) { - int rgb = bufferedImage.getRGB(x, y); - int alpha = (rgb >> 24) & 0xff; - if (alpha != 0) { - x_final = x; - continue outer; - } - } - } - - int pixels = x_final - i * single + 1; - double times = (double) width / 128; - int charWidth = (int) (pixels / times); - yml.set(ConfigUtils.native2ascii(c), charWidth); - i++; - } - j++; - } - yml.set("\\u0000", 0); - yml.save(asciillagerCache); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - File accentedCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "accented.yml"); - if (!accentedCache.exists()) { - File png = new File(plugin.getDataFolder(), "font" + File.separator + "accented.png"); - if (png.exists()) { - try { - BufferedImage bufferedImage = ImageIO.read(png); - YamlConfiguration yml = new YamlConfiguration(); - int i; - int j = 0; - for (String line : new String[]{ - "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf", - "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00e0\\u00e1\\u00e2\\u00e3", - "\\u00e4\\u00e5\\u00e6\\u00e7\\u00ec\\u00ed\\u00ee\\u00ef\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f9\\u00fa", - "\\u00fb\\u00fc\\u00fd\\u00ff\\u0100\\u0101\\u0102\\u0103\\u0104\\u0105\\u0106\\u0107\\u0108\\u0109\\u010a\\u010b", - "\\u010c\\u010d\\u010e\\u010f\\u0110\\u0111\\u0112\\u0113\\u0114\\u0115\\u0116\\u0117\\u0118\\u0119\\u011a\\u011b", - "\\u011c\\u011d\\u1e20\\u1e21\\u011e\\u011f\\u0120\\u0121\\u0122\\u0123\\u0124\\u0125\\u0126\\u0127\\u0128\\u0129", - "\\u012a\\u012b\\u012c\\u012d\\u012e\\u012f\\u0130\\u0131\\u0134\\u0135\\u0136\\u0137\\u0139\\u013a\\u013b\\u013c", - "\\u013d\\u013e\\u013f\\u0140\\u0141\\u0142\\u0143\\u0144\\u0145\\u0146\\u0147\\u0148\\u014a\\u014b\\u014c\\u014d", - "\\u014e\\u014f\\u0150\\u0151\\u0152\\u0153\\u0154\\u0155\\u0156\\u0157\\u0158\\u0159\\u015a\\u015b\\u015c\\u015d", - "\\u015e\\u015f\\u0160\\u0161\\u0162\\u0163\\u0164\\u0165\\u0166\\u0167\\u0168\\u0169\\u016a\\u016b\\u016c\\u016d", - "\\u016e\\u016f\\u0170\\u0171\\u0172\\u0173\\u0174\\u0175\\u0176\\u0177\\u0178\\u0179\\u017a\\u017b\\u017c\\u017d", - "\\u017e\\u01fc\\u01fd\\u01fe\\u01ff\\u0218\\u0219\\u021a\\u021b\\u0386\\u0388\\u0389\\u038a\\u038c\\u038e\\u038f", - "\\u0390\\u03aa\\u03ab\\u03ac\\u03ad\\u03ae\\u03af\\u03b0\\u03ca\\u03cb\\u03cc\\u03cd\\u03ce\\u0400\\u0401\\u0403", - "\\u0407\\u040c\\u040d\\u040e\\u0419\\u0439\\u0450\\u0451\\u0452\\u0453\\u0457\\u045b\\u045c\\u045d\\u045e\\u045f", - "\\u0490\\u0491\\u1e02\\u1e03\\u1e0a\\u1e0b\\u1e1e\\u1e1f\\u1e22\\u1e23\\u1e30\\u1e31\\u1e40\\u1e41\\u1e56\\u1e57", - "\\u1e60\\u1e61\\u1e6a\\u1e6b\\u1e80\\u1e81\\u1e82\\u1e83\\u1e84\\u1e85\\u1ef2\\u1ef3\\u00e8\\u00e9\\u00ea\\u00eb", - "\\u0149\\u01e7\\u01eb\\u040f\\u1e0d\\u1e25\\u1e5b\\u1e6d\\u1e92\\u1eca\\u1ecb\\u1ecc\\u1ecd\\u1ee4\\u1ee5\\u2116", - "\\u0207\\u0194\\u0263\\u0283\\u2047\\u01f1\\u01f2\\u01f3\\u01c4\\u01c5\\u01c6\\u01c7\\u01c8\\u01ca\\u01cb\\u01cc", - "\\u2139\\u1d6b\\ua732\\ua733\\ua734\\ua735\\ua736\\ua737\\ua738\\ua73a\\ua73c\\ua73d\\ua74e\\ua74f\\ua760\\ua761", - "\\ufb04\\ufb06\\u16a1\\u16b5\\u01a0\\u01a1\\u01af\\u01b0\\u1eae\\u1eaf\\u1ea4\\u1ea5\\u1ebe\\u1ebf\\u1ed1\\u1eda", - "\\u1edb\\u1ee8\\u1ee9\\u1eb0\\u1eb1\\u1ea6\\u1ea7\\u1ec0\\u1ec1\\u1ed3\\u1edc\\u1edd\\u1eea\\u1eeb\\u1ea2\\u1ea3", - "\\u1eb2\\u1eb3\\u1ea8\\u1ea9\\u1eba\\u1ebb\\u1ed5\\u1ede\\u1ec2\\u1ec3\\u1ec8\\u1ec9\\u1ece\\u1ecf\\u1ed4\\u1edf", - "\\u1ee6\\u1ee7\\u1eec\\u1eed\\u1ef6\\u1ef7\\u1ea0\\u1ea1\\u1eb6\\u1eb7\\u1eac\\u1ead\\u1eb8\\u1eb9\\u1ec6\\u1ec7", - "\\u1ed8\\u1ed9\\u1ee2\\u1ee3\\u1ef0\\u1ef1\\u1ef4\\u1ef5\\u1ed0\\u0195\\u1eaa\\u1eab\\u1ed6\\u1ed7\\u1eef\\u261e", - "\\u261c\\u262e\\u1eb4\\u1eb5\\u1ebc\\u1ebd\\u1ec4\\u1ec5\\u1ed2\\u1ee0\\u1ee1\\u1eee\\u1ef8\\u1ef9\\u0498\\u0499", - "\\u04a0\\u04a1\\u04aa\\u04ab\\u01f6\\u26a0\\u24ea\\u2460\\u2461\\u2462\\u2463\\u2464\\u2465\\u2466\\u2467\\u2468", - "\\u2469\\u246a\\u246b\\u246c\\u246d\\u246e\\u246f\\u2470\\u2471\\u2472\\u2473\\u24b6\\u24b7\\u24b8\\u24b9\\u24ba", - "\\u24bb\\u24bc\\u24bd\\u24be\\u24bf\\u24c0\\u24c1\\u24c2\\u24c3\\u24c4\\u24c5\\u24c6\\u24c7\\u24c8\\u24c9\\u24ca", - "\\u24cb\\u24cc\\u24cd\\u24ce\\u24cf\\u24d0\\u24d1\\u24d2\\u24d3\\u24d4\\u24d5\\u24d6\\u24d7\\u24d8\\u24d9\\u24da", - "\\u24db\\u24dc\\u24dd\\u24de\\u24df\\u24e0\\u24e1\\u24e2\\u24e3\\u24e4\\u24e5\\u24e6\\u24e7\\u24e8\\u24e9\\u0327", - "\\u0282\\u0290\\u0276\\u01cd\\u01ce\\u01de\\u01df\\u01fa\\u01fb\\u0202\\u0203\\u0226\\u0227\\u01e0\\u01e1\\u1e00", - "\\u1e01\\u0200\\u0201\\u1e06\\u1e07\\u1e04\\u1e05\\u1d6c\\u1e08\\u1e09\\u1e10\\u1e11\\u1e12\\u1e13\\u1e0e\\u1e0f", - "\\u1e0c\\u1d6d\\u1e14\\u1e15\\u1e16\\u1e17\\u1e18\\u1e19\\u1e1c\\u1e1d\\u0228\\u0229\\u1e1a\\u1e1b\\u0204\\u0205", - "\\u0206\\u1d6e\\u01f4\\u01f5\\u01e6\\u1e26\\u1e27\\u1e28\\u1e29\\u1e2a\\u1e2b\\u021e\\u021f\\u1e24\\u1e96\\u1e2e", - "\\u1e2f\\u020a\\u020b\\u01cf\\u01d0\\u0208\\u0209\\u1e2c\\u1e2d\\u01f0\\u0237\\u01e8\\u01e9\\u1e32\\u1e33\\u1e34", - "\\u1e35\\u1e3a\\u1e3b\\u1e3c\\u1e3d\\u1e36\\u1e37\\u1e38\\u1e39\\u2c62\\u1e3e\\u1e3f\\u1e42\\u1e43\\u1d6f\\u1e44", - "\\u1e45\\u1e46\\u1e47\\u1e4a\\u1e4b\\u01f8\\u01f9\\u1e48\\u1e49\\u1d70\\u01ec\\u01ed\\u022c\\u022d\\u1e4c\\u1e4d", - "\\u1e4e\\u1e4f\\u1e50\\u1e51\\u1e52\\u1e53\\u020e\\u020f\\u022a\\u022b\\u01d1\\u01d2\\u022e\\u022f\\u0230\\u0231", - "\\u020c\\u020d\\u01ea\\u1e54\\u1e55\\u1d71\\u0212\\u0213\\u1e58\\u1e59\\u1e5c\\u1e5d\\u1e5e\\u1e5f\\u0210\\u0211", - "\\u1e5a\\u1d73\\u1d72\\u1e64\\u1e65\\u1e66\\u1e67\\u1e62\\u1e63\\u1e68\\u1e69\\u1d74\\u1e70\\u1e71\\u1e6e\\u1e6f", - "\\u1e6c\\u1e97\\u1d75\\u1e72\\u1e73\\u1e76\\u1e77\\u1e78\\u1e79\\u1e7a\\u1e7b\\u01d3\\u01d4\\u01d5\\u01d6\\u01d7", - "\\u01d8\\u01d9\\u01da\\u01db\\u01dc\\u1e74\\u1e75\\u0214\\u0215\\u0216\\u1e7e\\u1e7f\\u1e7c\\u1e7d\\u1e86\\u1e87", - "\\u1e88\\u1e89\\u1e98\\u1e8c\\u1e8d\\u1e8a\\u1e8b\\u0232\\u0233\\u1e8e\\u1e8f\\u1e99\\u1e94\\u1e95\\u1e90\\u1e91", - "\\u1e93\\u1d76\\u01ee\\u01ef\\u1e9b\\ua73e\\ua73f\\u01e2\\u01e3\\u1d7a\\u1efb\\u1d02\\u1d14\\uab63\\u0238\\u02a3", - "\\u02a5\\u02a4\\u02a9\\u02aa\\u02ab\\u0239\\u02a8\\u02a6\\u02a7\\uab50\\uab51\\u20a7\\u1efa\\ufb2e\\ufb2f\\u0180", - "\\u0182\\u0183\\u0187\\u0188\\u018a\\u018b\\u018c\\u0193\\u01e4\\u01e5\\u0197\\u0196\\u0269\\u0198\\u0199\\u019d", - "\\u01a4\\u01a5\\u027d\\u01a6\\u01ac\\u01ad\\u01ab\\u01ae\\u0217\\u01b1\\u019c\\u01b3\\u01b4\\u01b5\\u01b6\\u01a2", - "\\u01a3\\u0222\\u0223\\u02ad\\u02ae\\u02af\\ufb14\\ufb15\\ufb17\\ufb16\\ufb13\\u04d0\\u04d1\\u04d2\\u04d3\\u04f6", - "\\u04f7\\u0494\\u0495\\u04d6\\u04d7\\u04bc\\u04bd\\u04be\\u04bf\\u04da\\u04db\\u04dc\\u04dd\\u04c1\\u04c2\\u04de", - "\\u04df\\u04e2\\u04e3\\u04e4\\u04e5\\u04e6\\u04e7\\u04ea\\u04eb\\u04f0\\u04f1\\u04ee\\u04ef\\u04f2\\u04f3\\u04f4", - "\\u04f5\\u04f8\\u04f9\\u04ec\\u04ed\\u0476\\u0477\\u04d4\\u04fa\\u0502\\ua682\\ua680\\ua688\\u052a\\u052c\\ua684", - "\\u0504\\u0510\\u04e0\\u0506\\u048a\\u04c3\\u049e\\u049c\\u051e\\u051a\\u04c5\\u052e\\u0512\\u0520\\u0508\\u0514", - "\\u04cd\\u04c9\\u0528\\u04c7\\u04a4\\u0522\\u050a\\u04a8\\u0524\\u04a6\\u048e\\u0516\\u050c\\ua690\\u04ac\\ua68a", - "\\ua68c\\u050e\\u04b2\\u04fc\\u04fe\\u0526\\ua694\\u04b4\\ua68e\\u04b6\\u04cb\\u04b8\\ua692\\ua696\\ua686\\u048c", - "\\u0518\\u051c\\u04d5\\u04fb\\u0503\\ua683\\ua681\\ua689\\u052b\\u052d\\ua685\\u0505\\u0511\\u04e1\\u0507\\u048b", - "\\u04c4\\u049f\\u049d\\u051f\\u051b\\u04c6\\u052f\\u0513\\u0521\\u0509\\u0515\\u04ce\\u04ca\\u0529\\u04c8\\u04a5", - "\\u0523\\u050b\\u04a9\\u0525\\u04a7\\u048f\\u0517\\u050d\\ua691\\u04ad\\ua68b\\ua68d\\u050f\\u04b3\\u04fd\\u04ff", - "\\u0527\\ua695\\u04b5\\ua68f\\u04b7\\u04cc\\u04b9\\ua693\\ua697\\ua687\\u048d\\u0519\\u051d\\u1f08\\u1f00\\u1f09", - "\\u1f01\\u1f0a\\u1f02\\u1f0b\\u1f03\\u1f0c\\u1f04\\u1f0d\\u1f05\\u1f0e\\u1f06\\u1f0f\\u1f07\\u1fba\\u1f70\\u1fb8", - "\\u1fb0\\u1fb9\\u1fb1\\u1fbb\\u1f71\\u1f88\\u1f80\\u1f89\\u1f81\\u1f8a\\u1f82\\u1f8b\\u1f83\\u1f8c\\u1f84\\u1f8d", - "\\u1f85\\u1f8e\\u1f86\\u1f8f\\u1f87\\u1fbc\\u1fb4\\u1fb6\\u1fb7\\u1fb2\\u1fb3\\u1f18\\u1f10\\u1f19\\u1f11\\u1f1a", - "\\u1f12\\u1f1b\\u1f13\\u1f1c\\u1f14\\u1f1d\\u1f15\\u1fc8\\u1fc9\\u1f72\\u1f73\\u1f28\\u1f20\\u1fca\\u1f74\\u1f29", - "\\u1f21\\u1f2a\\u1f22\\u1f2b\\u1f23\\u1f2c\\u1f24\\u1f2d\\u1f25\\u1f2e\\u1f26\\u1f2f\\u1f27\\u1f98\\u1f90\\u1f99", - "\\u1f91\\u1f9a\\u1f92\\u1f9b\\u1f93\\u1f9c\\u1f94\\u1f9d\\u1f95\\u1f9e\\u1f96\\u1f9f\\u1f97\\u1fcb\\u1f75\\u1fcc", - "\\u1fc3\\u1fc2\\u1fc4\\u1fc6\\u1fc7\\u1fda\\u1f76\\u1fdb\\u1f77\\u1f38\\u1f30\\u1f39\\u1f31\\u1f3a\\u1f32\\u1f3b", - "\\u1f33\\u1f3c\\u1f34\\u1f3d\\u1f35\\u1f3e\\u1f36\\u1f3f\\u1f37\\u1fd8\\u1fd0\\u1fd9\\u1fd1\\u1fd2\\u1fd3\\u1fd6", - "\\u1fd7\\u1ff8\\u1f78\\u1ff9\\u1f79\\u1f48\\u1f40\\u1f49\\u1f41\\u1f4a\\u1f42\\u1f4b\\u1f43\\u1f4c\\u1f44\\u1f4d", - "\\u1f45\\u1fec\\u1fe4\\u1fe5\\u1fea\\u1f7a\\u1feb\\u1f7b\\u1f59\\u1f51\\u1f5b\\u1f53\\u1f5d\\u1f55\\u1f5f\\u1f57", - "\\u1fe8\\u1fe0\\u1fe9\\u1fe1\\u03d3\\u03d4\\u1fe2\\u1fe3\\u1fe7\\u1f50\\u1f52\\u1f54\\u1fe6\\u1f56\\u1ffa\\u1f7c", - "\\u1ffb\\u1f7d\\u1f68\\u1f60\\u1f69\\u1f61\\u1f6a\\u1f62\\u1f6b\\u1f63\\u1f6c\\u1f64\\u1f6d\\u1f65\\u1f6e\\u1f66", - "\\u1f6f\\u1f67\\u1fa8\\u1fa0\\u1fa9\\u1fa1\\u1faa\\u1fa2\\u1fab\\u1fa3\\u1fac\\u1fa4\\u1fad\\u1fa5\\u1fae\\u1fa6", - "\\u1faf\\u1fa7\\u1ffc\\u1ff3\\u1ff2\\u1ff4\\u1ff6\\u1ff7\\u262f\\u2610\\u2611\\u2612\\u018d\\u01ba\\u2c7e\\u023f", - "\\u2c7f\\u0240\\u1d80\\ua7c4\\ua794\\u1d81\\u1d82\\u1d83\\ua795\\u1d84\\u1d85\\u1d86\\u1d87\\u1d88\\u1d89\\u1d8a", - "\\u1d8b\\u1d8c\\u1d8d\\ua7c6\\u1d8e\\u1d8f\\u1d90\\u1d92\\u1d93\\u1d94\\u1d95\\u1d96\\u1d97\\u1d98\\u1d99\\u1d9a", - "\\u1e9a\\u2152\\u2158\\u20a8\\u20af\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" - }) { - i = 0; - char[] chars = ConfigUtils.convertUnicodeStringToChars(line); - for (char c : chars) { - int width = bufferedImage.getWidth(); - double times = (double) width / 144; - int single = width / 16; - int x_final = i * single; - outer: - for (int x = i * single; x < (i+1) * single; x++) { - for (int y = (int) (j * 12 * times); y < (j+1) * 12 * times; y++) { - int rgb = bufferedImage.getRGB(x, y); - int alpha = (rgb >> 24) & 0xff; - if (alpha != 0) { - x_final = x; - continue outer; - } - } - } - - int pixels = x_final - i * single + 1; - int charWidth = (int) (pixels / times); - yml.set(ConfigUtils.native2ascii(c), charWidth); - i++; - } - j++; - } - yml.set("\\u0000", 0); - yml.save(accentedCache); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - File nonlatinEuropeanCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "nonlatin_european.yml"); - if (!nonlatinEuropeanCache.exists()) { - File png = new File(plugin.getDataFolder(), "font" + File.separator + "nonlatin_european.png"); - if (png.exists()) { - try { - BufferedImage bufferedImage = ImageIO.read(png); - YamlConfiguration yml = new YamlConfiguration(); - int i; - int j = 0; - for (String line : new String[]{ - "\\u00a1\\u2030\\u00ad\\u00b7\\u20b4\\u2260\\u00bf\\u00d7\\u00d8\\u00de\\u04bb\\u00f0\\u00f8\\u00fe\\u0391\\u0392", - "\\u0393\\u0394\\u0395\\u0396\\u0397\\u0398\\u0399\\u039a\\u039b\\u039c\\u039d\\u039e\\u039f\\u03a0\\u03a1\\u03a3", - "\\u03a4\\u03a5\\u03a6\\u03a7\\u03a8\\u03a9\\u03b1\\u03b2\\u03b3\\u03b4\\u03b5\\u03b6\\u03b7\\u03b8\\u03b9\\u03ba", - "\\u03bb\\u03bc\\u03bd\\u03be\\u03bf\\u03c0\\u03c1\\u03c2\\u03c3\\u03c4\\u03c5\\u03c6\\u03c7\\u03c8\\u03c9\\u0402", - "\\u0405\\u0406\\u0408\\u0409\\u040a\\u040b\\u0410\\u0411\\u0412\\u0413\\u0414\\u0415\\u0416\\u0417\\u0418\\u041a", - "\\u041b\\u041c\\u041d\\u041e\\u041f\\u0420\\u0421\\u0422\\u0423\\u0424\\u0425\\u0426\\u0427\\u0428\\u0429\\u042a", - "\\u042b\\u042c\\u042d\\u042e\\u042f\\u0430\\u0431\\u0432\\u0433\\u0434\\u0435\\u0436\\u0437\\u0438\\u043a\\u043b", - "\\u043c\\u043d\\u043e\\u043f\\u0440\\u0441\\u0442\\u0443\\u0444\\u0445\\u0446\\u0447\\u0448\\u0449\\u044a\\u044b", - "\\u044c\\u044d\\u044e\\u044f\\u0454\\u0455\\u0456\\u0458\\u0459\\u045a\\u2013\\u2014\\u2018\\u2019\\u201c\\u201d", - "\\u201e\\u2026\\u204a\\u2190\\u2191\\u2192\\u2193\\u21c4\\uff0b\\u018f\\u0259\\u025b\\u026a\\u04ae\\u04af\\u04e8", - "\\u04e9\\u02bb\\u02cc\\u037e\\u0138\\u1e9e\\u00df\\u20bd\\u20ac\\u0462\\u0463\\u0474\\u0475\\u04c0\\u0472\\u0473", - "\\u2070\\u00b9\\u00b3\\u2074\\u2075\\u2076\\u2077\\u2078\\u2079\\u207a\\u207b\\u207c\\u207d\\u207e\\u2071\\u2122", - "\\u0294\\u0295\\u29c8\\u2694\\u2620\\u049a\\u049b\\u0492\\u0493\\u04b0\\u04b1\\u04d8\\u04d9\\u0496\\u0497\\u04a2", - "\\u04a3\\u04ba\\u05d0\\u05d1\\u05d2\\u05d3\\u05d4\\u05d5\\u05d6\\u05d7\\u05d8\\u05d9\\u05db\\u05dc\\u05de\\u05dd", - "\\u05e0\\u05df\\u05e1\\u05e2\\u05e4\\u05e3\\u05e6\\u05e5\\u05e7\\u05e8\\u00a2\\u00a4\\u00a5\\u00a9\\u00ae\\u00b5", - "\\u00b6\\u00bc\\u00bd\\u00be\\u0387\\u2010\\u201a\\u2020\\u2021\\u2022\\u2031\\u2032\\u2033\\u2034\\u2035\\u2036", - "\\u2037\\u2039\\u203a\\u203b\\u203c\\u203d\\u2042\\u2048\\u2049\\u204b\\u204e\\u204f\\u2051\\u2052\\u2057\\u2117", - "\\u2212\\u2213\\u221e\\u2600\\u2601\\u2608\\u0404\\u2632\\u2635\\u263d\\u2640\\u2642\\u26a5\\u2660\\u2663\\u2665", - "\\u2666\\u2669\\u266a\\u266b\\u266c\\u266d\\u266e\\u266f\\u2680\\u2681\\u2682\\u2683\\u2684\\u2685\\u02ac\\u26a1", - "\\u26cf\\u2714\\u2744\\u274c\\u2764\\u2b50\\u2e18\\u2e2e\\u2e35\\u2e38\\u2e41\\u2e4b\\u295d\\u1614\\u0190\\u07c8", - "\\u03db\\u3125\\u2c6f\\u15fa\\u0186\\u15e1\\u018e\\u2132\\u2141\\ua7b0\\ua780\\u0500\\ua779\\u1d1a\\u27d8\\u2229", - "\\u0245\\u2144\\u0250\\u0254\\u01dd\\u025f\\u1d77\\u0265\\u1d09\\u027e\\u029e\\ua781\\u026f\\u0279\\u0287\\u028c", - "\\u028d\\u028e\\u0531\\u0532\\u0533\\u0534\\u0536\\u0537\\u0539\\u053a\\u053b\\u053c\\u053d\\u053e\\u053f\\u0540", - "\\u0541\\u0542\\u0543\\u0544\\u0545\\u0546\\u0547\\u0548\\u0549\\u054b\\u054c\\u054d\\u054e\\u054f\\u0550\\u0551", - "\\u0552\\u0553\\u0554\\u0555\\u0556\\u0559\\u0561\\u0562\\u0563\\u0564\\u0565\\u0566\\u0567\\u0568\\u0569\\u056a", - "\\u056b\\u056c\\u056d\\u056e\\u056f\\u0570\\u0571\\u0572\\u0573\\u0574\\u0575\\u0576\\u0577\\u0578\\u0579\\u057a", - "\\u057b\\u057c\\u057d\\u057e\\u057f\\u0580\\u0581\\u0582\\u0583\\u0584\\u0585\\u0586\\u0587\\u05e9\\u05ea\\u0538", - "\\u055a\\u055b\\u055c\\u055d\\u055e\\u055f\\u0560\\u0588\\u058f\\u00af\\u017f\\u01b7\\u0292\\u01f7\\u01bf\\u021c", - "\\u021d\\u0224\\u0225\\u02d9\\ua75a\\ua75b\\u2011\\u214b\\u23cf\\u23e9\\u23ea\\u23ed\\u23ee\\u23ef\\u23f4\\u23f5", - "\\u23f6\\u23f7\\u23f8\\u23f9\\u23fa\\u23fb\\u23fc\\u23fd\\u2b58\\u25b2\\u25b6\\u25bc\\u25c0\\u25cf\\u25e6\\u25d8", - "\\u2693\\u26e8\\u0132\\u0133\\u01c9\\ua728\\ua729\\ua739\\ua73b\\ufb00\\ufb01\\ufb02\\ufb03\\ufb05\\ufffd\\u0535", - "\\u054a\\u16a0\\u16a2\\u16a3\\u16a4\\u16a5\\u16a6\\u16a7\\u16a8\\u16a9\\u16aa\\u16ab\\u16ac\\u16ad\\u16ae\\u16af", - "\\u16b0\\u16b1\\u16b2\\u16b3\\u16b4\\u16b6\\u16b7\\u16b8\\u16b9\\u16ba\\u16bb\\u16bc\\u16bd\\u16be\\u16bf\\u16c0", - "\\u16c1\\u16c2\\u16c3\\u16c4\\u16c5\\u16c6\\u16c7\\u16c8\\u16c9\\u16ca\\u16cb\\u16cc\\u16cd\\u16ce\\u16cf\\u16d0", - "\\u16d1\\u16d2\\u16d3\\u16d4\\u16d5\\u16d6\\u16d7\\u16d8\\u16d9\\u16da\\u16db\\u16dc\\u16dd\\u16de\\u16df\\u16e0", - "\\u16e1\\u16e2\\u16e3\\u16e4\\u16e5\\u16e6\\u16e7\\u16e8\\u16e9\\u16ea\\u16eb\\u16ec\\u16ed\\u16ee\\u16ef\\u16f0", - "\\u16f1\\u16f2\\u16f3\\u16f4\\u16f5\\u16f6\\u16f7\\u16f8\\u263a\\u263b\\u00a6\\u2639\\u05da\\u05f3\\u05f4\\u05f0", - "\\u05f1\\u05f2\\u05be\\u05c3\\u05c6\\u00b4\\u00a8\\u1d00\\u0299\\u1d04\\u1d05\\u1d07\\ua730\\u0262\\u029c\\u1d0a", - "\\u1d0b\\u029f\\u1d0d\\u0274\\u1d0f\\u1d18\\ua7af\\u0280\\ua731\\u1d1b\\u1d1c\\u1d20\\u1d21\\u028f\\u1d22\\u00a7", - "\\u0271\\u0273\\u0272\\u0288\\u0256\\u0261\\u02a1\\u0255\\u0291\\u0278\\u029d\\u02a2\\u027b\\u0281\\u0266\\u028b", - "\\u0270\\u026c\\u026e\\u0298\\u01c0\\u01c3\\u01c2\\u01c1\\u0253\\u0257\\u1d91\\u0284\\u0260\\u029b\\u0267\\u026b", - "\\u0268\\u0289\\u028a\\u0258\\u0275\\u0264\\u025c\\u025e\\u0251\\u0252\\u025a\\u025d\\u0181\\u0189\\u0191\\u01a9", - "\\u01b2\\u10a0\\u10a1\\u10a2\\u10a3\\u10a4\\u10a5\\u10a6\\u10a7\\u10a8\\u10a9\\u10aa\\u10ab\\u10ac\\u10ad\\u10ae", - "\\u10af\\u10b0\\u10b1\\u10b2\\u10b3\\u10b4\\u10b5\\u10b6\\u10b7\\u10b8\\u10b9\\u10ba\\u10bb\\u10bc\\u10bd\\u10be", - "\\u10bf\\u10c0\\u10c1\\u10c2\\u10c3\\u10c4\\u10c5\\u10c7\\u10cd\\u10d0\\u10d1\\u10d2\\u10d3\\u10d4\\u10d5\\u10d6", - "\\u10d7\\u10d8\\u10d9\\u10da\\u10db\\u10dc\\u10dd\\u10de\\u10df\\u10e0\\u10e1\\u10e2\\u10e3\\u10e4\\u10e5\\u10e6", - "\\u10e7\\u10e8\\u10e9\\u10ea\\u10eb\\u10ec\\u10ed\\u10ee\\u10ef\\u10f0\\u10f1\\u10f2\\u10f3\\u10f4\\u10f5\\u10f6", - "\\u10f7\\u10f8\\u10f9\\u10fa\\u10fb\\u10fc\\u10fd\\u10fe\\u10ff\\ufb4a\\ufb2b\\ufb4e\\ufb44\\ufb3b\\ufb1f\\ufb1d", - "\\ufb4b\\ufb35\\ufb4c\\ufb31\\ua727\\ua726\\u027a\\u2c71\\u02a0\\u0297\\u0296\\u026d\\u0277\\u027f\\u0285\\u0286", - "\\u0293\\u029a\\u20aa\\u20be\\u058a\\u2d00\\u2d01\\u2d02\\u2d03\\u2d04\\u2d05\\u2d06\\u2d21\\u2d07\\u2d08\\u2d09", - "\\u2d0a\\u2d0b\\u2d0c\\u2d22\\u2d0d\\u2d0e\\u2d0f\\u2d10\\u2d11\\u2d12\\u2d23\\u2d13\\u2d14\\u2d15\\u2d16\\u2d17", - "\\u2d18\\u2d19\\u2d1a\\u2d1b\\u2d1c\\u2d1d\\u2d1e\\u2d24\\u2d1f\\u2d20\\u2d25\\u215b\\u215c\\u215d\\u215e\\u2153", - "\\u2154\\u2709\\u2602\\u2614\\u2604\\u26c4\\u2603\\u231b\\u231a\\u2690\\u270e\\u2763\\u2664\\u2667\\u2661\\u2662", - "\\u26c8\\u2630\\u2631\\u2633\\u2634\\u2636\\u2637\\u2194\\u21d2\\u21cf\\u21d4\\u21f5\\u2200\\u2203\\u2204\\u2209", - "\\u220b\\u220c\\u2282\\u2283\\u2284\\u2285\\u2227\\u2228\\u22bb\\u22bc\\u22bd\\u2225\\u2262\\u22c6\\u2211\\u22a4", - "\\u22a5\\u22a2\\u22a8\\u2254\\u2201\\u2234\\u2235\\u221b\\u221c\\u2202\\u22c3\\u2286\\u2287\\u25a1\\u25b3\\u25b7", - "\\u25bd\\u25c1\\u25c6\\u25c7\\u25cb\\u25ce\\u2606\\u2605\\u2718\\u2080\\u2081\\u2082\\u2083\\u2084\\u2085\\u2086", - "\\u2087\\u2088\\u2089\\u208a\\u208b\\u208c\\u208d\\u208e\\u222b\\u222e\\u221d\\u2300\\u2302\\u2318\\u3012\\u027c", - "\\u0184\\u0185\\u1e9f\\u023d\\u019a\\u019b\\u0220\\u019e\\u019f\\u01a7\\u01a8\\u01aa\\u01b8\\u01b9\\u01bb\\u01bc", - "\\u01bd\\u01be\\u0221\\u0234\\u0235\\u0236\\u023a\\u2c65\\u023b\\u023c\\u0246\\u0247\\u023e\\u2c66\\u0241\\u0242", - "\\u0243\\u0244\\u0248\\u0249\\u024a\\u024b\\u024c\\u024d\\u024e\\u024f\\u1e9c\\u1e9d\\u1efc\\u1efd\\u1efe\\u1eff", - "\\ua7a8\\ua7a9\\ud800\\udf30\\ud800\\udf31\\ud800\\udf32\\ud800\\udf33\\ud800\\udf34\\ud800\\udf35\\ud800\\udf36\\ud800\\udf37\\ud800\\udf38\\ud800\\udf39\\ud800\\udf3a\\ud800\\udf3b\\ud800\\udf3c\\ud800\\udf3d", - "\\ud800\\udf3e\\ud800\\udf3f\\ud800\\udf40\\ud800\\udf41\\ud800\\udf42\\ud800\\udf43\\ud800\\udf44\\ud800\\udf45\\ud800\\udf46\\ud800\\udf47\\ud800\\udf48\\ud800\\udf49\\ud800\\udf4a\\ud83c\\udf27\\ud83d\\udd25\\ud83c\\udf0a", - "\\u2150\\u2151\\u2155\\u2156\\u2157\\u2159\\u215a\\u215f\\u2189\\ud83d\\udde1\\ud83c\\udff9\\ud83e\\ude93\\ud83d\\udd31\\ud83c\\udfa3\\ud83e\\uddea\\u2697", - "\\u2bea\\u2beb\\u2c6d\\ud83d\\udee1\\u2702\\ud83c\\udf56\\ud83e\\udea3\\ud83d\\udd14\\u23f3\\u2691\\u20a0\\u20a1\\u20a2\\u20a3\\u20a4\\u20a5", - "\\u20a6\\u20a9\\u20ab\\u20ad\\u20ae\\u20b0\\u20b1\\u20b2\\u20b3\\u20b5\\u20b6\\u20b7\\u20b8\\u20b9\\u20ba\\u20bb", - "\\u20bc\\u20bf\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000\\u0000" - }) { - i = 0; - char[] chars = ConfigUtils.convertUnicodeStringToChars(line); - for (int z = 0; z < chars.length; z++) { - String unicode; - char c1 = chars[z]; - if (Character.isHighSurrogate(c1)) { - if (z + 1 < chars.length && Character.isLowSurrogate(chars[z+1])) { - char c2 = chars[++z]; - unicode = ConfigUtils.native2ascii(c1) + ConfigUtils.native2ascii(c2); - } else { - throw new IllegalArgumentException("Illegal surrogate character"); - } - } else { - unicode = ConfigUtils.native2ascii(c1); - } - int width = bufferedImage.getWidth(); - int single = width / 16; - int x_final = i * single; - outer: - for (int x = i * single; x < (i+1) * single; x++) { - for (int y = j * single; y < (j+1) * single; y++) { - int rgb = bufferedImage.getRGB(x, y); - int alpha = (rgb >> 24) & 0xff; - if (alpha != 0) { - x_final = x; - continue outer; - } - } - } - int pixels = x_final - i * single + 1; - double times = (double) width / 128; - int charWidth = (int) (pixels / times); - yml.set(unicode, charWidth); - i++; - } - j++; - } - yml.set("\\u0000", 0); - yml.save(nonlatinEuropeanCache); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - File unicodeCache = new File(plugin.getDataFolder(), "font" + File.separator + "cache" + File.separator + "unicode.yml"); - if (!unicodeCache.exists()) { - YamlConfiguration yml = new YamlConfiguration(); - for (int a = 0; a < 256; a++) { - File fontFile = new File(plugin.getDataFolder(), "font" + File.separator + "unicode_page_" + String.format("%02x", a) + ".png"); - if (!fontFile.exists()) continue; - String unicodeStr = String.format("%02x", a); - try { - BufferedImage bufferedImage = ImageIO.read(fontFile); - int width = bufferedImage.getWidth(); - int height = bufferedImage.getHeight(); - int single = width / 16; - int max_y = height / single; - for (int i = 0; i < 16; i++) { - for (int j = 0; j < max_y; j++) { - int x_final = i * single; - outer: - for (int x = i * single; x < (i+1) * single; x++) { - for (int y = j * single; y < (j+1) * single; y++) { - int rgb = bufferedImage.getRGB(x, y); - int alpha = (rgb >> 24) & 0xff; - if (alpha != 0) { - x_final = x; - continue outer; - } - } - } - int charWidth = (int) (((double) (x_final - i * single) / single * 8) + 1); - String unicode = "\\u" + unicodeStr + String.format("%02x", i + j * 16); - yml.set(unicode, charWidth); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - yml.set("\\u0020", 3); - yml.set("\\u0000", 0); - yml.save(unicodeCache); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public boolean isNotReplacedUnifont(int codePoint) { - return !bitMapChars.contains(codePoint); - } - - @Override - public boolean registerFontData(@NotNull Key key, @NotNull FontData fontData) { - if (fontDataMap.containsKey(key)) { - return false; - } - fontDataMap.put(key, fontData); - return true; - } - - @Override - public boolean unregisterFontData(@NotNull Key key) { - return fontDataMap.remove(key) != null; - } - - @Nullable - @Override - public FontData getFontData(@NotNull Key key) { - return fontDataMap.get(key); - } - - @Override - public int getTextWidth(@NotNull String textWithTags) { - return cacheSystem.getWidthFromCache(textWithTags); - } - - public class CacheSystem { - - private final LoadingCache textWidthCache; - - public CacheSystem(int size) { - textWidthCache = CacheBuilder.newBuilder() - .maximumSize(size) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build( - new CacheLoader<>() { - @NotNull - @Override - public Integer load(@NotNull String text) { - return fetchData(text); - } - }); - } - - private int fetchData(String text) { - if (CNConfig.legacyColorSupport) - text = AdventureManagerImpl.getInstance().legacyToMiniMessage(text); - ElementNode node = (ElementNode) MiniMessage.miniMessage().deserializeToTree(text); - ArrayList> list = new ArrayList<>(); - Key defaultFont = Key.of("minecraft", "default"); - nodeToStringInfo(node, list, defaultFont, false); - int totalLength = 0; - for (int i = 0, size = list.size(); i < size; i++) { - Tuple element = list.get(i); - FontData data = getFontData(element.getMid()); - if (data == null) { - LogUtils.warn("Unknown font: " + element.getMid() + " Please register it in font-width-data.yml"); - continue; - } - char[] chars = element.getLeft().toCharArray(); - if (!element.getMid().equals(defaultFont)) { - for (int j = 0; j < chars.length; j++) { - int width; - if (Character.isHighSurrogate(chars[j])) { - width = data.getWidth(Character.toCodePoint(chars[j], chars[++j])); - } else { - width = data.getWidth(chars[j]); - } - totalLength += (width + (element.getRight() ? 2 : 1)) * 2; - } - } else { - for (int j = 0; j < chars.length; j++) { - if (Character.isHighSurrogate(chars[j])) { - int codePoint = Character.toCodePoint(chars[j], chars[++j]); - if (isNotReplacedUnifont(codePoint)) { - totalLength += (data.getWidth(codePoint) * 2 + (element.getRight() ? 3 : 2)); - } else { - totalLength += (element.getRight() ? 2 : 1) * 2; - } - } else { - if (isNotReplacedUnifont(chars[j])) { - totalLength += (data.getWidth(chars[j]) * 2 + (element.getRight() ? 3 : 2)); - } else { - totalLength += (data.getWidth(chars[j]) + (element.getRight() ? 2 : 1)) * 2; - } - } - } - } - } - totalLength /= 2; - return totalLength; - } - - public void nodeToStringInfo(ElementNode node, List> list, Key font, boolean isBold) { - if (node instanceof ValueNode valueNode) { - String text = valueNode.value(); - if (!text.equals("")) - list.add(Tuple.of(text, font, isBold)); - } else if (node instanceof TagNode tagNode) { - if (tagNode.tag() instanceof Inserting inserting) { - Component component = inserting.value(); - if (component.decoration(TextDecoration.BOLD) == TextDecoration.State.TRUE) { - isBold = true; - } else if (component.decoration(TextDecoration.BOLD) == TextDecoration.State.FALSE) { - isBold = false; - } - var key = component.font(); - if (key != null) { - font = net.momirealms.customnameplates.common.Key.of(key.namespace(), key.value()); - } - if (component instanceof TextComponent textComponent) { - String text = textComponent.content(); - if (!text.equals("")) - list.add(Tuple.of(text, font, isBold)); - } - } - } - if (!node.unsafeChildren().isEmpty()) { - for (ElementNode child : node.unsafeChildren()) { - this.nodeToStringInfo(child, list, font, isBold); - } - } - } - - public int getWidthFromCache(String text) { - try { - return textWidthCache.get(text); - } catch (ExecutionException e) { - e.printStackTrace(); - return 0; - } - } - - public void destroy() { - textWidthCache.cleanUp(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/image/ImageManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/image/ImageManagerImpl.java deleted file mode 100644 index c245318..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/image/ImageManagerImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.image; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.ImageManager; -import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; - -public class ImageManagerImpl implements ImageManager { - - private final HashMap imageMap; - private final CustomNameplatesPlugin plugin; - - public ImageManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.imageMap = new HashMap<>(); - } - - public void load() { - if (!CNConfig.imageModule) return; - loadConfigs(); - } - - public void unload() { - this.imageMap.clear(); - } - - public void reload() { - unload(); - loadConfigs(); - } - - @Override - public ConfiguredChar getImage(@NotNull String key) { - return imageMap.get(key); - } - - @Override - public Collection getImages() { - return imageMap.values(); - } - - private void loadConfigs() { - File imgFolder = new File(plugin.getDataFolder(), "contents" + File.separator + "images"); - if (!imgFolder.exists() && imgFolder.mkdirs()) { - saveDefaultImages(); - } - File[] configFiles = imgFolder.listFiles(file -> file.getName().endsWith(".yml")); - if (configFiles == null) return; - Arrays.sort(configFiles, Comparator.comparing(File::getName)); - for (File configFile : configFiles) { - - String key = configFile.getName().substring(0, configFile.getName().length() - 4); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - if (!registerImage(key, - ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("image", key)) - .height(config.getInt("height", 10)) - .width(config.getInt("width", 10)) - .ascent(config.getInt("ascent", 8)) - .build() - )) { - LogUtils.warn("Found duplicated image: " + key); - } - } - } - - @Override - public boolean registerImage(@NotNull String key, @NotNull ConfiguredChar configuredChar) { - if (imageMap.containsKey(key)) return false; - imageMap.put(key, configuredChar); - return true; - } - - @Override - public boolean unregisterImage(@NotNull String key) { - return this.imageMap.remove(key) != null; - } - - 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.saveResource("contents" + File.separator + "images" + File.separator + name + part, false); - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/CoolDownManager.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/CoolDownManager.java deleted file mode 100644 index e32e513..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/CoolDownManager.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.misc; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import org.bukkit.Bukkit; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerQuitEvent; - -import java.util.HashMap; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Manages cooldowns for various actions or events. - * Keeps track of cooldown times for different keys associated with player UUIDs. - */ -public class CoolDownManager implements Listener { - - private final ConcurrentHashMap dataMap; - private final CustomNameplatesPlugin plugin; - - public CoolDownManager(CustomNameplatesPlugin plugin) { - this.dataMap = new ConcurrentHashMap<>(); - this.plugin = plugin; - } - - /** - * Checks if a player is currently in cooldown for a specific key. - * - * @param uuid The UUID of the player. - * @param key The key associated with the cooldown. - * @param time The cooldown time in milliseconds. - * @return True if the player is in cooldown, false otherwise. - */ - public boolean isCoolDown(UUID uuid, String key, long time) { - Data data = this.dataMap.computeIfAbsent(uuid, k -> new Data()); - return data.isCoolDown(key, time); - } - - public void load() { - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - public void unload() { - HandlerList.unregisterAll(this); - } - - public void disable() { - unload(); - this.dataMap.clear(); - } - - /** - * Event handler for when a player quits the game. Removes their cooldown data. - * - * @param event The PlayerQuitEvent triggered when a player quits. - */ - @EventHandler - public void onQuit(PlayerQuitEvent event) { - dataMap.remove(event.getPlayer().getUniqueId()); - } - - public static class Data { - - private final HashMap coolDownMap; - - public Data() { - this.coolDownMap = new HashMap<>(); - } - - /** - * Checks if the player is in cooldown for a specific key. - * - * @param key The key associated with the cooldown. - * @param delay The cooldown delay in milliseconds. - * @return True if the player is in cooldown, false otherwise. - */ - public synchronized boolean isCoolDown(String key, long delay) { - long time = System.currentTimeMillis(); - long last = coolDownMap.getOrDefault(key, time - delay); - if (last + delay > time) { - return true; // Player is in cooldown - } else { - coolDownMap.put(key, time); - return false; // Player is not in cooldown - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/DisplayController.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/DisplayController.java deleted file mode 100644 index b6256d3..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/DisplayController.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.misc; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.manager.RequirementManager; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.api.util.LogUtils; -import org.bukkit.entity.Player; - -public class DisplayController { - - private final Player owner; - private final int checkFrequency; - private final Requirement[] requirements; - private boolean isShown; - private int checkTimer; - private int refreshTimer; - private String latestValue; - private int timeLeft; - private int index; - private final TimeLimitText[] texts; - private boolean metAnyCondition; - - public DisplayController( - Player player, - int checkFrequency, - Requirement[] requirements, - TimeLimitText[] texts - ) { - this.owner = player; - this.checkFrequency = checkFrequency; - this.refreshTimer = 0; - this.requirements = requirements; - this.texts = texts; - this.checkTimer = checkFrequency - 1; - } - - public NextStage stateCheck(Condition condition) { - this.checkTimer++; - if (this.checkTimer % checkFrequency != 0) { - return NextStage.KEEP; - } - boolean canShow = RequirementManager.isRequirementMet(condition, requirements); - if (canShow) { - if (this.isShown) { - return NextStage.KEEP; - } else { - this.isShown = true; - return NextStage.UPDATE; - } - } else { - if (!this.isShown) { - return NextStage.KEEP; - } else { - this.isShown = false; - return NextStage.UPDATE; - } - } - } - - public boolean isShown() { - return isShown; - } - - public boolean updateText(Condition condition) { - if (timeLeft > 0) - timeLeft--; - - // Definitely goto "if" on init - if (timeLeft == 0) { - int triedTimes = 0; - do { - index++; - if (index >= texts.length) { - index = 0; - } - if (triedTimes == texts.length) { - timeLeft = Math.max(checkFrequency, 1); - metAnyCondition = false; - LogUtils.warn("No text is available for player " + owner.getName() + ". Please check your conditions."); - return updateText(""); - } - triedTimes++; - } while (!RequirementManager.isRequirementMet(condition, texts[index].getRequirements())); - - metAnyCondition = true; - timeLeft = texts[index].getDuration(); - refreshTimer = 0; - return updateText(texts[index].getText()); - } - - if (!metAnyCondition || texts[index].getRefreshFrequency() <= 0) { - return false; - } - - refreshTimer++; - if (refreshTimer >= texts[index].getRefreshFrequency()) { - refreshTimer = 0; - return updateText(texts[index].getText()); - } else { - return false; - } - } - - private boolean updateText(String text) { - var newText = PlaceholderAPI.setPlaceholders(owner, text); - if (newText.equals(latestValue)) { - return false; - } - latestValue = newText; - return true; - } - - public void initialize(Condition condition) { - index = texts.length - 1; - checkTimer = 0; - refreshTimer = 0; - timeLeft = 1; - // The text would definitely be refreshed - updateText(condition); - } - - public String getLatestContent() { - return latestValue; - } - - public enum NextStage { - KEEP, - UPDATE - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/PacketManager.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/PacketManager.java deleted file mode 100644 index e252571..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/PacketManager.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.misc; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class PacketManager { - - private static PacketManager instance; - private final ProtocolManager protocolManager; - private final CustomNameplatesPlugin plugin; - - public PacketManager(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.protocolManager = ProtocolLibrary.getProtocolManager(); - instance = this; - } - - public static PacketManager getInstance() { - return instance; - } - - public void send(Player player, PacketContainer packet) { - if (!player.isOnline()) { - return; - } - this.plugin.debug("Packet sent: " + packet.getType() + " to " + player.getName()); - this.protocolManager.sendServerPacket(player, packet); - } - - public void send(Player player, PacketContainer... packets) { - if (plugin.getVersionManager().isVersionNewerThan1_19_R3()) { - List bundle = new ArrayList<>(Arrays.asList(packets)); - PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE); - bundlePacket.getPacketBundles().write(0, bundle); - send(player, bundlePacket); - } else { - for (PacketContainer packet : packets) { - send(player, packet); - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/TimeLimitText.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/TimeLimitText.java deleted file mode 100644 index 6233bc7..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/TimeLimitText.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.misc; - -import net.momirealms.customnameplates.api.requirement.Requirement; - -public class TimeLimitText { - - private int duration; - private int refreshFrequency; - private String text; - private Requirement[] requirements; - - private TimeLimitText() { - this.duration = 100; - this.refreshFrequency = -1; - this.text = ""; - this.requirements = new Requirement[0]; - } - - public TimeLimitText(int duration, int refreshFrequency, String text, Requirement[] requirements) { - this.duration = duration; - this.text = text; - this.refreshFrequency = refreshFrequency; - this.requirements = requirements; - } - - public int getDuration() { - return duration; - } - - public String getText() { - return text; - } - - public Requirement[] getRequirements() { - return requirements; - } - - public int getRefreshFrequency() { - return refreshFrequency; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private final TimeLimitText text; - - public Builder() { - this.text = new TimeLimitText(); - } - - public static Builder of() { - return new Builder(); - } - - public Builder duration(int duration) { - text.duration = duration; - return this; - } - - public Builder refreshFrequency(int refreshFrequency) { - text.refreshFrequency = refreshFrequency; - return this; - } - - public Builder text(String content) { - text.text = content; - return this; - } - - public Builder requirement(Requirement[] requirements) { - text.requirements = requirements; - return this; - } - - public TimeLimitText build() { - return text; - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/VersionManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/VersionManagerImpl.java deleted file mode 100644 index 0c3aa5c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/misc/VersionManagerImpl.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.misc; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.manager.VersionManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.jetbrains.annotations.NotNull; - -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; - -/** - * This class implements the VersionManager interface and is responsible for managing version-related information. - */ -public class VersionManagerImpl implements VersionManager, Listener { - - - private final float mcVersion; - private final CustomNameplatesPluginImpl plugin; - private boolean isFolia; - private boolean isMojmap; - private final String pluginVersion; - private boolean isLatest = true; - - @SuppressWarnings("deprecation") - public VersionManagerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - // Get the server version - - String[] split = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\."); - this.mcVersion = Float.parseFloat(split[1] + "." + (split.length >= 3 ? split[2] : "0")); - - // Get the plugin version - this.pluginVersion = plugin.getDescription().getVersion(); - - // Check if the server is Folia - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - this.isFolia = true; - } catch (ClassNotFoundException ignored) { - this.isFolia = false; - } - - // Check if the server is Mojmap - try { - Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); - this.isMojmap = true; - } catch (ClassNotFoundException ignored) { - } - } - - @Override - public boolean isMojmap() { - return isMojmap; - } - - @Override - public boolean isVersionNewerThan1_19_R2() { - return mcVersion >= 19.3; - } - - @Override - public boolean isVersionNewerThan1_20() { - return mcVersion >= 20; - } - - @Override - public boolean isVersionNewerThan1_20_R2() { - return mcVersion >= 20.2; - } - - @Override - public boolean isVersionNewerThan1_20_5() { - return mcVersion >= 20.5; - } - - @NotNull - @Override - public String getPluginVersion() { - return pluginVersion; - } - - @Override - public boolean isLatest() { - return isLatest; - } - - @Override - public boolean isVersionNewerThan1_19() { - return mcVersion >= 19; - } - - @Override - public boolean isVersionNewerThan1_19_R3() { - return mcVersion >= 19.4; - } - - @Override - public int getPackFormat() { - switch (Bukkit.getServer().getBukkitVersion().split("-")[0]) { - case "1.21" -> { - return 34; - } - case "1.20.5", "1.20.6" -> { - return 32; - } - case "1.20.3", "1.20.4" -> { - return 22; - } - case "1.20.2" -> { - return 18; - } - case "1.20", "1.20.1" -> { - return 15; - } - case "1.19.4" -> { - return 13; - } - case "1.19.3" -> { - return 12; - } - case "1.19", "1.19.1", "1.19.2" -> { - return 9; - } - case "1.18", "1.18.1", "1.18.2" -> { - return 8; - } - default -> { - return 7; - } - } - } - - @Override - public boolean isFolia() { - return isFolia; - } - - // Method to asynchronously check for plugin updates - public CompletableFuture checkUpdate() { - CompletableFuture updateFuture = new CompletableFuture<>(); - plugin.getScheduler().runTaskAsync(() -> { - try { - URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2543&key=version"); - URLConnection conn = url.openConnection(); - conn.setConnectTimeout(3000); - conn.setReadTimeout(3000); - InputStream inputStream = conn.getInputStream(); - String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine(); - String current = getPluginVersion(); - inputStream.close(); - if (!compareVer(newest, current)) { - updateFuture.complete(false); - return; - } - isLatest = false; - updateFuture.complete(true); - } catch (Exception exception) { - LogUtils.warn("Error occurred when checking update."); - updateFuture.completeExceptionally(exception); - } - }); - return updateFuture; - } - - // Method to compare two version strings - // return true when update is available - private 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; - } - - @EventHandler - public void onJoin(PlayerJoinEvent event) { - final Player player = event.getPlayer(); - if (player.isOp()) { - if (!PlaceholderAPI.isRegistered("player")) { - AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, "You haven't installed Player Expansion yet. Click HERE to download. Otherwise you might encounter console spam on first installation."); - } - } - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/NameplateManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/NameplateManagerImpl.java deleted file mode 100644 index 21b839f..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/NameplateManagerImpl.java +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate; - -import com.comphenix.protocol.ProtocolLibrary; -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.OnlineUser; -import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent; -import net.momirealms.customnameplates.api.manager.NameplateManager; -import net.momirealms.customnameplates.api.manager.TeamTagManager; -import net.momirealms.customnameplates.api.manager.UnlimitedTagManager; -import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -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.api.mechanic.tag.unlimited.DynamicTextTagSetting; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.EntityTagPlayer; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.api.util.FontUtils; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener.*; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team.TeamTagManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited.UnlimitedTagManagerImpl; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityPoseChangeEvent; -import org.bukkit.event.entity.EntityPotionEffectEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerToggleSneakEvent; -import org.bukkit.potion.PotionEffectType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class NameplateManagerImpl implements NameplateManager, Listener { - - private final CustomNameplatesPlugin plugin; - /* A map for nameplate configs */ - private final HashMap nameplateMap; - /* A map for cached nameplates */ - private final ConcurrentHashMap cachedNameplateMap; - /* A map to quickly get Entity by its EntityID */ - private final ConcurrentHashMap entityID2EntityMap; - /* A map to store players that have tags */ - private final ConcurrentHashMap nameplatePlayerMap; - private CancellableTask nameplateRefreshTask; - private final TeamTagManagerImpl teamTagManager; - private final UnlimitedTagManagerImpl unlimitedTagManager; - private final EntityDestroyListener entityDestroyListener; - private final EntitySpawnListener entitySpawnListener; - private final EntityMoveListener entityMoveListener; - private final EntityLookListener entityLookListener; - private final EntityTeleportListener entityTeleportListener; - private final EntityAddEffectListener entityAddEffectListener; - private final EntityRemoveEffectListener entityRemoveEffectListener; - - /** - * Configs - */ - private long teamRefreshFrequency; - private String teamPrefix; - private String teamSuffix; - private boolean fixTab; - /* TEAM & UNLIMITED */ - private TagMode tagMode; - private int previewDuration; - private String defaultNameplate; - private String playerName, prefix, suffix; - private long refreshFrequency; - private final List tagSettings; - - public NameplateManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.nameplateMap = new HashMap<>(); - this.tagSettings = new ArrayList<>(); - - this.cachedNameplateMap = new ConcurrentHashMap<>(); - this.entityID2EntityMap = new ConcurrentHashMap<>(); - this.nameplatePlayerMap = new ConcurrentHashMap<>(); - this.teamTagManager = new TeamTagManagerImpl(this); - this.unlimitedTagManager = new UnlimitedTagManagerImpl(this); - - this.entityTeleportListener = new EntityTeleportListener(this); - this.entityDestroyListener = new EntityDestroyListener(this); - this.entitySpawnListener = new EntitySpawnListener(this); - this.entityLookListener = new EntityLookListener(this); - this.entityMoveListener = new EntityMoveListener(this); - this.entityAddEffectListener = new EntityAddEffectListener(this); - this.entityRemoveEffectListener = new EntityRemoveEffectListener(this); - } - - public void reload() { - unload(); - load(); - } - - public void unload() { - if (this.nameplateRefreshTask != null && !this.nameplateRefreshTask.isCancelled()) { - this.nameplateRefreshTask.cancel(); - } - this.nameplateMap.clear(); - this.tagSettings.clear(); - this.teamTagManager.unload(); - this.unlimitedTagManager.unload(); - - HandlerList.unregisterAll(this); - ProtocolLibrary.getProtocolManager().removePacketListener(entityDestroyListener); - ProtocolLibrary.getProtocolManager().removePacketListener(entitySpawnListener); - ProtocolLibrary.getProtocolManager().removePacketListener(entityLookListener); - ProtocolLibrary.getProtocolManager().removePacketListener(entityMoveListener); - ProtocolLibrary.getProtocolManager().removePacketListener(entityTeleportListener); - ProtocolLibrary.getProtocolManager().removePacketListener(entityAddEffectListener); - ProtocolLibrary.getProtocolManager().removePacketListener(entityRemoveEffectListener); - } - - public void disable() { - // to prevent sending channel message on disable - CNConfig.velocitab = false; - unload(); - } - - public void load() { - this.unlimitedTagManager.load(); - Bukkit.getPluginManager().registerEvents(this, plugin); - ProtocolLibrary.getProtocolManager().addPacketListener(entityDestroyListener); - ProtocolLibrary.getProtocolManager().addPacketListener(entitySpawnListener); - ProtocolLibrary.getProtocolManager().addPacketListener(entityLookListener); - ProtocolLibrary.getProtocolManager().addPacketListener(entityMoveListener); - ProtocolLibrary.getProtocolManager().addPacketListener(entityTeleportListener); - ProtocolLibrary.getProtocolManager().addPacketListener(entityAddEffectListener); - ProtocolLibrary.getProtocolManager().addPacketListener(entityRemoveEffectListener); - - if (!CNConfig.nameplateModule) return; - this.loadConfig(); - this.loadNameplates(); - - this.teamTagManager.load(teamRefreshFrequency, fixTab); - this.nameplateRefreshTask = plugin.getScheduler().runTaskAsyncTimer(() -> { - for (OnlineUser user : plugin.getStorageManager().getOnlineUsers()) { - updateCachedNameplate(user.getPlayer(), user.getNameplate()); - } - }, refreshFrequency * 50, refreshFrequency * 50, TimeUnit.MILLISECONDS); - - for (Player online : Bukkit.getOnlinePlayers()) { - createNameTag(online, false); - } - } - - private void loadConfig() { - YamlConfiguration config = plugin.getConfig("configs" + File.separator + "nameplate.yml"); - - tagMode = TagMode.valueOf(config.getString("mode", "TEAM").toUpperCase(Locale.ENGLISH)); - previewDuration = config.getInt("preview-duration", 5); - defaultNameplate = config.getString("default-nameplate", "none"); - - playerName = config.getString("nameplate.player-name", "%player_name%"); - prefix = config.getString("nameplate.prefix", ""); - suffix = config.getString("nameplate.suffix", ""); - refreshFrequency = config.getInt("nameplate.refresh-frequency", 10); - - teamPrefix = config.getString("team.prefix", ""); - teamSuffix = config.getString("team.suffix", ""); - teamRefreshFrequency = config.getInt("team.refresh-frequency", 10); - fixTab = config.getBoolean("team.fix-Tab", false); - - ConfigurationSection unlimitedSection = config.getConfigurationSection("unlimited"); - if (unlimitedSection != null) { - for (Map.Entry entry : unlimitedSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection innerSection) { - tagSettings.add( - DynamicTextTagSetting.builder() - .rawText(innerSection.getString("text", "")) - .refreshFrequency(innerSection.getInt("refresh-frequency", 20)) - .checkFrequency(innerSection.getInt("check-frequency", 20)) - .verticalOffset(innerSection.getDouble("vertical-offset", -1)) - .ownerRequirements(plugin.getRequirementManager().getRequirements(innerSection.getConfigurationSection("owner-conditions"))) - .viewerRequirements(plugin.getRequirementManager().getRequirements(innerSection.getConfigurationSection("viewer-conditions"))) - .build() - ); - } - } - } - } - - private void loadNameplates() { - File npFolder = new File(plugin.getDataFolder(), "contents" + File.separator + "nameplates"); - if (!npFolder.exists() && npFolder.mkdirs()) { - saveDefaultNameplates(); - } - File[] npConfigFiles = npFolder.listFiles(file -> file.getName().endsWith(".yml")); - if (npConfigFiles == null) return; - Arrays.sort(npConfigFiles, Comparator.comparing(File::getName)); - for (File npConfigFile : npConfigFiles) { - - String key = npConfigFile.getName().substring(0, npConfigFile.getName().length() - 4); - if (key.equals("none")) { - LogUtils.severe("You can't use 'none' as nameplate's key"); - continue; - } - - YamlConfiguration config = YamlConfiguration.loadConfiguration(npConfigFile); - if (!registerNameplate( - key, - Nameplate.builder() - .displayName(config.getString("display-name", key)) - .teamColor(TeamColor.valueOf(config.getString("name-color", "none").toUpperCase(Locale.ENGLISH))) - .namePrefix(config.getString("name-prefix", "")) - .nameSuffix(config.getString("name-suffix", "")) - .left(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("left.image", key + "_left")) - .height(config.getInt("left.height", 16)) - .ascent(config.getInt("left.ascent", 12)) - .width(config.getInt("left.width", 16)) - .build()) - .right(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("right.image", key + "_right")) - .height(config.getInt("right.height", 16)) - .ascent(config.getInt("right.ascent", 12)) - .width(config.getInt("right.width", 16)) - .build()) - .middle(ConfiguredChar.builder() - .character(CharacterArranger.getAndIncrease()) - .png(config.getString("middle.image", key + "_middle")) - .height(config.getInt("middle.height", 16)) - .ascent(config.getInt("middle.ascent", 12)) - .width(config.getInt("middle.width", 16)) - .build()) - .build()) - ) { - LogUtils.warn("Found duplicated nameplate: " + key); - } - } - } - - @EventHandler (ignoreCancelled = true, priority = EventPriority.LOW) - public void onDataLoaded(NameplateDataLoadEvent event) { - if (!CNConfig.nameplateModule) return; - - OnlineUser data = event.getOnlineUser(); - String nameplate = data.getNameplateKey(); - if (nameplate.equals("none")) { - nameplate = defaultNameplate; - } - - if (!nameplate.equals("none") && !containsNameplate(nameplate)) { - if (nameplate.equals(defaultNameplate)) { - LogUtils.severe("Default nameplate doesn't exist"); - return; - } - - LogUtils.severe("Nameplate " + nameplate + " doesn't exist. To prevent bugs, player " + event.getUUID() + " 's nameplate data is reset"); - data.setNameplate("none"); - plugin.getStorageManager().saveOnlinePlayerData(event.getUUID()); - return; - } - - Nameplate np = getNameplate(nameplate); - CachedNameplate cachedNameplate = new CachedNameplate(); - putCachedNameplateToMap(event.getUUID(), cachedNameplate); - updateCachedNameplate(cachedNameplate, data.getPlayer(), np); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - var player = event.getPlayer(); - handlePlayerJoin(player); - } - - @Override - public void handlePlayerJoin(Player player) { - this.putEntityIDToMap(player.getEntityId(), player); - - // nameplate module part - if (CNConfig.nameplateModule) { - if (!CNConfig.isOtherTeamPluginHooked() && !isProxyMode()) { - plugin.getTeamManager().createTeam(player); - } - this.createNameTag(player, true); - } - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - var player = event.getPlayer(); - handlePlayerQuit(player); - } - - @Override - public void handlePlayerQuit(Player player) { - this.removeEntityIDFromMap(player.getEntityId()); - this.teamTagManager.handlePlayerQuit(player); - this.unlimitedTagManager.handlePlayerQuit(player); - this.removeNameplatePlayerFromMap(player.getUniqueId()); - - // nameplate module part - if (CNConfig.nameplateModule) { - this.removeCachedNameplateFromMap(player.getUniqueId()); - if (!CNConfig.isOtherTeamPluginHooked() && !isProxyMode()) { - plugin.getTeamManager().removeTeam(player); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onPlayerToggleSneak(PlayerToggleSneakEvent event) { - var player = event.getPlayer(); - unlimitedTagManager.handlePlayerSneak(player, event.isSneaking(), player.isFlying()); - } - - @EventHandler (ignoreCancelled = true) - public void onChangePose(EntityPoseChangeEvent event) { - if (event.getEntity() instanceof Player player) { - unlimitedTagManager.handlePlayerPose(player, event.getPose()); - } - } - - // TODO: Potion effect packet seems to be only sent to the player who has effect, - // it might be something related to metadata. Using BukkitAPI is not the best choice - // since it might conflict with some potion plugins that based on packets - @EventHandler (ignoreCancelled = true) - public void onPotionEffectChange(EntityPotionEffectEvent event) { - if (event.getModifiedType() == PotionEffectType.INVISIBILITY) { - unlimitedTagManager.handlePotionEffect(event.getEntity(), - event.getAction() == EntityPotionEffectEvent.Action.REMOVED || event.getAction() == EntityPotionEffectEvent.Action.CLEARED); - } - } - - @Override - public boolean putEntityIDToMap(int entityID, @NotNull Entity entity) { - if (this.entityID2EntityMap.containsKey(entityID)) - return false; - this.entityID2EntityMap.put(entityID, entity); - return true; - } - - @Override - public Entity removeEntityIDFromMap(int entityID) { - return this.entityID2EntityMap.remove(entityID); - } - - @Override - public boolean putCachedNameplateToMap(@NotNull UUID uuid, @NotNull CachedNameplate nameplate) { - if (this.cachedNameplateMap.containsKey(uuid)) { - return false; - } - this.cachedNameplateMap.put(uuid, nameplate); - return true; - } - - @Override - public CachedNameplate removeCachedNameplateFromMap(@NotNull UUID uuid) { - return this.cachedNameplateMap.remove(uuid); - } - - @Override - public Player getPlayerByEntityID(int id) { - Entity entity = entityID2EntityMap.get(id); - if (entity instanceof Player player) { - return player; - } - return null; - } - - @Override - public Entity getEntityByEntityID(int id) { - return entityID2EntityMap.get(id); - } - - @Override - public boolean updateCachedNameplate(@NotNull Player player) { - Optional onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId()); - if (onlineUser.isEmpty()) return false; - Nameplate nameplate = onlineUser.get().getNameplate(); - return updateCachedNameplate(player, nameplate); - } - - @Override - public boolean updateCachedNameplate(@NotNull Player player, Nameplate nameplate) { - CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId()); - if (cachedNameplate == null) return false; - return updateCachedNameplate(cachedNameplate, player, nameplate); - } - - @Override - public CachedNameplate getCacheNameplate(@NotNull Player player) { - return cachedNameplateMap.get(player.getUniqueId()); - } - - @Override - public NameplatePlayer createNameTag(@NotNull Player player, boolean isJoin) { - if (tagMode == TagMode.TEAM) { - NameplatePlayer nameplatePlayer = this.teamTagManager.createTagForPlayer(player, teamPrefix, teamSuffix); - putNameplatePlayerToMap(nameplatePlayer); - this.unlimitedTagManager.createOrGetTagForPlayer(player, isJoin); - return nameplatePlayer; - } else if (tagMode == TagMode.UNLIMITED) { - EntityTagPlayer tagPlayer = this.unlimitedTagManager.createOrGetTagForPlayer(player, isJoin); - for (DynamicTextTagSetting setting : tagSettings) { - tagPlayer.addTag(setting); - } - putNameplatePlayerToMap(tagPlayer); - return tagPlayer; - } else { - this.unlimitedTagManager.createOrGetTagForPlayer(player, isJoin); - return null; - } - } - - @Override - public void putNameplatePlayerToMap(@NotNull NameplatePlayer player) { - this.nameplatePlayerMap.put(player.getPlayer().getUniqueId(), player); - } - - @Override - public NameplatePlayer getNameplatePlayer(@NotNull UUID uuid) { - return this.nameplatePlayerMap.get(uuid); - } - - @Override - public NameplatePlayer removeNameplatePlayerFromMap(@NotNull UUID uuid) { - return this.nameplatePlayerMap.remove(uuid); - } - - private boolean updateCachedNameplate(CachedNameplate cachedNameplate, Player player, Nameplate nameplate) { - String parsePrefix = PlaceholderAPI.setPlaceholders(player, prefix); - String parseName = PlaceholderAPI.setPlaceholders(player, playerName); - String parseSuffix = PlaceholderAPI.setPlaceholders(player, suffix); - if (nameplate != null) { - int width= FontUtils.getTextWidth( - parsePrefix - + nameplate.getNamePrefix() - + parseName - + nameplate.getNameSuffix() - + parseSuffix - ); - - cachedNameplate.setTeamColor(nameplate.getTeamColor()); - cachedNameplate.setNamePrefix(nameplate.getNamePrefix()); - cachedNameplate.setNameSuffix(nameplate.getNameSuffix()); - cachedNameplate.setTagSuffix(parseSuffix + nameplate.getSuffixWithFont(width)); - cachedNameplate.setTagPrefix(nameplate.getPrefixWithFont(width) + parsePrefix); - } else { - cachedNameplate.setTeamColor(TeamColor.WHITE); - cachedNameplate.setNamePrefix(""); - cachedNameplate.setNameSuffix(""); - cachedNameplate.setTagPrefix(parsePrefix); - cachedNameplate.setTagSuffix(parseSuffix); - } - cachedNameplate.setPlayerName(parseName); - return true; - } - - @NotNull - @Override - public String getNameplatePrefix(@NotNull Player player) { - CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId()); - if (cachedNameplate == null) return ""; - return cachedNameplate.getTagPrefix(); - } - - @NotNull - @Override - public String getNameplateSuffix(@NotNull Player player) { - CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId()); - if (cachedNameplate == null) return ""; - return cachedNameplate.getTagSuffix(); - } - - @NotNull - @Override - public String getFullNameTag(@NotNull Player player) { - CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId()); - if (cachedNameplate == null) { - return player.getName(); - } - - return cachedNameplate.getNamePrefix() - + cachedNameplate.getTagPrefix() - + cachedNameplate.getPlayerName() - + cachedNameplate.getTagSuffix() - + cachedNameplate.getNameSuffix(); - } - - @NotNull - @Override - public List getAvailableNameplates(@NotNull Player player) { - List nameplates = new ArrayList<>(); - for (String nameplate : nameplateMap.keySet()) { - if (hasNameplate(player, nameplate)) { - nameplates.add(nameplate); - } - } - return nameplates; - } - - @NotNull - @Override - public List getAvailableNameplateDisplayNames(@NotNull Player player) { - List nameplates = new ArrayList<>(); - for (Map.Entry entry : nameplateMap.entrySet()) { - if (hasNameplate(player, entry.getKey())) { - nameplates.add(entry.getValue().getDisplayName()); - } - } - return nameplates; - } - - @Override - public boolean equipNameplate(@NotNull Player player, @NotNull String nameplateKey, boolean temp) { - Nameplate nameplate = getNameplate(nameplateKey); - if (nameplate == null && nameplateKey.equals("none")) { - return false; - } - plugin.getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(it -> { - if (it.getNameplateKey().equals(nameplateKey)) { - return; - } - it.setNameplate(nameplateKey); - this.updateCachedNameplate(player, nameplate); - NameplatePlayer nameplatePlayer = getNameplatePlayer(player.getUniqueId()); - if (nameplatePlayer != null) - nameplatePlayer.updateText(); - if (!temp) - plugin.getStorageManager().saveOnlinePlayerData(player.getUniqueId()); - }, () -> { - LogUtils.severe("Player " + player.getName() + "'s data is not loaded."); - }); - return true; - } - - @Override - public void unEquipNameplate(Player player, boolean temp) { - plugin.getStorageManager().getOnlineUser(player.getUniqueId()).ifPresentOrElse(it -> { - if (it.getNameplateKey().equals("none")) { - return; - } - it.setNameplate("none"); - this.updateCachedNameplate(player, getNameplate(getDefaultNameplate())); - NameplatePlayer nameplatePlayer = getNameplatePlayer(player.getUniqueId()); - if (nameplatePlayer != null) - nameplatePlayer.updateText(); - if (!temp) - plugin.getStorageManager().saveOnlinePlayerData(player.getUniqueId()); - }, () -> { - LogUtils.severe("Player " + player.getName() + "'s data is not loaded."); - }); - } - - 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.saveResource("contents" + File.separator + "nameplates" + File.separator + name + part, false); - } - } - } - - @Override - public boolean registerNameplate(@NotNull String key, @NotNull Nameplate nameplate) { - if (this.nameplateMap.containsKey(key)) return false; - this.nameplateMap.put(key, nameplate); - return true; - } - - @Override - public boolean unregisterNameplate(@NotNull String key) { - return this.nameplateMap.remove(key) != null; - } - - @Override - public boolean isProxyMode() { - return CNConfig.velocitab; - } - - @Override - public int getPreviewDuration() { - return previewDuration; - } - - @NotNull - @Override - public TagMode getTagMode() { - return tagMode; - } - - @Nullable - @Override - public Nameplate getNameplate(@NotNull String key) { - return nameplateMap.get(key); - } - - @NotNull - @Override - public Collection getNameplates() { - return nameplateMap.values(); - } - - @NotNull - @Override - public Collection getNameplateKeys() { - return nameplateMap.keySet(); - } - - @Override - public boolean containsNameplate(@NotNull String key) { - return nameplateMap.containsKey(key); - } - - @Override - public boolean hasNameplate(@NotNull Player player, @NotNull String nameplate) { - return player.hasPermission("nameplates.equip." + nameplate); - } - - @NotNull - @Override - public TeamColor getTeamColor(@NotNull Player player) { - CachedNameplate nameplate = getCacheNameplate(player); - return nameplate == null ? TeamColor.WHITE : nameplate.getTeamColor(); - } - - @NotNull - @Override - public String getDefaultNameplate() { - return defaultNameplate; - } - - @NotNull - @Override - public TeamTagManager getTeamTagManager() { - return teamTagManager; - } - - @NotNull - @Override - public UnlimitedTagManager getUnlimitedTagManager() { - return unlimitedTagManager; - } - - public void onEntityMove(Player receiver, int entityID, short x, short y, short z, boolean onGround) { - unlimitedTagManager.handleEntityMovePacket(receiver, entityID, x, y, z, onGround); - } - - public void onEntityDestroy(Player receiver, List list) { - teamTagManager.handleEntityDestroyPacket(receiver, list); - unlimitedTagManager.handleEntityDestroyPacket(receiver, list); - } - - public void onEntitySpawn(Player receiver, int entityID) { - teamTagManager.handleEntitySpawnPacket(receiver, entityID); - unlimitedTagManager.handleEntitySpawnPacket(receiver, entityID); - } - - public void onEntityTeleport(Player receiver, int entityID, double x, double y, double z, boolean onGround) { - unlimitedTagManager.handleEntityTeleportPacket(receiver, entityID, x, y, z, onGround); - } - -// public void onEntityRemoveEffect(Player player, int entityID) { -// unlimitedTagManager.handlePotionEffect(player, entityID,true); -// } -// -// public void onEntityAddEffect(Player player, int entityID) { -// unlimitedTagManager.handlePotionEffect(player, entityID,false); -// } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/DisguiseListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/DisguiseListener.java deleted file mode 100644 index fed36ab..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/DisguiseListener.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import me.libraryaddict.disguise.events.DisguiseEvent; -import me.libraryaddict.disguise.events.UndisguiseEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited.UnlimitedPlayer; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited.UnlimitedTagManagerImpl; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -import java.util.concurrent.TimeUnit; - -public class DisguiseListener implements Listener { - - private final UnlimitedTagManagerImpl unlimitedTagManager; - - public DisguiseListener(UnlimitedTagManagerImpl unlimitedTagManager) { - this.unlimitedTagManager = unlimitedTagManager; - } - - @EventHandler (ignoreCancelled = true) - public void onDisguise(DisguiseEvent event) { - if (this.unlimitedTagManager.getUnlimitedObject(event.getEntity().getUniqueId()) instanceof UnlimitedPlayer player) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(player::updateVisibility, 50, TimeUnit.MILLISECONDS); - } - } - - @EventHandler (ignoreCancelled = true) - public void onUnDisguise(UndisguiseEvent event) { - if (this.unlimitedTagManager.getUnlimitedObject(event.getEntity().getUniqueId()) instanceof UnlimitedPlayer player) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(player::updateVisibility, 50, TimeUnit.MILLISECONDS); - } - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityAddEffectListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityAddEffectListener.java deleted file mode 100644 index a886361..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityAddEffectListener.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntityAddEffectListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntityAddEffectListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.ENTITY_EFFECT); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { -// PacketContainer packet = event.getPacket(); -// System.out.println("添加包"); -// if (packet.getEffectTypes().read(0) == PotionEffectType.INVISIBILITY) { -// System.out.println("是隐身"); -// manager.onEntityAddEffect(event.getPlayer(), packet.getIntegers().read(0)); -// } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityDestroyListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityDestroyListener.java deleted file mode 100644 index 782158d..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityDestroyListener.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntityDestroyListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntityDestroyListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.ENTITY_DESTROY); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { - if (event.isCancelled()) return; - PacketContainer packet = event.getPacket(); - manager.onEntityDestroy(event.getPlayer(), packet.getIntLists().read(0)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityLookListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityLookListener.java deleted file mode 100644 index 488593e..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityLookListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntityLookListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntityLookListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.REL_ENTITY_MOVE_LOOK); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { - if (event.isCancelled()) return; - PacketContainer packet = event.getPacket(); - manager.onEntityMove(event.getPlayer(), - packet.getIntegers().read(0), - packet.getShorts().read(0), - packet.getShorts().read(1), - packet.getShorts().read(2), - packet.getBooleans().read(0) - ); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityMoveListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityMoveListener.java deleted file mode 100644 index 14f108b..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityMoveListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntityMoveListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntityMoveListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.REL_ENTITY_MOVE); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { - if (event.isCancelled()) return; - PacketContainer packet = event.getPacket(); - manager.onEntityMove(event.getPlayer(), - packet.getIntegers().read(0), - packet.getShorts().read(0), - packet.getShorts().read(1), - packet.getShorts().read(2), - packet.getBooleans().read(0) - ); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityRemoveEffectListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityRemoveEffectListener.java deleted file mode 100644 index 8887a88..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityRemoveEffectListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntityRemoveEffectListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntityRemoveEffectListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGHEST, PacketType.Play.Server.REMOVE_ENTITY_EFFECT); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { -// if (event.isCancelled()) return; -// PacketContainer packet = event.getPacket(); -// if (packet.getEffectTypes().read(0) == PotionEffectType.INVISIBILITY) { -// manager.onEntityRemoveEffect(event.getPlayer(), packet.getIntegers().read(0)); -// } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntitySpawnListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntitySpawnListener.java deleted file mode 100644 index 8c04ddb..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntitySpawnListener.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntitySpawnListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntitySpawnListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, CustomNameplatesPlugin.getInstance().getVersionManager().isVersionNewerThan1_20_R2() ? PacketType.Play.Server.SPAWN_ENTITY : PacketType.Play.Server.NAMED_ENTITY_SPAWN); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { - if (event.isCancelled()) return; - manager.onEntitySpawn(event.getPlayer(), event.getPacket().getIntegers().read(0)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityTeleportListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityTeleportListener.java deleted file mode 100644 index 346b58c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/EntityTeleportListener.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl; - -public class EntityTeleportListener extends PacketAdapter { - - private final NameplateManagerImpl manager; - - public EntityTeleportListener(NameplateManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.ENTITY_TELEPORT); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { - if (event.isCancelled()) return; - PacketContainer packet = event.getPacket(); - manager.onEntityTeleport( - event.getPlayer(), - packet.getIntegers().read(0), - packet.getDoubles().read(0), - packet.getDoubles().read(1), - packet.getDoubles().read(2), - packet.getBooleans().read(0) - ); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/MagicCosmeticsListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/MagicCosmeticsListener.java deleted file mode 100644 index 557eae5..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/MagicCosmeticsListener.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.francobm.magicosmetics.api.Cosmetic; -import com.francobm.magicosmetics.api.CosmeticType; -import com.francobm.magicosmetics.cache.PlayerData; -import com.francobm.magicosmetics.cache.cosmetics.Hat; -import com.francobm.magicosmetics.events.*; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited.UnlimitedPlayer; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited.UnlimitedTagManagerImpl; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -import java.util.concurrent.TimeUnit; - -public class MagicCosmeticsListener implements Listener { - - private final UnlimitedTagManagerImpl unlimitedTagManager; - - public MagicCosmeticsListener(UnlimitedTagManagerImpl unlimitedTagManager) { - this.unlimitedTagManager = unlimitedTagManager; - } - - @EventHandler (ignoreCancelled = true) - public void onChangeCos(CosmeticChangeEquipEvent event) { - final Cosmetic cosmetic = event.getNewCosmetic(); - final Player player = event.getPlayer(); - if (cosmetic instanceof Hat hat) { - if (unlimitedTagManager.getUnlimitedObject(player.getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) { - unlimitedPlayer.setHatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); - unlimitedPlayer.teleport(); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onEnterBlackListWorld(PlayerChangeBlacklistEvent event) { - var player = event.getPlayer(); - if (unlimitedTagManager.getUnlimitedObject(player.getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) { - if (event.isInWorldBlacklist()) { - unlimitedPlayer.setHatOffset(0); - } else { - PlayerData playerData = PlayerData.getPlayer(player); - if (playerData != null) { - final Cosmetic cosmetic = playerData.getHat(); - if (cosmetic instanceof Hat hat) { - unlimitedPlayer.setHatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); - unlimitedPlayer.teleport(); - } - } - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onEquip(CosmeticEquipEvent event) { - final Cosmetic cosmetic = event.getCosmetic(); - final Player player = event.getPlayer(); - if (cosmetic instanceof Hat hat) { - if (unlimitedTagManager.getUnlimitedObject(player.getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) { - unlimitedPlayer.setHatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); - unlimitedPlayer.teleport(); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onUnEquip(CosmeticUnEquipEvent event) { - final Player player = event.getPlayer(); - if (event.getCosmeticType() == CosmeticType.HAT) { - if (unlimitedTagManager.getUnlimitedObject(player.getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) { - unlimitedPlayer.setHatOffset(0); - unlimitedPlayer.teleport(); - } - } - } - - @EventHandler (ignoreCancelled = true) - public void onDataLoaded(PlayerDataLoadEvent event) { - for (Cosmetic cosmetic : event.getEquippedCosmetics()) { - if (cosmetic instanceof Hat hat) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> { - if (unlimitedTagManager.getUnlimitedObject(event.getPlayerData().getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) { - unlimitedPlayer.setHatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); - unlimitedPlayer.teleport(); - } - }, 100, TimeUnit.MILLISECONDS); - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/PlayerInfoListener.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/PlayerInfoListener.java deleted file mode 100644 index ec894fb..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/listener/PlayerInfoListener.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.wrappers.EnumWrappers; -import com.comphenix.protocol.wrappers.PlayerInfoData; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import com.comphenix.protocol.wrappers.WrappedGameProfile; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team.TeamTagManagerImpl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -public class PlayerInfoListener extends PacketAdapter { - - private final TeamTagManagerImpl manager; - - public PlayerInfoListener(TeamTagManagerImpl manager) { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO); - this.manager = manager; - } - - @Override - public void onPacketSending(PacketEvent event) { - if (event.isCancelled()) return; - PacketContainer packet = event.getPacket(); - Set actions = packet.getPlayerInfoActions().read(0); - if (!actions.contains(EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME)) - return; - List list = (List) packet.getModifier().read(1); - List newList = new ArrayList<>(); - int size = list.size(); - for (int i = 0; i < size; i++) { - Object dataHandle = list.get(i); - if (dataHandle == null) { - continue; - } - PlayerInfoData data = PlayerInfoData.getConverter().getSpecific(dataHandle); - WrappedGameProfile profile = data.getProfile(); - PlayerInfoData newData = new PlayerInfoData( - profile, - data.getLatency(), - data.getGameMode(), - WrappedChatComponent.fromJson(String.format("{\"text\":\"%s\"}", profile.getName())) - ); - newList.add(PlayerInfoData.getConverter().getGeneric(newData)); - } - packet.getModifier().write(1, newList); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamPlayer.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamPlayer.java deleted file mode 100644 index d0cef78..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamPlayer.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.TeamTagManager; -import net.momirealms.customnameplates.api.mechanic.misc.ViewerText; -import net.momirealms.customnameplates.api.mechanic.tag.team.TeamTagPlayer; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.common.team.TeamTagVisibility; -import org.bukkit.entity.Player; - -import java.util.Vector; - -public class TeamPlayer implements TeamTagPlayer { - - private final TeamTagManager manager; - private final Player owner; - private ViewerText prefix; - private ViewerText suffix; - private final Vector nearbyPlayers; - private boolean isPreviewing; - private final TeamPreviewSimpleEntity previewEntity; - - public TeamPlayer(TeamTagManager manager, Player owner, String prefix, String suffix) { - this.manager = manager; - this.owner = owner; - this.prefix = new ViewerText(owner, prefix); - this.suffix = new ViewerText(owner, suffix); - this.nearbyPlayers = new Vector<>(); - this.prefix.updateForOwner(); - this.suffix.updateForOwner(); - this.previewEntity = new TeamPreviewSimpleEntity(this); - } - - @Override - public void setPrefix(String prefix) { - this.prefix = new ViewerText(owner, prefix); - } - - @Override - public void setSuffix(String suffix) { - this.suffix = new ViewerText(owner, suffix); - } - - @Override - public ViewerText getPrefix() { - return prefix; - } - - @Override - public ViewerText getSuffix() { - return suffix; - } - - public void updateForNearbyPlayers(boolean force) { - this.prefix.updateForOwner(); - this.suffix.updateForOwner(); - for (Player viewer : nearbyPlayers) { - updateForOne(viewer, force); - } - if (isPreviewing) { - previewEntity.update(); - } - } - - public void removeNearbyPlayer(Player player) { - if (!nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.remove(player); - removeForOne(player); - prefix.removeViewer(player); - suffix.removeViewer(player); - } - - public void addNearbyPlayer(Player player) { - if (nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.add(player); - updateForOne(player, false); - } - - @Override - public void destroy() { - manager.removeTeamPlayerFromMap(owner.getUniqueId()); - for (Player viewer : nearbyPlayers) { - removeForOne(viewer); - } - nearbyPlayers.clear(); - previewEntity.destroy(); - } - - private void updateForOne(Player viewer, boolean force) { - try { - if ((prefix.updateForViewer(viewer) | suffix.updateForViewer(viewer)) || force) { - CustomNameplatesPlugin.get().getTeamManager().updateTeam( - owner, - viewer, - prefix.getLatestValue(viewer), - suffix.getLatestValue(viewer), - CustomNameplatesPlugin.get().getNameplateManager().getTeamColor(owner), - TeamTagVisibility.ALWAYS - ); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void removeForOne(Player viewer) { - CustomNameplatesPlugin.get().getTeamManager().updateTeam( - owner, - viewer, - "", - "", - TeamColor.WHITE, - TeamTagVisibility.ALWAYS - ); - } - - @Override - public void setPreview(boolean preview) { - if (isPreviewing == preview) { - return; - } - isPreviewing = preview; - if (isPreviewing) { - this.previewEntity.spawn(); - } else { - this.previewEntity.destroy(); - } - } - - @Override - public boolean isPreviewing() { - return isPreviewing; - } - - @Override - public Player getPlayer() { - return owner; - } - - @Override - public void updateText() { - updateForNearbyPlayers(false); - if (isPreviewing) { - previewEntity.update(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamPreviewSimpleEntity.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamPreviewSimpleEntity.java deleted file mode 100644 index b5711d9..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamPreviewSimpleEntity.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager; -import net.momirealms.customnameplates.paper.util.FakeEntityUtils; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -public class TeamPreviewSimpleEntity { - - private final TeamPlayer teamPlayer; - private final UUID uuid = UUID.randomUUID(); - private final int entityId; - private CancellableTask tpTask; - - public TeamPreviewSimpleEntity(TeamPlayer teamPlayer) { - this.teamPlayer = teamPlayer; - this.entityId = FakeEntityUtils.getAndIncrease(); - } - - private String getTagString() { - TeamColor teamColor = CustomNameplatesPlugin.get().getNameplateManager().getTeamColor(teamPlayer.getPlayer()); - if (teamColor == TeamColor.NONE || teamColor == TeamColor.CUSTOM) - teamColor = TeamColor.WHITE; - return teamPlayer.getPrefix().getLatestValue(teamPlayer.getPlayer()) + - "<" + teamColor.name() + ">" + - teamPlayer.getPlayer().getName() + - "" + - teamPlayer.getSuffix().getLatestValue(teamPlayer.getPlayer()); - } - - public void spawn() { - teamPlayer.getSuffix().updateForViewer(teamPlayer.getPlayer()); - teamPlayer.getPrefix().updateForViewer(teamPlayer.getPlayer()); - PacketManager.getInstance().send(teamPlayer.getPlayer(), getSpawnPackets(getTagString())); - this.tpTask = CustomNameplatesPlugin.get().getScheduler().runTaskAsyncTimer( - () -> PacketManager.getInstance().send(teamPlayer.getPlayer(), getTeleportPacket()), - 50, - 50, - TimeUnit.MILLISECONDS - ); - } - - public void destroy() { - if (this.tpTask != null && !this.tpTask.isCancelled()) { - this.tpTask.cancel(); - } - PacketContainer destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); - destroyPacket.getIntLists().write(0, List.of(entityId)); - PacketManager.getInstance().send(teamPlayer.getPlayer(), destroyPacket); - teamPlayer.getSuffix().removeViewer(teamPlayer.getPlayer()); - teamPlayer.getPrefix().removeViewer(teamPlayer.getPlayer()); - } - - public void update() { - if (teamPlayer.getPrefix().updateForViewer(teamPlayer.getPlayer()) | teamPlayer.getSuffix().updateForViewer(teamPlayer.getPlayer())) - PacketManager.getInstance().send(teamPlayer.getPlayer(), FakeEntityUtils.getMetaPacket(entityId, getTagString(), false)); - } - - public int getEntityId() { - return entityId; - } - - public UUID getUUID() { - return uuid; - } - - public PacketContainer getTeleportPacket() { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getIntegers().write(0, entityId); - Location location = teamPlayer.getPlayer().getLocation(); - packet.getDoubles().write(0, location.getX()); - packet.getDoubles().write(1, location.getY() + 0.8); - packet.getDoubles().write(2, location.getZ()); - return packet; - } - - private PacketContainer[] getSpawnPackets(String text) { - PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); - entityPacket.getModifier().write(0, entityId); - entityPacket.getModifier().write(1, uuid); - entityPacket.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND); - Location location = teamPlayer.getPlayer().getLocation(); - entityPacket.getDoubles().write(0, location.getX()); - entityPacket.getDoubles().write(1, location.getY() + 0.8); - entityPacket.getDoubles().write(2, location.getZ()); - PacketContainer metaPacket = FakeEntityUtils.getMetaPacket(entityId, text, false); - return new PacketContainer[] {entityPacket, metaPacket}; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamTagManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamTagManagerImpl.java deleted file mode 100644 index 9954276..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/team/TeamTagManagerImpl.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team; - -import com.comphenix.protocol.ProtocolLibrary; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.NameplateManager; -import net.momirealms.customnameplates.api.manager.TeamTagManager; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.api.util.LocationUtils; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener.PlayerInfoListener; -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class TeamTagManagerImpl implements TeamTagManager { - - private final NameplateManager manager; - private final ConcurrentHashMap teamPlayerMap; - private CancellableTask refreshTask; - private final PlayerInfoListener tabListener; - - public TeamTagManagerImpl(NameplateManager manager) { - this.manager = manager; - this.teamPlayerMap = new ConcurrentHashMap<>(); - this.tabListener = new PlayerInfoListener(this); - } - - public void load(long refreshFrequency, boolean fixTab) { - this.refreshTask = CustomNameplatesPlugin.get().getScheduler().runTaskAsyncTimer( - () -> { - try { - for (TeamPlayer teamPlayer : teamPlayerMap.values()) { - teamPlayer.updateForNearbyPlayers(false); - } - } catch (Exception e) { - LogUtils.severe( - "Error occurred when updating team tags. " + - "This might not be a bug in CustomNameplates. Please report " + - "to the Plugin on the top of the following " + - "stack trace." - ); - e.printStackTrace(); - } - }, - refreshFrequency * 50L, - refreshFrequency * 50L, - TimeUnit.MILLISECONDS - ); - if (fixTab) { - ProtocolLibrary.getProtocolManager().addPacketListener(tabListener); - } - } - - public void unload() { - if (this.refreshTask != null && !this.refreshTask.isCancelled()) { - this.refreshTask.cancel(); - } - for (TeamPlayer entry : teamPlayerMap.values()) { - entry.destroy(); - } - ProtocolLibrary.getProtocolManager().removePacketListener(tabListener); - } - - @NotNull - @Override - @SuppressWarnings("DuplicatedCode") - public TeamPlayer createTagForPlayer(Player player, String prefix, String suffix) { - if (this.teamPlayerMap.containsKey(player.getUniqueId())) { - return this.teamPlayerMap.get(player.getUniqueId()); - } - - var teamPlayer = new TeamPlayer(this, player, prefix, suffix); - this.teamPlayerMap.put( - player.getUniqueId(), - teamPlayer - ); - for (Player online : Bukkit.getOnlinePlayers()) { - if ( online == player - || !online.canSee(player) - || LocationUtils.getDistance(online, player) > 48 - || online.getWorld() != player.getWorld() - || online.isDead() - ) continue; - teamPlayer.addNearbyPlayer(online); - } - return teamPlayer; - } - - @Override - public TeamPlayer removeTeamPlayerFromMap(UUID uuid) { - return teamPlayerMap.remove(uuid); - } - - @Nullable - public TeamPlayer getTeamPlayer(UUID uuid) { - return teamPlayerMap.get(uuid); - } - - public void handleEntitySpawnPacket(Player receiver, int entityId) { - Entity spawned = manager.getEntityByEntityID(entityId); - if (spawned == null) return; - TeamPlayer teamPlayer = getTeamPlayer(spawned.getUniqueId()); - if (teamPlayer == null) return; - teamPlayer.addNearbyPlayer(receiver); - } - - public void handleEntityDestroyPacket(Player receiver, List list) { - for (int id : list) { - handleSingleEntityDestroy(receiver, id); - } - } - - public void handlePlayerQuit(Player quit) { - teamPlayerMap.remove(quit.getUniqueId()); - for (TeamPlayer teamPlayer : teamPlayerMap.values()) { - teamPlayer.removeNearbyPlayer(quit); - } - } - - private void handleSingleEntityDestroy(Player receiver, int entityID) { - Entity deSpawned = manager.getEntityByEntityID(entityID); - if (deSpawned == null) return; - TeamPlayer teamPlayer = getTeamPlayer(deSpawned.getUniqueId()); - if (teamPlayer == null) return; - teamPlayer.removeNearbyPlayer(receiver); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/DynamicTextEntityImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/DynamicTextEntityImpl.java deleted file mode 100644 index 42ea91e..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/DynamicTextEntityImpl.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.RequirementManager; -import net.momirealms.customnameplates.api.mechanic.misc.ViewerText; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.DynamicTextEntity; -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.DisguiseUtils; -import net.momirealms.customnameplates.paper.util.FakeEntityUtils; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; - -import java.util.List; -import java.util.UUID; -import java.util.Vector; -import java.util.concurrent.TimeUnit; - -public class DynamicTextEntityImpl implements DynamicTextEntity { - - private final UUID uuid = UUID.randomUUID(); - private final UnlimitedPlayer owner; - private double yOffset; - private final int entityId; - private final ViewerText viewerText; - private final Requirement[] ownerRequirements; - private final Requirement[] viewerRequirements; - private final Vector viewers; - private Player[] viewerArray; - private final int refreshFrequency; - private final int checkFrequency; - private int checkTimer; - private int refreshTimer; - private boolean ownerCanShow; - private final PacketContainer destroyPacket; - - public DynamicTextEntityImpl( - UnlimitedPlayer unlimitedPlayer, - ViewerText text, - int refreshFrequency, - int checkFrequency, - double yOffset, - Requirement[] ownerRequirements, - Requirement[] viewerRequirements - ) { - this.entityId = FakeEntityUtils.getAndIncrease(); - this.owner = unlimitedPlayer; - this.yOffset = yOffset; - this.viewerText = text; - this.ownerRequirements = ownerRequirements; - this.viewerRequirements = viewerRequirements; - this.checkFrequency = checkFrequency; - this.refreshFrequency = refreshFrequency; - this.ownerCanShow = RequirementManager.isRequirementMet(new Condition(owner.getPlayer()), ownerRequirements); - this.destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); - this.destroyPacket.getIntLists().write(0, List.of(entityId)); - this.viewers = new Vector<>(); - this.viewersToArray(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DynamicTextEntityImpl that = (DynamicTextEntityImpl) o; - return uuid.equals(that.uuid); - } - - @Override - public int hashCode() { - return uuid.hashCode(); - } - - @Override - public boolean canShow() { - return ownerCanShow; - } - - @Override - public boolean isShownTo(Player viewer) { - return viewers.contains(viewer); - } - - @Override - public boolean canSee(Player viewer) { - Condition condition = new Condition(viewer); - return RequirementManager.isRequirementMet(condition, viewerRequirements); - } - - @Override - public void timer() { - if (owner.isPreviewing()) { - Location location = owner.getPlayer().getLocation(); - teleport(owner.getPlayer(), location.getX(), location.getY(), location.getZ(), false); - } - - checkTimer++; - if (checkTimer >= checkFrequency) { - checkTimer = 0; - updateVisibility(); - } - - refreshTimer++; - if (refreshTimer >= refreshFrequency) { - refreshTimer = 0; - viewerText.updateForOwner(); - updateText(); - } - } - - @Override - public void updateVisibility() { - if (!RequirementManager.isRequirementMet(new Condition(owner.getPlayer()), ownerRequirements)) { - ownerCanShow = false; - for (Player all : owner.getNearbyPlayers()) { - removePlayerFromViewers(all); - } - } else { - ownerCanShow = true; - for (Player all : owner.getNearbyPlayers()) { - if (canSee(all)) { - addPlayerToViewers(all); - } else { - removePlayerFromViewers(all); - } - } - } - } - - @Override - public void removePlayerFromViewers(Player player) { - if (!viewers.contains(player)) { - return; - } - this.viewers.remove(player); - viewersToArray(); - destroy(player); - } - - @Override - public void addPlayerToViewers(Player player) { - if (viewers.contains(player)) { - return; - } - this.viewers.add(player); - viewersToArray(); - spawn(player, owner.getPlayer().getPose()); - } - - @Override - public double getOffset() { - return yOffset; - } - - @Override - public void setOffset(double offset) { - if (yOffset == offset) return; - yOffset = offset; - for (Player all : viewerArray) { - PacketManager.getInstance().send(all, getTeleportPacket(0)); - } - } - - @Override - public ViewerText getViewerText() { - return viewerText; - } - - @Override - public void spawn(Player viewer, Pose pose) { - if (viewerText.updateForViewer(viewer)) { - PacketManager.getInstance().send(viewer, getSpawnPackets(viewerText.getLatestValue(viewer), pose)); - } - } - - @Override - public void spawn(Pose pose) { - for (Player all : viewerArray) { - spawn(all, pose); - } - } - - @Override - public void destroy() { - for (Player all : viewerArray) { - PacketManager.getInstance().send(all, destroyPacket); - } - viewerText.clear(); - viewers.clear(); - viewersToArray(); - } - - @Override - public void destroy(Player viewer) { - PacketManager.getInstance().send(viewer, destroyPacket); - viewerText.removeViewer(viewer); - } - - @Override - public void teleport(double x, double y, double z, boolean onGround) { - PacketContainer packet = getTeleportPacket(x, y, z, onGround); - for (Player all : viewerArray) { - PacketManager.getInstance().send(all, packet); - } - } - - @Override - public void teleport() { - PacketContainer packet = getTeleportPacket(getPlayerHeight()); - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, packet); - } - } - - @Override - public void teleport(Player viewer, double x, double y, double z, boolean onGround) { - if (viewers.contains(viewer)) { - PacketManager.getInstance().send(viewer, getTeleportPacket(x, y, z, onGround)); - } - } - - @Override - public void setSneak(boolean isSneaking, boolean onGround) { - if (!onGround) { - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, viewerText.getLatestValue(viewer), isSneaking)); - } - } - } - - @Override - public int getEntityId() { - return entityId; - } - - @Override - public void move(short x, short y, short z, boolean onGround) { - PacketContainer packet = getMovePacket(x, y, z, onGround); - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, packet); - } - } - - @Override - public void move(Player viewer, short x, short y, short z, boolean onGround) { - if (viewers.contains(viewer)) { - PacketContainer packet = getMovePacket(x, y, z, onGround); - PacketManager.getInstance().send(viewer, packet); - } - } - - @Override - public void respawn(Player viewer, Pose pose) { - destroy(viewer); - spawn(viewer, pose); - } - - @Override - public void respawn(Pose pose) { - for (Player viewer : viewerArray) { - respawn(viewer, pose); - } - } - - @Override - public void updateText() { - for (Player viewer : viewerArray) { - updateText(viewer); - } - } - - @Override - public void updateText(Player viewer) { - if (viewerText.updateForViewer(viewer)) { - PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, viewerText.getLatestValue(viewer), owner.getPlayer().isSneaking())); - } - } - - @Override - public UUID getUUID() { - return uuid; - } - - @Override - public void handlePose(Pose previous, Pose pose) { - // Add delay to prevent the tag from appearing earlier - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> { - for (Player viewer : viewerArray) { - respawn(viewer, pose); - } - }, 20, TimeUnit.MILLISECONDS); - } - - public PacketContainer getMovePacket(short x, short y, short z, boolean onGround) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.REL_ENTITY_MOVE); - packet.getIntegers().write(0, entityId); - packet.getShorts().write(0, x); - packet.getShorts().write(1, y); - packet.getShorts().write(2, z); - packet.getBooleans().write(0, onGround); - return packet; - } - - public PacketContainer getTeleportPacket(double x, double y, double z, boolean onGround) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getIntegers().write(0, entityId); - packet.getDoubles().write(0, x); - packet.getDoubles().write(1, y + owner.getHatOffset() + getPlayerHeight() + yOffset); - packet.getDoubles().write(2, z); - packet.getBooleans().write(0, onGround); - return packet; - } - - public PacketContainer getTeleportPacket(double correction) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getIntegers().write(0, entityId); - Location location = getEntityLocation(correction); - packet.getDoubles().write(0, location.getX()); - packet.getDoubles().write(1, location.getY()); - packet.getDoubles().write(2, location.getZ()); - return packet; - } - - private Location getEntityLocation(double playerHeight) { - var player = owner.getPlayer(); - double x = player.getLocation().getX(); - double y = player.getLocation().getY(); - double z = player.getLocation().getZ(); - y += yOffset; - y += owner.getHatOffset(); - y += playerHeight; - return new Location(null, x, y, z); - } - - private PacketContainer[] getSpawnPackets(String text, Pose pose) { - PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); - entityPacket.getModifier().write(0, entityId); - entityPacket.getModifier().write(1, uuid); - entityPacket.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND); - Location location = getEntityLocation(getPlayerHeight()); - entityPacket.getDoubles().write(0, location.getX()); - entityPacket.getDoubles().write(1, location.getY()); - entityPacket.getDoubles().write(2, location.getZ()); - PacketContainer metaPacket = FakeEntityUtils.getMetaPacket(entityId, text, pose == Pose.SNEAKING); - return new PacketContainer[] {entityPacket, metaPacket}; - } - - private double getPlayerHeight() { - if (CNConfig.hasLibsDisguise && DisguiseUtils.isDisguised(owner.getPlayer()) && DisguiseUtils.getDisguisedType(owner.getPlayer()) != EntityType.PLAYER) { - return DisguiseUtils.getDisguisedHeight(owner.getPlayer()); - } - return owner.getPlayer().getHeight(); - } - - private void viewersToArray() { - viewerArray = viewers.toArray(new Player[0]); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/StaticTextEntityImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/StaticTextEntityImpl.java deleted file mode 100644 index e6dc34e..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/StaticTextEntityImpl.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.NearbyRule; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextEntity; -import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.DisguiseUtils; -import net.momirealms.customnameplates.paper.util.FakeEntityUtils; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; - -import java.util.List; -import java.util.UUID; -import java.util.Vector; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class StaticTextEntityImpl implements StaticTextEntity { - - private final UUID uuid = UUID.randomUUID(); - private final UnlimitedEntity owner; - private double yOffset; - private final int entityId; - private final Vector viewers; - private Player[] viewerArray; - private final ConcurrentHashMap textCache; - private final NearbyRule comeRule; - private final NearbyRule leaveRule; - private String defaultText; - private final String id; - private final PacketContainer destroyPacket; - - public StaticTextEntityImpl ( - UnlimitedEntity owner, - double yOffset, - NearbyRule comeRule, - NearbyRule leaveRule, - String defaultText, - String id - ) { - this.entityId = FakeEntityUtils.getAndIncrease(); - this.owner = owner; - this.yOffset = yOffset; - this.viewers = new Vector<>(); - this.textCache = new ConcurrentHashMap<>(); - this.comeRule = comeRule; - this.leaveRule = leaveRule; - this.defaultText = defaultText; - this.id = id; - this.destroyPacket = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); - this.destroyPacket.getIntLists().write(0, List.of(entityId)); - this.viewersToArray(); - } - - @Override - public String getID() { - return id; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StaticTextEntityImpl that = (StaticTextEntityImpl) o; - return uuid.equals(that.uuid); - } - - @Override - public int hashCode() { - return uuid.hashCode(); - } - - @Override - public boolean isShownTo(Player viewer) { - return viewers.contains(viewer); - } - - @Override - public NearbyRule getComeRule() { - return comeRule; - } - - @Override - public NearbyRule getLeaveRule() { - return leaveRule; - } - - @Override - public void removePlayerFromViewers(Player player) { - if (!viewers.contains(player)) { - return; - } - this.viewers.remove(player); - viewersToArray(); - destroy(player); - } - - @Override - public void addPlayerToViewers(Player player) { - if (viewers.contains(player)) { - return; - } - this.viewers.add(player); - viewersToArray(); - spawn(player, owner.getEntity().getPose()); - } - - @Override - public double getOffset() { - return yOffset; - } - - @Override - public void setOffset(double offset) { - if (yOffset == offset) return; - yOffset = offset; - for (Player all : viewerArray) { - PacketManager.getInstance().send(all, getTeleportPacket()); - } - } - - @Override - public void setText(String text) { - if (text.equals(defaultText)) return; - this.defaultText = text; - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, getText(viewer), owner.getEntity().isSneaking())); - } - } - - @Override - public String getText(Player viewer) { - return textCache.getOrDefault(viewer.getUniqueId(), defaultText); - } - - @Override - public void setText(Player viewer, String text) { - String previous = this.textCache.put(viewer.getUniqueId(), text); - if (previous != null && previous.equals(text)) { - return; - } - PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, text, owner.getEntity().isSneaking())); - } - - @Override - public void removeText(Player viewer) { - String previous = this.textCache.remove(viewer.getUniqueId()); - if (previous != null && previous.equals(defaultText)) { - return; - } - PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, defaultText, owner.getEntity().isSneaking())); - } - - @Override - public void spawn(Player viewer, Pose pose) { - PacketManager.getInstance().send(viewer, getSpawnPackets(getText(viewer), pose)); - } - - @Override - public void destroy() { - for (Player all : viewerArray) { - PacketManager.getInstance().send(all, destroyPacket); - } - viewers.clear(); - textCache.clear(); - viewersToArray(); - } - - @Override - public void destroy(Player viewer) { - PacketManager.getInstance().send(viewer, destroyPacket); - textCache.remove(viewer.getUniqueId()); - } - - @Override - public void teleport(double x, double y, double z, boolean onGround) { - PacketContainer packet = getTeleportPacket(x, y, z, onGround); - for (Player all : viewerArray) { - PacketManager.getInstance().send(all, packet); - } - } - - @Override - public void teleport(Player viewer, double x, double y, double z, boolean onGround) { - if (viewers.contains(viewer)) { - PacketManager.getInstance().send(viewer, getTeleportPacket(x, y, z, onGround)); - } - } - - @Override - public void teleport() { - PacketContainer packet = getTeleportPacket(); - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, packet); - } - } - - @Override - public int getEntityId() { - return entityId; - } - - @Override - public void move(short x, short y, short z, boolean onGround) { - PacketContainer packet = getMovePacket(x, y, z, onGround); - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, packet); - } - } - - @Override - public void move(Player viewer, short x, short y, short z, boolean onGround) { - if (viewers.contains(viewer)) { - PacketContainer packet = getMovePacket(x, y, z, onGround); - PacketManager.getInstance().send(viewer, packet); - } - } - - @Override - public void respawn(Player viewer, Pose pose) { - destroy(viewer); - spawn(viewer, pose); - } - - @Override - public void respawn(Pose pose) { - for (Player viewer : viewerArray) { - respawn(viewer, pose); - } - } - - @Override - public UUID getUUID() { - return uuid; - } - - @Override - public void handlePose(Pose previous, Pose pose) { - // Add delay to prevent the tag from appearing earlier - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> { - for (Player viewer : viewerArray) { - respawn(viewer, pose); - } - }, 20, TimeUnit.MILLISECONDS); - } - - @Override - public void setSneak(boolean isSneaking, boolean onGround) { - if (!onGround) { - for (Player viewer : viewerArray) { - PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, getText(viewer), isSneaking)); - } - } - } - - public PacketContainer getMovePacket(short x, short y, short z, boolean onGround) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.REL_ENTITY_MOVE); - packet.getIntegers().write(0, entityId); - packet.getShorts().write(0, x); - packet.getShorts().write(1, y); - packet.getShorts().write(2, z); - packet.getBooleans().write(0, onGround); - return packet; - } - - public PacketContainer getTeleportPacket(double x, double y, double z, boolean onGround) { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getIntegers().write(0, entityId); - packet.getDoubles().write(0, x); - packet.getDoubles().write(1, y + ((owner instanceof UnlimitedPlayer player) ? player.getHatOffset() : 0) + getCorrection() + yOffset); - packet.getDoubles().write(2, z); - packet.getBooleans().write(0, onGround); - return packet; - } - - public PacketContainer getTeleportPacket() { - PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getIntegers().write(0, entityId); - Location location = getEntityLocation(); - packet.getDoubles().write(0, location.getX()); - packet.getDoubles().write(1, location.getY()); - packet.getDoubles().write(2, location.getZ()); - return packet; - } - - private Location getEntityLocation() { - var entity = owner.getEntity(); - double x = entity.getLocation().getX(); - double y = entity.getLocation().getY(); - double z = entity.getLocation().getZ(); - y += yOffset; - if (owner instanceof UnlimitedPlayer player) y += player.getHatOffset(); - y += getCorrection(); - return new Location(null, x, y, z); - } - - private PacketContainer[] getSpawnPackets(String text, Pose pose) { - PacketContainer entityPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY); - entityPacket.getModifier().write(0, entityId); - entityPacket.getModifier().write(1, uuid); - entityPacket.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND); - Location location = getEntityLocation(); - entityPacket.getDoubles().write(0, location.getX()); - entityPacket.getDoubles().write(1, location.getY()); - entityPacket.getDoubles().write(2, location.getZ()); - PacketContainer metaPacket = FakeEntityUtils.getMetaPacket(entityId, text, pose == Pose.SNEAKING); - return new PacketContainer[] {entityPacket, metaPacket}; - } - - private double getCorrection() { - double height = owner.getEntity().getHeight(); - if (CNConfig.hasLibsDisguise && DisguiseUtils.isDisguised(owner.getEntity()) && DisguiseUtils.getDisguisedType(owner.getEntity()) != EntityType.PLAYER) { - height = DisguiseUtils.getDisguisedHeight(owner.getEntity()); - } - return height - 1.8; - } - - private void viewersToArray() { - viewerArray = viewers.toArray(new Player[0]); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedEntity.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedEntity.java deleted file mode 100644 index aac1b9b..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedEntity.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited; - -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.EntityTagEntity; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextEntity; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.StaticTextTagSetting; -import net.momirealms.customnameplates.api.util.LocationUtils; -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Vector; - -public class UnlimitedEntity implements EntityTagEntity { - - protected final UnlimitedTagManagerImpl manager; - protected final Entity entity; - protected final Vector nearbyPlayers; - protected Player[] nearbyPlayerArray; - protected final Vector staticTags; - protected StaticTextEntity[] staticTagArray; - - public UnlimitedEntity(UnlimitedTagManagerImpl manager, Entity entity) { - this.manager = manager; - this.entity = entity; - this.nearbyPlayers = new Vector<>(); - this.staticTags = new Vector<>(); - staticTagVectorToArray(); - playerVectorToArray(); - } - - public Entity getEntity() { - return entity; - } - - public Player[] getNearbyPlayers() { - return nearbyPlayerArray; - } - - public void addNearbyPlayerNaturally(Player player) { - if (nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.add(player); - playerVectorToArray(); - for (StaticTextEntity tag : staticTagArray) { - if (tag.getComeRule().isPassed(player, entity)) { - tag.addPlayerToViewers(player); - } - } - } - - public void removeNearbyPlayerNaturally(Player player) { - if (!nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.remove(player); - playerVectorToArray(); - for (StaticTextEntity tag : staticTagArray) { - if (tag.getLeaveRule().isPassed(player, entity)) { - tag.removePlayerFromViewers(player); - } - } - } - - public void addNearByPlayerToMap(int range) { - for (Player online : Bukkit.getOnlinePlayers()) { - if ( online == entity - || LocationUtils.getDistance(online, entity) > range - || online.getWorld() != entity.getWorld() - || online.isDead() - ) continue; - addNearbyPlayerNaturally(online); - } - } - - @Override - public void addTag(StaticTextEntity tag) { - if (staticTags.contains(tag)) { - return; - } - staticTags.add(tag); - staticTagVectorToArray(); - for (Player all : nearbyPlayers) { - if (tag.getComeRule().isPassed(all, entity)) { - tag.addPlayerToViewers(all); - } - } - } - - @Override - public StaticTextEntity addTag(StaticTextTagSetting setting) { - var tag = manager.createNamedEntity(this, setting); - addTag(tag); - return tag; - } - - @Override - public void removeTag(StaticTextEntity tag) { - if (staticTags.remove(tag)) { - tag.destroy(); - staticTagVectorToArray(); - } - } - - @Override - public Collection getStaticTags() { - return new ArrayList<>(staticTags); - } - - @Override - public void forceAddNearbyPlayer(Player player) { - if (nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.add(player); - playerVectorToArray(); - for (StaticTextEntity tag : staticTagArray) { - tag.addPlayerToViewers(player); - } - } - - @Override - public void forceRemoveNearbyPlayer(Player player) { - if (!nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.remove(player); - playerVectorToArray(); - for (StaticTextEntity tag : staticTagArray) { - tag.removePlayerFromViewers(player); - } - } - - public void move(Player receiver, short x, short y, short z, boolean onGround) { - for (StaticTextEntity tag : staticTagArray) { - tag.move(receiver, x, y, z, onGround); - } - } - - public void teleport(Player receiver, double x, double y, double z, boolean onGround) { - for (StaticTextEntity tag : staticTagArray) { - tag.teleport(receiver, x, y, z, onGround); - } - } - - public void teleport() { - for (StaticTextEntity tag : staticTagArray) { - tag.teleport(); - } - } - - public void handlePose(Pose previous, Pose pose) { - for (StaticTextEntity tag : staticTagArray) { - tag.handlePose(previous, pose); - } - } - - @Override - public void destroy() { - manager.removeUnlimitedEntityFromMap(entity.getUniqueId()); - for (StaticTextEntity tag : staticTagArray) { - tag.destroy(); - } - nearbyPlayers.clear(); - staticTags.clear(); - staticTagArray = null; - nearbyPlayerArray = null; - } - - public void respawn() { - for (StaticTextEntity tag : staticTagArray) { - tag.respawn(entity.getPose()); - } - } - - protected void staticTagVectorToArray() { - staticTagArray = staticTags.toArray(new StaticTextEntity[0]); - } - - protected void playerVectorToArray() { - nearbyPlayerArray = nearbyPlayers.toArray(new Player[0]); - } - - public void setVisibility(boolean visible) { - if (!visible) { - for (StaticTextEntity tag : staticTagArray) { - for (Player player : nearbyPlayerArray) { - tag.removePlayerFromViewers(player); - } - } - } else { - for (StaticTextEntity tag : staticTagArray) { - for (Player player : nearbyPlayerArray) { - if (tag.getComeRule().isPassed(player, entity)) { - tag.addPlayerToViewers(player); - } - } - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedPlayer.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedPlayer.java deleted file mode 100644 index b37c170..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedPlayer.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.mechanic.nameplate.TagMode; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.*; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.common.team.TeamTagVisibility; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Vector; - -public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer { - - private final Player owner; - private final Vector dynamicTags; - private DynamicTextEntity[] dynamicTagArray; - private double hatOffset; - private boolean isPreviewing; - - public UnlimitedPlayer(UnlimitedTagManagerImpl manager, Player player) { - super(manager, player); - this.owner = player; - this.dynamicTags = new Vector<>(); - this.dynamicTagVectorToArray(); - } - - @Override - public void addTag(DynamicTextEntity tag) { - if (dynamicTags.contains(tag)) { - return; - } - dynamicTags.add(tag); - dynamicTagVectorToArray(); - for (Player all : getNearbyPlayers()) { - if (tag.canShow() && tag.canSee(all)) { - tag.addPlayerToViewers(all); - } - } - } - - @Override - public void addTag(StaticTextEntity tag) { - if (staticTags.contains(tag)) { - return; - } - staticTags.add(tag); - staticTagVectorToArray(); - for (Player all : getNearbyPlayers()) { - if (tag.getComeRule().isPassed(all, owner)) { - tag.addPlayerToViewers(all); - } - } - } - - @Override - public DynamicTextEntity addTag(DynamicTextTagSetting setting) { - var tag = manager.createNamedEntity(this, setting); - addTag(tag); - return tag; - } - - @Override - public StaticTextEntity addTag(StaticTextTagSetting setting) { - var tag = manager.createNamedEntity(this, setting); - addTag(tag); - return tag; - } - - @Override - public void removeTag(DynamicTextEntity tag) { - if (dynamicTags.remove(tag)) { - tag.destroy(); - dynamicTagVectorToArray(); - } - } - - @Override - public Collection getDynamicTags() { - return new ArrayList<>(dynamicTags); - } - - @Override - public void setHatOffset(double hatOffset) { - this.hatOffset = hatOffset; - } - - @Override - public void setPreview(boolean preview) { - if (isPreviewing == preview) { - return; - } - isPreviewing = preview; - if (isPreviewing) { - addNearbyPlayerNaturally(owner); - } else { - removeNearbyPlayerNaturally(owner); - } - } - - @Override - public boolean isPreviewing() { - return isPreviewing; - } - - @Override - public Player getPlayer() { - return owner; - } - - @Override - public void updateText() { - for (DynamicTextEntity tag : dynamicTagArray) { - tag.updateText(); - } - } - - @Override - public double getHatOffset() { - return hatOffset; - } - - @Override - public void addNearbyPlayerNaturally(Player player) { - if (nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.add(player); - playerVectorToArray(); - for (StaticTextEntity tag : staticTagArray) { - if (tag.getComeRule().isPassed(player, entity)) { - tag.addPlayerToViewers(player); - } - } - for (DynamicTextEntity tag : dynamicTagArray) { - if (tag.canShow() && tag.canSee(player)) { - tag.addPlayerToViewers(player); - } - } - setNameInvisibleFor(player); - } - - @Override - public void removeNearbyPlayerNaturally(Player player) { - if (!nearbyPlayers.contains(player)) { - return; - } - nearbyPlayers.remove(player); - playerVectorToArray(); - for (StaticTextEntity tag : staticTagArray) { - if (tag.getLeaveRule().isPassed(player, entity)) { - tag.removePlayerFromViewers(player); - } - } - for (DynamicTextEntity tag : dynamicTagArray) { - tag.removePlayerFromViewers(player); - } - setNameVisibleFor(player); - } - - @Override - public void destroy() { - manager.removeUnlimitedEntityFromMap(entity.getUniqueId()); - for (Player viewer : getNearbyPlayers()) { - if (getPlayer().isOnline()) { - setNameVisibleFor(viewer); - } - } - for (DynamicTextEntity tag : dynamicTagArray) { - tag.destroy(); - } - for (StaticTextEntity tag : staticTagArray) { - tag.destroy(); - } - nearbyPlayers.clear(); - dynamicTags.clear(); - staticTags.clear(); - staticTagArray = null; - } - - public void sneak(boolean sneaking, boolean flying) { - for (StaticTextEntity tag : staticTagArray) { - tag.setSneak(sneaking, !flying); - } - for (DynamicTextEntity tag : dynamicTagArray) { - tag.setSneak(sneaking, !flying); - } - } - - @Override - public void respawn() { - super.respawn(); - for (DynamicTextEntity tag : dynamicTagArray) { - tag.respawn(owner.getPose()); - } - } - - @Override - public void move(Player receiver, short x, short y, short z, boolean onGround) { - super.move(receiver, x, y, z, onGround); - for (DynamicTextEntity tag : dynamicTagArray) { - tag.move(receiver, x, y, z, onGround); - } - } - - @Override - public void teleport(Player receiver, double x, double y, double z, boolean onGround) { - super.teleport(receiver, x, y, z, onGround); - for (DynamicTextEntity tag : dynamicTagArray) { - tag.teleport(receiver, x, y, z, onGround); - } - } - - @Override - public void teleport() { - super.teleport(); - for (DynamicTextEntity tag : dynamicTagArray) { - tag.teleport(); - } - } - - @Override - public void handlePose(Pose previous, Pose pose) { - super.handlePose(previous, pose); - for (DynamicTextEntity tag : dynamicTagArray) { - tag.handlePose(previous, pose); - } - } - - private void setNameInvisibleFor(Player viewer) { - if (CNConfig.nameplateModule && CustomNameplatesPlugin.get().getNameplateManager().getTagMode() == TagMode.UNLIMITED) { - CustomNameplatesPlugin.get().getTeamManager().updateTeam( - owner, - viewer, - "", - "", - TeamColor.WHITE, - TeamTagVisibility.NEVER - ); - } - } - - private void setNameVisibleFor(Player viewer) { - if (CNConfig.nameplateModule && CustomNameplatesPlugin.get().getNameplateManager().getTagMode() == TagMode.UNLIMITED) { - CustomNameplatesPlugin.get().getTeamManager().updateTeam( - owner, - viewer, - "", - "", - TeamColor.WHITE, - TeamTagVisibility.ALWAYS - ); - } - } - - public void timer() { - for (DynamicTextEntity tag : dynamicTagArray) { - tag.timer(); - } - } - - public void updateVisibility() { - for (DynamicTextEntity tag : dynamicTagArray) { - tag.updateVisibility(); - } - } - - private void dynamicTagVectorToArray() { - dynamicTagArray = dynamicTags.toArray(new DynamicTextEntity[0]); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedTagManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedTagManagerImpl.java deleted file mode 100644 index 52a2443..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/UnlimitedTagManagerImpl.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.NameplateManager; -import net.momirealms.customnameplates.api.manager.UnlimitedTagManager; -import net.momirealms.customnameplates.api.mechanic.misc.ViewerText; -import net.momirealms.customnameplates.api.mechanic.tag.unlimited.*; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener.DisguiseListener; -import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener.MagicCosmeticsListener; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -public class UnlimitedTagManagerImpl implements UnlimitedTagManager { - - private final NameplateManager manager; - private final ConcurrentHashMap unlimitedEntityMap; - private CancellableTask refreshTask; - private MagicCosmeticsListener magicCosmeticsListener; - private DisguiseListener disguiseListener; - private final VehicleChecker vehicleChecker; - - public UnlimitedTagManagerImpl(NameplateManager nameplateManager) { - this.manager = nameplateManager; - this.unlimitedEntityMap = new ConcurrentHashMap<>(); - this.vehicleChecker = new VehicleChecker(this); - if (Bukkit.getPluginManager().getPlugin("MagicCosmetics") != null) { - this.magicCosmeticsListener = new MagicCosmeticsListener(this); - } - if (Bukkit.getPluginManager().getPlugin("LibsDisguises") != null) { - this.disguiseListener = new DisguiseListener(this); - } - } - - public void load() { - this.vehicleChecker.load(); - this.refreshTask = CustomNameplatesPlugin.get().getScheduler().runTaskAsyncTimer( - () -> { - try { - for (UnlimitedEntity unlimitedEntity : unlimitedEntityMap.values()) { - if (unlimitedEntity instanceof UnlimitedPlayer unlimitedPlayer) { - unlimitedPlayer.timer(); - } - } - } catch (Exception e) { - LogUtils.severe( - "Error occurred when updating unlimited tags. " + - "This might not be a bug in CustomNameplates. Please report " + - "to the Plugin on the top of the following " + - "stack trace." - ); - e.printStackTrace(); - } - }, - 50L, - 50L, - TimeUnit.MILLISECONDS - ); - if (this.magicCosmeticsListener != null) { - Bukkit.getPluginManager().registerEvents(magicCosmeticsListener, CustomNameplatesPlugin.get()); - } - if (this.disguiseListener != null) { - Bukkit.getPluginManager().registerEvents(disguiseListener, CustomNameplatesPlugin.get()); - } - } - - public void unload() { - this.vehicleChecker.unload(); - if (this.refreshTask != null && !this.refreshTask.isCancelled()) { - this.refreshTask.cancel(); - } - for (UnlimitedEntity entry : unlimitedEntityMap.values()) { - entry.destroy(); - } - if (this.magicCosmeticsListener != null) { - HandlerList.unregisterAll(magicCosmeticsListener); - } - if (this.disguiseListener != null) { - HandlerList.unregisterAll(disguiseListener); - } - } - - @NotNull - @Override - public StaticTextEntity createNamedEntity(EntityTagEntity entity, StaticTextTagSetting setting) { - return new StaticTextEntityImpl( - (UnlimitedEntity) entity, - setting.getVerticalOffset(), - setting.getComeRule(), - setting.getLeaveRule(), - setting.getDefaultText(), - setting.getID() - ); - } - - @NotNull - @Override - public DynamicTextEntity createNamedEntity(EntityTagPlayer player, DynamicTextTagSetting setting) { - return new DynamicTextEntityImpl( - (UnlimitedPlayer) player, - new ViewerText(player.getPlayer(), setting.getRawText()), - setting.getRefreshFrequency(), - setting.getCheckFrequency(), - setting.getVerticalOffset(), - setting.getOwnerRequirements(), - setting.getViewerRequirements() - ); - } - - @Override - public UnlimitedEntity createOrGetTagForEntity(Entity entity) { - if (entity instanceof Player player) { - return createOrGetTagForPlayer(player, false); - } - - final UUID uuid = entity.getUniqueId(); - if (this.unlimitedEntityMap.containsKey(uuid)) { - return this.unlimitedEntityMap.get(uuid); - } - - var unlimitedEntity = new UnlimitedEntity(this, entity); - this.unlimitedEntityMap.put( - uuid, - unlimitedEntity - ); - - unlimitedEntity.addNearByPlayerToMap(48); - return unlimitedEntity; - } - - @Override - public UnlimitedPlayer createOrGetTagForPlayer(Player player, boolean isJoin) { - if (!player.isOnline()) - return null; - final UUID uuid = player.getUniqueId(); - if (this.unlimitedEntityMap.containsKey(uuid)) { - return (UnlimitedPlayer) this.unlimitedEntityMap.get(uuid); - } - - var unlimitedPlayer = new UnlimitedPlayer(this, player); - this.unlimitedEntityMap.put( - uuid, - unlimitedPlayer - ); - - if (player.getGameMode() != GameMode.SPECTATOR && !isJoin) { - unlimitedPlayer.addNearByPlayerToMap(48); - } - return unlimitedPlayer; - } - - public UnlimitedEntity removeUnlimitedEntityFromMap(UUID uuid) { - return unlimitedEntityMap.remove(uuid); - } - - @Nullable - public UnlimitedEntity getUnlimitedObject(UUID uuid) { - return unlimitedEntityMap.get(uuid); - } - - public void handlePotionEffect(Entity entity, boolean visible) { - UnlimitedEntity unlimitedEntity = getUnlimitedObject(entity.getUniqueId()); - if (unlimitedEntity == null) return; - unlimitedEntity.setVisibility(visible); - if (unlimitedEntity instanceof UnlimitedPlayer unlimitedPlayer) { - CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(unlimitedPlayer::updateVisibility, 50, TimeUnit.MILLISECONDS); - } - } - - public void handleEntitySpawnPacket(Player receiver, int entityId) { - Player spawned = manager.getPlayerByEntityID(entityId); - if (spawned == null) return; - UnlimitedEntity unlimitedEntity = getUnlimitedObject(spawned.getUniqueId()); - if (unlimitedEntity == null) return; - if (receiver == spawned) return; - unlimitedEntity.addNearbyPlayerNaturally(receiver); - } - - public void handlePlayerPose(Player player, Pose pose) { - UnlimitedEntity unlimitedEntity = getUnlimitedObject(player.getUniqueId()); - if (unlimitedEntity != null) { - unlimitedEntity.handlePose(player.getPose(), pose); - } - } - - public void handlePlayerQuit(Player quit) { - UnlimitedEntity unlimitedEntity = removeUnlimitedEntityFromMap(quit.getUniqueId()); - if (unlimitedEntity != null) { - unlimitedEntity.destroy(); - } - for (UnlimitedEntity entry : unlimitedEntityMap.values()) { - entry.removeNearbyPlayerNaturally(quit); - } - } - - public void handleEntityMovePacket(Player receiver, int entityID, short x, short y, short z, boolean onGround) { - Entity mover = manager.getEntityByEntityID(entityID); - if (mover == null) return; - UnlimitedEntity unlimitedEntity = getUnlimitedObject(mover.getUniqueId()); - if (unlimitedEntity == null) return; - unlimitedEntity.move(receiver, x, y, z, onGround); - } - - public void handleEntityTeleportPacket(Player receiver, int entityID, double x, double y, double z, boolean onGround) { - Entity tp = manager.getEntityByEntityID(entityID); - if (tp == null) return; - UnlimitedEntity unlimitedEntity = getUnlimitedObject(tp.getUniqueId()); - if (unlimitedEntity == null) return; - unlimitedEntity.teleport(receiver, x, y, z, onGround); - } - - public void handleEntityDestroyPacket(Player receiver, List list) { - for (int id : list) { - handleSingleEntityDestroy(receiver, id); - } - } - - private void handleSingleEntityDestroy(Player receiver, int entityID) { - Entity deSpawned = manager.getEntityByEntityID(entityID); - if (deSpawned == null) return; - UnlimitedEntity unlimitedEntity = getUnlimitedObject(deSpawned.getUniqueId()); - if (unlimitedEntity == null) return; - unlimitedEntity.removeNearbyPlayerNaturally(receiver); - } - - public void handlePlayerSneak(Player sneaker, boolean sneaking, boolean flying) { - UnlimitedEntity unlimitedEntity = getUnlimitedObject(sneaker.getUniqueId()); - if (!(unlimitedEntity instanceof UnlimitedPlayer unlimitedPlayer)) return; - unlimitedPlayer.sneak(sneaking, flying); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/VehicleChecker.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/VehicleChecker.java deleted file mode 100644 index 9541b4f..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/nameplate/tag/unlimited/VehicleChecker.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.nameplate.tag.unlimited; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityDeathEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.spigotmc.event.entity.EntityDismountEvent; -import org.spigotmc.event.entity.EntityMountEvent; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -public class VehicleChecker implements Listener { - - private final Set entitiesOnVehicle; - private final UnlimitedTagManagerImpl unlimitedTagManager; - private CancellableTask updatePosTask; - - public VehicleChecker(UnlimitedTagManagerImpl unlimitedTagManager) { - this.unlimitedTagManager = unlimitedTagManager; - this.entitiesOnVehicle = Collections.synchronizedSet(new HashSet<>()); - } - - public void load() { - Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get()); - for (Player all : Bukkit.getOnlinePlayers()) { - Entity vehicle = all.getVehicle(); - if (vehicle != null) { - entitiesOnVehicle.add(all.getUniqueId()); - } - } - this.updatePosTask = CustomNameplatesPlugin.getInstance().getScheduler().runTaskAsyncTimer(() -> { - for (UUID inVehicle : entitiesOnVehicle) { - UnlimitedEntity unlimitedEntity = unlimitedTagManager.getUnlimitedObject(inVehicle); - if (unlimitedEntity != null) { - unlimitedEntity.teleport(); - } - } - }, 100, 100, TimeUnit.MILLISECONDS); - } - - public void unload() { - HandlerList.unregisterAll(this); - if (this.updatePosTask != null && !this.updatePosTask.isCancelled()) - this.updatePosTask.cancel(); - this.entitiesOnVehicle.clear(); - } - - @EventHandler (ignoreCancelled = true) - public void onMount(EntityMountEvent event) { - final Entity passenger = event.getEntity(); - final UUID uuid = passenger.getUniqueId(); - UnlimitedEntity unlimitedEntity = unlimitedTagManager.getUnlimitedObject(uuid); - if (unlimitedEntity != null) { - unlimitedEntity.teleport(); - entitiesOnVehicle.add(uuid); - } - } - - @EventHandler (ignoreCancelled = true) - public void onDeath(EntityDeathEvent event) { - final UUID uuid = event.getEntity().getUniqueId(); - entitiesOnVehicle.remove(uuid); - } - - @EventHandler (ignoreCancelled = true) - public void onDismount(EntityDismountEvent event) { - final Entity passenger = event.getEntity(); - final UUID uuid = passenger.getUniqueId(); - UnlimitedEntity unlimitedEntity = unlimitedTagManager.getUnlimitedObject(uuid); - if (unlimitedEntity != null) { - unlimitedEntity.respawn(); - entitiesOnVehicle.remove(uuid); - } - } - - @EventHandler - public void onJoin(PlayerJoinEvent event) { - final Player player = event.getPlayer(); - Entity vehicle = player.getVehicle(); - if (vehicle != null) { - entitiesOnVehicle.add(player.getUniqueId()); - } - } - - @EventHandler - public void onQuit(PlayerJoinEvent event) { - entitiesOnVehicle.remove(event.getPlayer().getUniqueId()); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/pack/ResourcePackManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/pack/ResourcePackManagerImpl.java deleted file mode 100644 index dc8289d..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/pack/ResourcePackManagerImpl.java +++ /dev/null @@ -1,669 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.pack; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.ResourcePackManager; -import net.momirealms.customnameplates.api.mechanic.background.BackGround; -import net.momirealms.customnameplates.api.mechanic.bubble.Bubble; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate; -import net.momirealms.customnameplates.api.mechanic.placeholder.DescentText; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.ConfigUtils; -import org.apache.commons.io.FileUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.Plugin; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -public class ResourcePackManagerImpl implements ResourcePackManager { - - private final CustomNameplatesPlugin plugin; - - public ResourcePackManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - } - - public void reload() { - unload(); - load(); - } - - public void load() { - - } - - public void unload() { - - } - - @Override - public void generateResourcePack() { - // delete the old one - File resourcePackFolder = new File(plugin.getDataFolder() + File.separator + "ResourcePack"); - this.deleteDirectory(resourcePackFolder); - - // create folders - File fontFolder = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "assets" + File.separator + CNConfig.namespace + File.separatorChar + "font"); - File texturesFolder = new File(plugin.getDataFolder(), "ResourcePack" + File.separator+ "assets" + File.separator + CNConfig.namespace + File.separatorChar + "textures"); - if (!fontFolder.mkdirs() || !texturesFolder.mkdirs()) { - LogUtils.severe("Failed to generate resource pack folders"); - return; - } - - // create json object - JsonObject fontJson = new JsonObject(); - JsonArray providers = new JsonArray(); - fontJson.add("providers", providers); - - // save BossBars - this.saveBossBar(); - // save unicodes - this.saveLegacyUnicodes(); - // generate shaders - if (!plugin.getVersionManager().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) { - e.printStackTrace(); - } - } - // 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); - // set pack.mcmeta - this.setPackFormat(); - // save json object to file - this.saveFont(fontJson); - // copy the resource pack to hooked plugins - this.copyResourcePackToHookedPlugins(resourcePackFolder); - } - - private void generateShaders(String path, boolean v1_20_5) { - if (!CNConfig.enableShader) return; - plugin.saveResource(path + "rendertype_text.fsh", true); - plugin.saveResource(path + "rendertype_text.json", true); - plugin.saveResource(path + "rendertype_text.vsh", true); - 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) { - e.printStackTrace(); - } - - 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%", !CNConfig.animatedImage ? "" : ShaderConstants.Animated_Text_Out) - .replace("%SHADER_1%", !CNConfig.textEffects ? mainShader : ShaderConstants.ItemsAdder_Text_Effects + mainShader) - .replace("%SHADER_2%", !CNConfig.animatedImage ? "" : ShaderConstants.Animated_Text_VSH) - .replace("%SHADER_3%", !CNConfig.hideScoreboardNumber ? "" : ShaderConstants.Hide_ScoreBoard_Numbers) - ); - } catch (IOException e) { - e.printStackTrace(); - } - - 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) { - e.printStackTrace(); - } - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(shader2), StandardCharsets.UTF_8))) { - writer.write(sb2.toString() - .replace("%SHADER_0%", !CNConfig.animatedImage ? "" : ShaderConstants.Animated_Text_In) - .replace("%SHADER_1%", !CNConfig.animatedImage ? "" : ShaderConstants.Animated_Text_FSH) - ); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void saveLegacyUnicodes() { - if (CNConfig.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 saveBossBar() { - if (CNConfig.newBossBarImage) { - String color = CNConfig.barColorToRemove.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.saveResource(path + color + "_background.png", true); - plugin.saveResource(path + color + "_progress.png", true); - } - if (CNConfig.legacyBossBarImage) { - String path = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "gui" + File.separator + "bars.png"; - plugin.saveResource(path, true); - try { - File inputFile = new File(plugin.getDataFolder(), path); - BufferedImage image = ImageIO.read(inputFile); - int y; - switch (CNConfig.barColorToRemove) { - 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) { - e.printStackTrace(); - } - } - } - - private void saveFont(JsonObject fontJson) { - try (FileWriter fileWriter = new FileWriter( - plugin.getDataFolder() + - File.separator + "ResourcePack" + - File.separator + "assets" + - File.separator + CNConfig.namespace + - File.separator + "font" + - File.separator + CNConfig.font + ".json") - ) { - fileWriter.write(fontJson.toString().replace("\\\\", "\\")); - } catch (IOException e) { - e.printStackTrace(); - } - - ArrayList ascentTexts = new ArrayList<>(); - ArrayList ascentUnicodes = new ArrayList<>(); - - for (DescentText descentText : plugin.getPlaceholderManager().getDescentTexts()) { - if (descentText.isUnicode()) { - ascentUnicodes.add(descentText.getAscent()); - } else { - ascentTexts.add(descentText.getAscent()); - } - } - - ascentTexts.removeAll(ascentUnicodes); - - for (int ascent : ascentTexts) { - String line; - StringBuilder sb = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(plugin.getDataFolder(), "font" + File.separator + "default.json")), StandardCharsets.UTF_8))) { - while ((line = reader.readLine()) != null) { - sb.append(line).append(System.lineSeparator()); - } - } catch (IOException e) { - e.printStackTrace(); - } - File outPut = new File(plugin.getDataFolder(), - "ResourcePack" + - File.separator + "assets" + - File.separator + CNConfig.namespace + - File.separator + "font" + - File.separator + "ascent_" + ascent + ".json"); - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(outPut), StandardCharsets.UTF_8))) { - writer.write(sb.toString().replace("\\\\", "\\").replace("%ascent%", String.valueOf(ascent)).replace("%ASCENT%", String.valueOf(ascent+3))); - } catch (IOException e) { - e.printStackTrace(); - } - } - for (int ascent : ascentUnicodes) { - String line; - StringBuilder sb = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(plugin.getDataFolder(), "font" + File.separator + "unicode.json")), StandardCharsets.UTF_8))) { - while ((line = reader.readLine()) != null) { - sb.append(line).append(System.lineSeparator()); - } - } catch (IOException e) { - e.printStackTrace(); - } - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(new File(plugin.getDataFolder(), - "ResourcePack" + - File.separator + "assets" + - File.separator + CNConfig.namespace + - File.separator + "font" + - File.separator + "ascent_" + ascent + ".json")), StandardCharsets.UTF_8))) { - writer.write(sb.toString().replace("\\\\", "\\").replace("%ascent%", String.valueOf(ascent)).replace("%ASCENT%", String.valueOf(ascent+3))); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - @Override - public void deleteDirectory(File file){ - if (file.exists()) { - try { - FileUtils.deleteDirectory(file); - } catch (IOException e){ - e.printStackTrace(); - } - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void saveSplit(File texturesFolder) { - try { - plugin.saveResource("space_split.png", false); - FileUtils.copyFile(new File(plugin.getDataFolder(),"space_split.png"), new File(texturesFolder, CNConfig.folderSplit.replace("\\", File.separator) + "space_split.png")); - File file = new File(plugin.getDataFolder(),"space_split.png"); - if (file.exists()) { - file.delete(); - } - } catch (IOException e){ - e.printStackTrace(); - } - } - - private void copyResourcePackToHookedPlugins(File resourcePackFolder) { - Plugin ia = Bukkit.getPluginManager().getPlugin("ItemsAdder"); - if (ia != null) { - File file = new File(ia.getDataFolder(), "config.yml"); - YamlConfiguration iaConfig = YamlConfiguration.loadConfiguration(file); - List folders = iaConfig.getStringList("resource-pack.zip.merge_other_plugins_resourcepacks_folders"); - boolean changed = false; - if (CNConfig.copyPackIA) { - if (!folders.contains("CustomNameplates/ResourcePack")) { - folders.add("CustomNameplates/ResourcePack"); - iaConfig.set("resource-pack.zip.merge_other_plugins_resourcepacks_folders", folders); - changed = true; - } - } else { - if (folders.contains("CustomNameplates/ResourcePack")) { - folders.remove("CustomNameplates/ResourcePack"); - iaConfig.set("resource-pack.zip.merge_other_plugins_resourcepacks_folders", folders); - changed = true; - } - } - if (changed) { - try { - iaConfig.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - if (CNConfig.copyPackIAOld){ - try { - FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("ItemsAdder")).getDataFolder() + File.separator + "contents" + File.separator + "nameplates" + File.separator + "resourcepack" + File.separator + "assets") ); - } - catch (IOException e){ - e.printStackTrace(); - LogUtils.warn("Failed to copy files to ItemsAdder..."); - } - } - if (CNConfig.copyPackOraxen){ - try { - FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(Objects.requireNonNull(Bukkit.getPluginManager().getPlugin("Oraxen")).getDataFolder() + File.separator + "pack" + File.separator + "assets")); - } - catch (IOException e){ - e.printStackTrace(); - LogUtils.warn("Failed to copy files to Oraxen..."); - } - } - } - - private List getNameplates(File texturesFolder) { - ArrayList list = new ArrayList<>(); - if (!CNConfig.nameplateModule) return list; - for (Nameplate nameplate : plugin.getNameplateManager().getNameplates()) { - for (ConfiguredChar configuredChar : new ConfiguredChar[]{nameplate.getLeft(), nameplate.getMiddle(), nameplate.getRight()}) { - JsonObject jo = new JsonObject(); - jo.add("type", new JsonPrimitive("bitmap")); - jo.add("file", new JsonPrimitive(CNConfig.namespace + ":" + CNConfig.folderNameplate.replaceAll("\\\\", "/") + configuredChar.getFile())); - jo.add("ascent", new JsonPrimitive(configuredChar.getAscent())); - jo.add("height", new JsonPrimitive(configuredChar.getHeight())); - JsonArray ja = new JsonArray(); - ja.add(ConfigUtils.native2ascii(configuredChar.getCharacter())); - jo.add("chars", ja); - list.add(jo); - try { - FileUtils.copyFile( - new File(plugin.getDataFolder(), - "contents" + File.separator + "nameplates" + File.separator + configuredChar.getFile()), - new File(texturesFolder, - CNConfig.folderNameplate.replace("\\", File.separator) + configuredChar.getFile())); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return list; - } - - - private List getBackgrounds(File texturesFolder) { - ArrayList list = new ArrayList<>(); - if (!CNConfig.backgroundModule) return list; - for (BackGround backGround : plugin.getBackGroundManager().getBackGrounds()) { - for (ConfiguredChar configuredChar : new ConfiguredChar[]{ - backGround.getLeft(), backGround.getOffset_1(), - backGround.getOffset_2(), backGround.getOffset_4(), - backGround.getOffset_8(), backGround.getOffset_16(), - backGround.getOffset_32(), backGround.getOffset_64(), - backGround.getOffset_128(), backGround.getRight()} - ) { - JsonObject jo = new JsonObject(); - jo.add("type", new JsonPrimitive("bitmap")); - jo.add("file", new JsonPrimitive(CNConfig.namespace + ":" + CNConfig.folderBackground.replaceAll("\\\\", "/") + configuredChar.getFile())); - jo.add("ascent", new JsonPrimitive(configuredChar.getAscent())); - jo.add("height", new JsonPrimitive(configuredChar.getHeight())); - JsonArray ja = new JsonArray(); - ja.add(ConfigUtils.native2ascii(configuredChar.getCharacter())); - jo.add("chars", ja); - list.add(jo); - try { - FileUtils.copyFile( - new File(plugin.getDataFolder(), - "contents" + File.separator + "backgrounds" + File.separator + configuredChar.getFile()), - new File(texturesFolder, - CNConfig.folderBackground.replace("\\", File.separator) + configuredChar.getFile())); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return list; - } - - private List getBubbles(File texturesFolder) { - ArrayList list = new ArrayList<>(); - if (!CNConfig.bubbleModule) return list; - for (Bubble bubble : plugin.getBubbleManager().getBubbles()) { - for (ConfiguredChar configuredChar : new ConfiguredChar[]{bubble.getLeft(), bubble.getMiddle(), bubble.getRight(), bubble.getTail()}) { - JsonObject jo = new JsonObject(); - jo.add("type", new JsonPrimitive("bitmap")); - jo.add("file", new JsonPrimitive(CNConfig.namespace + ":" + CNConfig.folderBubble.replaceAll("\\\\", "/") + configuredChar.getFile())); - jo.add("ascent", new JsonPrimitive(configuredChar.getAscent())); - jo.add("height", new JsonPrimitive(configuredChar.getHeight())); - JsonArray ja = new JsonArray(); - ja.add(ConfigUtils.native2ascii(configuredChar.getCharacter())); - jo.add("chars", ja); - list.add(jo); - try { - FileUtils.copyFile( - new File(plugin.getDataFolder(), - "contents" + File.separator + "bubbles" + File.separator + configuredChar.getFile()), - new File(texturesFolder, - CNConfig.folderBubble.replace("\\", File.separator) + configuredChar.getFile())); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return list; - } - - private List getImages(File texturesFolder) { - ArrayList list = new ArrayList<>(); - if (!CNConfig.imageModule) return list; - for (ConfiguredChar configuredChar : plugin.getImageManager().getImages()) { - JsonObject jo = new JsonObject(); - jo.add("type", new JsonPrimitive("bitmap")); - jo.add("file", new JsonPrimitive(CNConfig.namespace + ":" + CNConfig.folderImage.replaceAll("\\\\", "/") + configuredChar.getFile())); - jo.add("ascent", new JsonPrimitive(configuredChar.getAscent())); - jo.add("height", new JsonPrimitive(configuredChar.getHeight())); - JsonArray ja = new JsonArray(); - ja.add(ConfigUtils.native2ascii(configuredChar.getCharacter())); - jo.add("chars", ja); - list.add(jo); - try { - FileUtils.copyFile( - new File(plugin.getDataFolder(), - "contents" + File.separator + "images" + File.separator + configuredChar.getFile()), - new File(texturesFolder, - CNConfig.folderImage.replace("\\", File.separator) + configuredChar.getFile())); - } catch (IOException e) { - e.printStackTrace(); - } - } - return list; - } - - private List getOffsets(File texturesFolder) { - this.saveSplit(texturesFolder); - ArrayList list = new ArrayList<>(); - for (OffsetFont offsetFont : OffsetFont.values()) { - JsonObject jsonObject = new JsonObject(); - jsonObject.add("type", new JsonPrimitive("bitmap")); - jsonObject.add("file", new JsonPrimitive(CNConfig.namespace + ":" + CNConfig.folderSplit.replaceAll("\\\\","/") + "space_split.png")); - jsonObject.add("ascent", new JsonPrimitive(-5000)); - jsonObject.add("height", new JsonPrimitive(offsetFont.getHeight())); - final JsonArray jsonArray = new JsonArray(); - jsonArray.add(ConfigUtils.native2ascii(offsetFont.getCharacter())); - jsonObject.add("chars", jsonArray); - list.add(jsonObject); - } - return list; - } - - private void setPackFormat() { - if (plugin.getVersionManager().isVersionNewerThan1_20_5()) { - plugin.saveResource("ResourcePack" + File.separator + "pack_1_20_5.mcmeta", false); - 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.saveResource("ResourcePack" + File.separator + "pack.mcmeta", false); - } - -// File format_file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta"); -// String line; -// StringBuilder sb = new StringBuilder(); -// try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(format_file), StandardCharsets.UTF_8))) { -// while ((line = reader.readLine()) != null) { -// sb.append(line).append(System.lineSeparator()); -// } -// } catch (IOException e) { -// e.printStackTrace(); -// } -// try (BufferedWriter writer = new BufferedWriter( -// new OutputStreamWriter(new FileOutputStream(new File(plugin.getDataFolder(), -// "ResourcePack" + File.separator + "pack.mcmeta")), StandardCharsets.UTF_8))) { -// writer.write(sb.toString().replace("%version%", String.valueOf(plugin.getVersionManager().getPackFormat()))); -// } catch (IOException e) { -// e.printStackTrace(); -// } - } - - 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"; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/placeholder/PlaceholderManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/placeholder/PlaceholderManagerImpl.java deleted file mode 100644 index 61c3044..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/placeholder/PlaceholderManagerImpl.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.placeholder; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.PlaceholderManager; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.placeholder.*; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.Pair; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class PlaceholderManagerImpl implements PlaceholderManager { - - private final Pattern placeholderPattern = Pattern.compile("%([^%]*)%"); - private final PluginPlaceholders pluginPlaceholders; - private final HashMap staticTextMap; - private final HashMap switchTextMap; - private final HashMap descentTextMap; - private final HashMap conditionalTextMap; - private final HashMap nameplateTextMap; - private final HashMap backGroundTextMap; - private final HashMap vanillaHudMap; - private final CustomNameplatesPlugin plugin; - - public PlaceholderManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - this.pluginPlaceholders = new PluginPlaceholders(plugin, this); - this.staticTextMap = new HashMap<>(); - this.switchTextMap = new HashMap<>(); - this.descentTextMap = new HashMap<>(); - this.conditionalTextMap = new HashMap<>(); - this.nameplateTextMap = new HashMap<>(); - this.backGroundTextMap = new HashMap<>(); - this.vanillaHudMap = new HashMap<>(); - } - - @NotNull - @Override - public List detectPlaceholders(String text){ - List placeholders = new ArrayList<>(); - Matcher matcher = placeholderPattern.matcher(text); - while (matcher.find()) placeholders.add(matcher.group()); - return placeholders; - } - - public void reload() { - unload(); - load(); - } - - public void load() { - this.loadConfigs(); - - if (!pluginPlaceholders.isRegistered()) - pluginPlaceholders.register(); - } - - public void unload() { - if (pluginPlaceholders.isRegistered()) - pluginPlaceholders.unregister(); - - this.staticTextMap.clear(); - this.switchTextMap.clear(); - this.descentTextMap.clear(); - this.conditionalTextMap.clear(); - this.nameplateTextMap.clear(); - this.backGroundTextMap.clear(); - this.vanillaHudMap.clear(); - } - - @Override - public StaticText getStaticText(String key) { - return staticTextMap.get(key); - } - - @Override - public Collection getStaticTexts() { - return staticTextMap.values(); - } - - @Override - public SwitchText getSwitchText(String key) { - return switchTextMap.get(key); - } - - @Override - public Collection getSwitchTexts() { - return switchTextMap.values(); - } - - @Override - public DescentText getDescentText(String key) { - return descentTextMap.get(key); - } - - @Override - public Collection getDescentTexts() { - return descentTextMap.values(); - } - - @Override - public ConditionalText getConditionalText(String key) { - return conditionalTextMap.get(key); - } - - @Override - public Collection getConditionalTexts() { - return conditionalTextMap.values(); - } - - @Override - public NameplateText getNameplateText(String key) { - return nameplateTextMap.get(key); - } - - @Override - public Collection getNameplateTexts() { - return nameplateTextMap.values(); - } - - @Override - public BackGroundText getBackGroundText(String key) { - return backGroundTextMap.get(key); - } - - @Override - public Collection getBackGroundTexts() { - return backGroundTextMap.values(); - } - - @Override - public VanillaHud getVanillaHud(String key) { - return vanillaHudMap.get(key); - } - - @Override - public Collection getVanillaHuds() { - return vanillaHudMap.values(); - } - - public void loadConfigs() { - YamlConfiguration config = plugin.getConfig("configs" + File.separator + "custom-placeholders.yml"); - ConfigurationSection staticSection = config.getConfigurationSection("static-text"); - if (staticSection != null) { - loadStaticTexts(staticSection); - } - - ConfigurationSection switchSection = config.getConfigurationSection("switch-text"); - if (switchSection != null) { - loadSwitchTexts(switchSection); - } - - ConfigurationSection descentSection = config.getConfigurationSection("descent-text"); - if (descentSection != null) { - loadDescentTexts(descentSection, false); - } - - ConfigurationSection unicodeSection = config.getConfigurationSection("descent-unicode"); - if (unicodeSection != null) { - loadDescentTexts(unicodeSection, true); - } - - ConfigurationSection conditionalSection = config.getConfigurationSection("conditional-text"); - if (conditionalSection != null) { - loadConditionalTexts(conditionalSection); - } - - ConfigurationSection nameplateSection = config.getConfigurationSection("nameplate-text"); - if (nameplateSection != null) { - loadNameplateTexts(nameplateSection); - } - - ConfigurationSection backgroundSection = config.getConfigurationSection("background-text"); - if (backgroundSection != null) { - loadBackGroundTexts(backgroundSection); - } - - ConfigurationSection vanillaSection = config.getConfigurationSection("vanilla-hud"); - if (vanillaSection != null) { - loadVanillaHuds(vanillaSection); - } - } - - private void loadVanillaHuds(ConfigurationSection section) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - ConfiguredChar fullC = plugin.getImageManager().getImage(innerSection.getString("images.full","")); - if (fullC == null) { - LogUtils.warn("Vanilla hud placeholder wouldn't work because image " + innerSection.getString("full","") + " doesn't exist."); - continue; - } - - ConfiguredChar emptyC = plugin.getImageManager().getImage(innerSection.getString("images.empty","")); - if (emptyC == null) { - LogUtils.warn("Vanilla hud placeholder wouldn't work because image " + innerSection.getString("empty","") + " doesn't exist."); - continue; - } - - ConfiguredChar halfC = plugin.getImageManager().getImage(innerSection.getString("images.half","")); - if (halfC == null) { - LogUtils.warn("Vanilla hud placeholder wouldn't work because image " + innerSection.getString("half","") + " doesn't exist."); - continue; - } - - vanillaHudMap.put(entry.getKey(), - VanillaHud.builder() - .reverse(innerSection.getBoolean("reverse", true)) - .half(halfC.getCharacter()) - .empty(emptyC.getCharacter()) - .full(fullC.getCharacter()) - .max(innerSection.getString("placeholder.max-value")) - .current(innerSection.getString("placeholder.value")) - .build() - ); - } - } - - private void loadBackGroundTexts(ConfigurationSection section) { - if (!CNConfig.backgroundModule) return; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - if (innerSection.contains("background")) { - String bg = innerSection.getString("background"); - var background = plugin.getBackGroundManager().getBackGround(bg); - if (background == null) { - LogUtils.warn("Background: " + bg + " doesn't exist"); - continue; - } - backGroundTextMap.put(entry.getKey(), - BackGroundText.builder() - .background(background) - .text(innerSection.getString("text", "")) - .removeShadow(innerSection.getBoolean("remove-shadow")) - .build() - ); - } - } - } - - private void loadNameplateTexts(ConfigurationSection section) { - if (!CNConfig.nameplateModule) return; - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - var nameplate = plugin.getNameplateManager().getNameplate(innerSection.getString("nameplate")); - if (nameplate == null) { - LogUtils.warn("Nameplate: " + innerSection.getString("nameplate") + " doesn't exist. nameplate-text: " + entry.getKey() + " would not take effect"); - continue; - } - - nameplateTextMap.put(entry.getKey(), - NameplateText.builder() - .nameplate(nameplate) - .text(innerSection.getString("text", "")) - .build() - ); - } - } - - private void loadDescentTexts(ConfigurationSection section, boolean unicode) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - descentTextMap.put( - entry.getKey(), - DescentText.builder() - .descent(innerSection.getInt("descent", 0)) - .text(innerSection.getString("text", "")) - .unicode(unicode || innerSection.getBoolean("is-unicode")) - .build() - ); - } - } - - private void loadStaticTexts(ConfigurationSection section) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - staticTextMap.put( - entry.getKey(), - StaticText.builder() - .value(innerSection.getInt("value")) - .text(innerSection.getString("text")) - .state(StaticText.StaticState.valueOf(innerSection.getString("position", "middle").toUpperCase(Locale.ENGLISH))) - .build() - ); - } - } - - private void loadSwitchTexts(ConfigurationSection section) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - HashMap valueMap = new HashMap<>(); - ConfigurationSection valueSection = innerSection.getConfigurationSection("case"); - if (valueSection != null) { - for (String key : valueSection.getKeys(false)) { - valueMap.put(key, valueSection.getString(key)); - } - } - - switchTextMap.put( - entry.getKey(), - SwitchText.builder() - .toParse(Objects.requireNonNull(innerSection.getString("switch"))) - .defaultValue(innerSection.getString("default", "")) - .valueMap(valueMap) - .build() - ); - } - } - - private void loadConditionalTexts(ConfigurationSection section) { - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection innerSection)) - continue; - - ArrayList> list = new ArrayList<>(); - for (Map.Entry innerEntry : innerSection.getValues(false).entrySet()) { - if (!(innerEntry.getValue() instanceof ConfigurationSection prioritySection)) { - continue; - } - list.add(Pair.of( - prioritySection.getString("text"), - plugin.getRequirementManager().getRequirements(prioritySection.getConfigurationSection("conditions")) - )); - } - - conditionalTextMap.put( - entry.getKey(), - ConditionalText.builder() - .textList(list) - .build() - ); - } - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/placeholder/PluginPlaceholders.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/placeholder/PluginPlaceholders.java deleted file mode 100644 index 29988f6..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/placeholder/PluginPlaceholders.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.placeholder; - -import me.clip.placeholderapi.PlaceholderAPI; -import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.PlaceholderManager; -import net.momirealms.customnameplates.api.mechanic.bubble.Bubble; -import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar; -import net.momirealms.customnameplates.api.mechanic.font.OffsetFont; -import net.momirealms.customnameplates.api.mechanic.nameplate.Nameplate; -import net.momirealms.customnameplates.api.mechanic.placeholder.*; -import net.momirealms.customnameplates.api.util.FontUtils; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PluginPlaceholders extends PlaceholderExpansion { - - private final CustomNameplatesPlugin plugin; - private final PlaceholderManager placeholderManager; - - public PluginPlaceholders(CustomNameplatesPlugin plugin, PlaceholderManager placeholderManager) { - this.plugin = plugin; - this.placeholderManager = placeholderManager; - } - - @Override - public @NotNull String getIdentifier() { - return "nameplates"; - } - - @Override - public @NotNull String getAuthor() { - return "XiaoMoMi"; - } - - @Override - public @NotNull String getVersion() { - return "2.3"; - } - - @Override - public boolean persist() { - return true; - } - - @Override - public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { - String[] mainSplit = params.split("_", 2); - String mainPara = mainSplit[0]; - String mainArg = mainSplit.length == 1 ? "" : mainSplit[1]; - switch (mainPara) { - case "image" -> { - ConfiguredChar configuredChar = plugin.getImageManager().getImage(mainArg); - if (configuredChar == null) return "Image not exists"; - return FontUtils.surroundNameplateFont(String.valueOf(configuredChar.getCharacter())); - } - case "image-char" -> { - ConfiguredChar configuredChar = plugin.getImageManager().getImage(mainArg); - if (configuredChar == null) return "Image not exists"; - return String.valueOf(configuredChar.getCharacter()); - } - case "offset" -> { - return FontUtils.surroundNameplateFont(OffsetFont.getOffsetChars(Integer.parseInt(mainArg))); - } - case "offset-char" -> { - return OffsetFont.getOffsetChars(Integer.parseInt(mainArg)); - } - case "checkupdate" -> { - return String.valueOf(!plugin.getVersionManager().isLatest()); - } - case "is-latest" -> { - return String.valueOf(plugin.getVersionManager().isLatest()); - } - case "static" -> { - StaticText text = placeholderManager.getStaticText(mainArg); - if (text == null) return "Static text not exists: " + mainArg; - return text.getValue(offlinePlayer); - } - case "unicode", "descent" -> { - DescentText descentText = placeholderManager.getDescentText(mainArg); - if (descentText == null) return "Descent text not exists: " + mainArg; - return descentText.getValue(offlinePlayer); - } - case "conditional" -> { - ConditionalText conditionalText = placeholderManager.getConditionalText(mainArg); - if (conditionalText == null) return "Conditional text not exists: " + mainArg; - return conditionalText.getValue(offlinePlayer); - } - case "nameplate" -> { - NameplateText nameplateText = placeholderManager.getNameplateText(mainArg); - if (nameplateText == null) return "Nameplate text not exists: " + mainArg; - return nameplateText.getValue(offlinePlayer); - } - case "background" -> { - BackGroundText backGroundText = placeholderManager.getBackGroundText(mainArg); - if (backGroundText == null) return "Background text not exists: " + mainArg; - return backGroundText.getValue(offlinePlayer); - } - case "vanilla" -> { - VanillaHud vanillaHud = placeholderManager.getVanillaHud(mainArg); - if (vanillaHud == null) return "Vanilla text not exists: " + mainArg; - return vanillaHud.getValue(offlinePlayer); - } - case "switch" -> { - SwitchText switchText = placeholderManager.getSwitchText(mainArg); - if (switchText == null) return "Switch text not exists: " + mainArg; - return switchText.getValue(offlinePlayer); - } - // fix MagicAvatar's ugly colors - case "magicavatar" -> { - String parsed = PlaceholderAPI.setPlaceholders(offlinePlayer, "%magicavatar_player%"); - return parsed.replace("§r", ""); - } - } - - Player onlinePlayer = offlinePlayer.getPlayer(); - if (onlinePlayer == null) return null; - switch (mainPara) { - case "time" -> { - long time = onlinePlayer.getWorld().getTime(); - String ap = time >= 6000 && time < 18000 ? " PM" : " AM"; - int hours = (int) (time / 1000) ; - int minutes = (int) ((time - hours * 1000 ) * 0.06); - hours += 6; - while (hours >= 12) hours -= 12; - if (minutes < 10) return hours + ":0" + minutes + ap; - else return hours + ":" + minutes + ap; - } - case "actionbar" -> { - return plugin.getActionBarManager().getOtherPluginActionBar(onlinePlayer); - } - case "prefix" -> { - return plugin.getNameplateManager().getNameplatePrefix(onlinePlayer); - } - case "suffix" -> { - return plugin.getNameplateManager().getNameplateSuffix(onlinePlayer); - } - case "nametag" -> { - return plugin.getNameplateManager().getFullNameTag(onlinePlayer); - } - case "equipped" -> { - var optPlayer = plugin.getStorageManager().getOnlineUser(onlinePlayer.getUniqueId()); - if (optPlayer.isEmpty()) return "Data not loaded"; - switch (mainArg) { - case "nameplate" -> { - return optPlayer.get().getNameplateKey(); - } - case "nameplate-name" -> { - Nameplate nameplate = optPlayer.get().getNameplate(); - if (nameplate == null) { - return ""; - } - return nameplate.getDisplayName(); - } - case "bubble" -> { - return optPlayer.get().getBubbleKey(); - } - case "bubble-name" -> { - Bubble bubble = optPlayer.get().getBubble(); - if (bubble == null) { - return ""; - } - return bubble.getDisplayName(); - } - } - } - } - return null; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/RequirementManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/RequirementManagerImpl.java deleted file mode 100644 index 678d056..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/RequirementManagerImpl.java +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement; - -import me.clip.placeholderapi.PlaceholderAPI; -import net.momirealms.customnameplates.api.manager.RequirementManager; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.api.requirement.RequirementExpansion; -import net.momirealms.customnameplates.api.requirement.RequirementFactory; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.Pair; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.mechanic.requirement.papi.PapiCondition; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.customnameplates.paper.util.ClassUtils; -import net.momirealms.customnameplates.paper.util.ConfigUtils; -import net.momirealms.customnameplates.paper.util.DisguiseUtils; -import net.momirealms.customnameplates.paper.util.GeyserUtils; -import net.momirealms.sparrow.heart.SparrowHeart; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.*; - -public class RequirementManagerImpl implements RequirementManager { - - private final CustomNameplatesPluginImpl plugin; - private final String EXPANSION_FOLDER = "expansions/requirement"; - private final HashMap requirementBuilderMap; - - public RequirementManagerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - this.requirementBuilderMap = new HashMap<>(64); - this.registerInbuiltRequirements(); - } - - public void load() { - this.loadExpansions(); - } - - public void unload() { - - } - - public void reload() { - unload(); - load(); - } - - public void disable() { - this.requirementBuilderMap.clear(); - } - - /** - * 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. - */ - @Override - public boolean registerRequirement(String type, RequirementFactory requirementFactory) { - if (this.requirementBuilderMap.containsKey(type)) return false; - this.requirementBuilderMap.put(type, requirementFactory); - return true; - } - - /** - * 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. - */ - @Override - public boolean unregisterRequirement(String type) { - return this.requirementBuilderMap.remove(type) != null; - } - - private void registerInbuiltRequirements() { - this.registerTimeRequirement(); - this.registerYRequirement(); - this.registerContainRequirement(); - this.registerStartWithRequirement(); - this.registerEndWithRequirement(); - this.registerEqualsRequirement(); - this.registerBiomeRequirement(); - this.registerDateRequirement(); - this.registerPermissionRequirement(); - this.registerWorldRequirement(); - this.registerWeatherRequirement(); - this.registerGreaterThanRequirement(); - this.registerAndRequirement(); - this.registerOrRequirement(); - this.registerLevelRequirement(); - this.registerRandomRequirement(); - this.registerCoolDownRequirement(); - this.registerLessThanRequirement(); - this.registerNumberEqualRequirement(); - this.registerRegexRequirement(); - this.registerEnvironmentRequirement(); - this.registerPotionEffectRequirement(); - this.registerPapiRequirement(); - this.registerInListRequirement(); - this.registerGameModeRequirement(); - this.registerGeyserRequirement(); - this.registerDisguisedRequirement(); - this.registerDisguisedTypeRequirement(); - } - - /** - * 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 - */ - @NotNull - @Override - public Requirement[] getRequirements(ConfigurationSection section) { - List requirements = new ArrayList<>(); - if (section == null) { - return requirements.toArray(new Requirement[0]); - } - for (Map.Entry entry : section.getValues(false).entrySet()) { - String typeOrName = entry.getKey(); - if (hasRequirement(typeOrName)) { - requirements.add(getRequirement(typeOrName, entry.getValue())); - } else { - requirements.add(getRequirement(section.getConfigurationSection(typeOrName))); - } - } - return requirements.toArray(new Requirement[0]); - } - - public boolean hasRequirement(String type) { - return requirementBuilderMap.containsKey(type); - } - - /** - * Retrieves a Requirement object based on a configuration section and advanced flag. - * - * @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 - @Override - public Requirement getRequirement(ConfigurationSection section) { - if (section == null) return EmptyRequirement.instance; - String type = section.getString("type"); - if (type == null) { - LogUtils.warn("No requirement type found at " + section.getCurrentPath()); - return EmptyRequirement.instance; - } - var builder = getRequirementFactory(type); - if (builder == null) { - return EmptyRequirement.instance; - } - return builder.build(section.get("value")); - } - - /** - * Gets a requirement based on the provided key and value. - * If a valid RequirementFactory is found for the key, it is used to create the requirement. - * If no factory is found, a warning is logged, and an empty requirement instance is returned. - * - * @param type The key representing the requirement type. - * @param value The value associated with the requirement. - * @return A Requirement instance based on the key and value, or an empty requirement if not found. - */ - @Override - @NotNull - public Requirement getRequirement(String type, Object value) { - RequirementFactory factory = getRequirementFactory(type); - if (factory == null) { - LogUtils.warn("Requirement type: " + type + " doesn't exist."); - return EmptyRequirement.instance; - } - return factory.build(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. - */ - @Override - @Nullable - public RequirementFactory getRequirementFactory(String type) { - return requirementBuilderMap.get(type); - } - - private void registerTimeRequirement() { - registerRequirement("time", (args) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return condition -> { - long time = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getWorld().getTime(); - for (Pair pair : timePairs) - if (time >= pair.left() && time <= pair.right()) - return true; - return false; - }; - }); - } - - private void registerYRequirement() { - registerRequirement("ypos", (args) -> { - List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); - return condition -> { - int y = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation().getBlockY(); - for (Pair pair : timePairs) - if (y >= pair.left() && y <= pair.right()) - return true; - return false; - }; - }); - } - - private void registerOrRequirement() { - registerRequirement("||", (args) -> { - if (args instanceof ConfigurationSection section) { - Requirement[] requirements = getRequirements(section); - return condition -> { - for (Requirement requirement : requirements) { - if (requirement.isConditionMet(condition)) { - return true; - } - } - return false; - }; - } else { - LogUtils.warn("Wrong value format found at || requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerAndRequirement() { - registerRequirement("&&", (args) -> { - if (args instanceof ConfigurationSection section) { - Requirement[] requirements = getRequirements(section); - return condition -> { - outer: { - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(condition)) { - break outer; - } - } - return true; - } - return false; - }; - } else { - LogUtils.warn("Wrong value format found at && requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerLevelRequirement() { - registerRequirement("level", (args) -> { - int level = (int) args; - return condition -> { - int current = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLevel(); - return current >= level; - }; - }); - } - - private void registerRandomRequirement() { - registerRequirement("random", (args) -> { - double random = ConfigUtils.getDoubleValue(args); - return condition -> Math.random() < random; - }); - } - - private void registerGeyserRequirement() { - registerRequirement("geyser", (args) -> { - boolean arg = (boolean) args; - return condition -> { - if (arg) { - return GeyserUtils.isBedrockPlayer(condition.getOfflinePlayer().getUniqueId()); - } else { - return !GeyserUtils.isBedrockPlayer(condition.getOfflinePlayer().getUniqueId()); - } - }; - }); - } - - private void registerDisguisedRequirement() { - registerRequirement("self-disguised", (args) -> { - boolean arg = (boolean) args; - return condition -> { - if (!CNConfig.hasLibsDisguise) return true; - if (arg) { - return DisguiseUtils.isDisguised(condition.getOfflinePlayer().getPlayer()); - } else { - return !DisguiseUtils.isDisguised(condition.getOfflinePlayer().getPlayer()); - } - }; - }); - } - - private void registerBiomeRequirement() { - registerRequirement("biome", (args) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation()); - return biomes.contains(currentBiome); - }; - }); - registerRequirement("!biome", (args) -> { - HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - String currentBiome = SparrowHeart.getInstance().getBiomeResourceLocation(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation()); - return !biomes.contains(currentBiome); - }; - }); - } - - private void registerDisguisedTypeRequirement() { - registerRequirement("disguised-type", (args) -> { - HashSet types = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - if (!CNConfig.hasLibsDisguise) return true; - Player player = condition.getOfflinePlayer().getPlayer(); - if (!DisguiseUtils.isDisguised(player)) return false; - return types.contains(DisguiseUtils.getDisguisedType(player).name()); - }; - }); - registerRequirement("!disguised-type", (args) -> { - HashSet types = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - if (!CNConfig.hasLibsDisguise) return true; - Player player = condition.getOfflinePlayer().getPlayer(); - if (!DisguiseUtils.isDisguised(player)) return false; - return !types.contains(DisguiseUtils.getDisguisedType(player).name()); - }; - }); - } - - private void registerWorldRequirement() { - registerRequirement("world", (args) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> worlds.contains(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getWorld().getName()); - }); - registerRequirement("!world", (args) -> { - HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> !worlds.contains(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getWorld().getName()); - }); - } - - private void registerWeatherRequirement() { - registerRequirement("weather", (args) -> { - List weathers = ConfigUtils.stringListArgs(args); - return condition -> { - String currentWeather; - World world = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getWorld(); - if (world.isClearWeather()) currentWeather = "clear"; - else if (world.isThundering()) currentWeather = "thunder"; - else currentWeather = "rain"; - for (String weather : weathers) - if (weather.equalsIgnoreCase(currentWeather)) - return true; - return false; - }; - }); - } - - private void registerCoolDownRequirement() { - registerRequirement("cooldown", (args) -> { - if (args instanceof ConfigurationSection section) { - String key = section.getString("key"); - int time = section.getInt("time"); - return condition -> !plugin.getCoolDownManager().isCoolDown(condition.getOfflinePlayer().getUniqueId(), key, time); - } else { - LogUtils.warn("Wrong value format found at cooldown requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerDateRequirement() { - registerRequirement("date", (args) -> { - HashSet dates = new HashSet<>(ConfigUtils.stringListArgs(args)); - return condition -> { - Calendar calendar = Calendar.getInstance(); - String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); - return dates.contains(current); - }; - }); - } - - private void registerPermissionRequirement() { - registerRequirement("permission", (args) -> { - List perms = ConfigUtils.stringListArgs(args); - return condition -> { - for (String perm : perms) - if (Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).hasPermission(perm)) - return true; - return false; - }; - }); - registerRequirement("!permission", (args) -> { - List perms = ConfigUtils.stringListArgs(args); - return condition -> { - for (String perm : perms) - if (Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).hasPermission(perm)) - return false; - return true; - }; - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerGreaterThanRequirement() { - registerRequirement(">=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return Double.parseDouble(p1) >= Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at >= requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement(">", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return Double.parseDouble(p1) > Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at > requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerRegexRequirement() { - registerRequirement("regex", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("papi", ""); - String v2 = section.getString("regex", ""); - return condition -> PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1).matches(v2); - } else { - LogUtils.warn("Wrong value format found at regex requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerNumberEqualRequirement() { - registerRequirement("=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return Double.parseDouble(p1) == Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return Double.parseDouble(p1) != Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - @SuppressWarnings("DuplicatedCode") - private void registerLessThanRequirement() { - registerRequirement("<", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return Double.parseDouble(p1) < Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at < requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("<=", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return Double.parseDouble(p1) <= Double.parseDouble(p2); - }; - } else { - LogUtils.warn("Wrong value format found at <= requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerStartWithRequirement() { - registerRequirement("startsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return p1.startsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at startsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!startsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return !p1.startsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !startsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEndWithRequirement() { - registerRequirement("endsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return p1.endsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at endsWith requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!endsWith", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return !p1.endsWith(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !endsWith requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerContainRequirement() { - registerRequirement("contains", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return p1.contains(p2); - }; - } else { - LogUtils.warn("Wrong value format found at contains requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!contains", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return !p1.contains(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !contains requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEqualsRequirement() { - registerRequirement("equals", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return p1.equals(p2); - }; - } else { - LogUtils.warn("Wrong value format found at equals requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!equals", (args) -> { - if (args instanceof ConfigurationSection section) { - String v1 = section.getString("value1", ""); - String v2 = section.getString("value2", ""); - return condition -> { - String p1 = v1.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v1) : v1; - String p2 = v2.startsWith("%") ? PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), v2) : v2; - return !p1.equals(p2); - }; - } else { - LogUtils.warn("Wrong value format found at !equals requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerPapiRequirement() { - registerRequirement("papi-condition", (args) -> { - if (args instanceof ConfigurationSection section) { - return new PapiCondition(section.getValues(false)); - } else { - LogUtils.warn("Wrong value format found at papi-condition requirement."); - return EmptyRequirement.instance; - } - }); - } - - private void registerEnvironmentRequirement() { - registerRequirement("environment", (args) -> { - List environments = ConfigUtils.stringListArgs(args); - return condition -> { - var name = condition.getOfflinePlayer().getPlayer().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); - return environments.contains(name); - }; - }); - registerRequirement("!environment", (args) -> { - List environments = ConfigUtils.stringListArgs(args); - return condition -> { - var name = condition.getOfflinePlayer().getPlayer().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); - return !environments.contains(name); - }; - }); - } - - private void registerGameModeRequirement() { - registerRequirement("gamemode", (args) -> { - List modes = ConfigUtils.stringListArgs(args); - return condition -> { - var name = condition.getOfflinePlayer().getPlayer().getGameMode().name().toLowerCase(Locale.ENGLISH); - return modes.contains(name); - }; - }); - registerRequirement("!gamemode", (args) -> { - List modes = ConfigUtils.stringListArgs(args); - return condition -> { - var name = condition.getOfflinePlayer().getPlayer().getGameMode().name().toLowerCase(Locale.ENGLISH); - return !modes.contains(name); - }; - }); - } - - - private void registerPotionEffectRequirement() { - registerRequirement("potion-effect", (args) -> { - String potions = (String) args; - String[] split = potions.split("(<=|>=|<|>|==)", 2); - PotionEffectType type = PotionEffectType.getByName(split[0]); - if (type == null) { - LogUtils.warn("Potion effect doesn't exist: " + split[0]); - return EmptyRequirement.instance; - } - int required = Integer.parseInt(split[1]); - String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); - return condition -> { - int level = -1; - PotionEffect potionEffect = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getPotionEffect(type); - if (potionEffect != null) { - level = potionEffect.getAmplifier(); - } - boolean result = false; - switch (operator) { - case ">=" -> { - if (level >= required) result = true; - } - case ">" -> { - if (level > required) result = true; - } - case "==" -> { - if (level == required) result = true; - } - case "!=" -> { - if (level != required) result = true; - } - case "<=" -> { - if (level <= required) result = true; - } - case "<" -> { - if (level < required) result = true; - } - } - return result; - }; - }); - } - - private void registerInListRequirement() { - registerRequirement("in-list", (args) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - List values = ConfigUtils.stringListArgs(section.get("values")); - return condition -> { - String p1 = PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), papi); - return values.contains(p1); - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyRequirement.instance; - } - }); - registerRequirement("!in-list", (args) -> { - if (args instanceof ConfigurationSection section) { - String papi = section.getString("papi", ""); - List values = ConfigUtils.stringListArgs(section.get("values")); - return condition -> { - String p1 = PlaceholderAPI.setPlaceholders(condition.getOfflinePlayer(), papi); - return !values.contains(p1); - }; - } else { - LogUtils.warn("Wrong value format found at in-list requirement."); - return EmptyRequirement.instance; - } - }); - } - - /** - * Loads requirement expansions from external JAR files located in the expansion folder. - * Each expansion JAR should contain classes that extends the RequirementExpansion class. - * Expansions are registered and used to create custom requirements. - * If an error occurs while loading or initializing an expansion, a warning message is logged. - */ - @SuppressWarnings("ResultOfMethodCallIgnored") - private void loadExpansions() { - File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); - if (!expansionFolder.exists()) - expansionFolder.mkdirs(); - - List> classes = new ArrayList<>(); - File[] expansionJars = expansionFolder.listFiles(); - if (expansionJars == null) return; - for (File expansionJar : expansionJars) { - if (expansionJar.getName().endsWith(".jar")) { - try { - Class expansionClass = ClassUtils.findClass(expansionJar, RequirementExpansion.class); - classes.add(expansionClass); - } catch (IOException | ClassNotFoundException e) { - LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); - } - } - } - try { - for (Class expansionClass : classes) { - RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); - unregisterRequirement(expansion.getRequirementType()); - registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory()); - LogUtils.info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - LogUtils.warn("Error occurred when creating expansion instance.", e); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/ExpressionAnd.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/ExpressionAnd.java deleted file mode 100644 index e6db1bf..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/ExpressionAnd.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import org.bukkit.OfflinePlayer; - -import java.util.List; - -public record ExpressionAnd(List requirements) implements PapiRequirement { - - @Override - public boolean isMet(OfflinePlayer player) { - for (PapiRequirement requirement : requirements) { - if (!requirement.isMet(player)) return false; - } - return true; - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiCondition.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiCondition.java deleted file mode 100644 index d0581e0..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiCondition.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import net.momirealms.customnameplates.api.requirement.Condition; -import net.momirealms.customnameplates.api.requirement.Requirement; -import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.MemorySection; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class PapiCondition implements Requirement { - - private final List papiRequirement; - - public PapiCondition(Map expressions){ - papiRequirement = getRequirements(expressions); - } - - @Override - public boolean isConditionMet(Condition condition) { - final OfflinePlayer player = condition.getOfflinePlayer(); - for (PapiRequirement requirement : papiRequirement) { - if (!requirement.isMet(player)) { - return false; - } - } - return true; - } - - private List getRequirements(Map map) { - List papiRequirements = new ArrayList<>(); - map.keySet().forEach(key -> { - if (key.startsWith("&&")) { - if (map.get(key) instanceof MemorySection map2) { - papiRequirements.add(new ExpressionAnd(getRequirements(map2.getValues(false)))); - } - } else if (key.startsWith("||")) { - if (map.get(key) instanceof MemorySection map2) { - papiRequirements.add(new ExpressionOr(getRequirements(map2.getValues(false)))); - } - } else { - if (map.get(key) instanceof MemorySection map2) { - String type = map2.getString("type"); - String papi = map2.getString("papi"); - String value = map2.getString("value"); - if (value == null || papi == null || type == null) return; - switch (type){ - case "==" -> papiRequirements.add(new PapiEquals(papi, value)); - case "!=" -> papiRequirements.add(new PapiNotEquals(papi, value)); - case ">=" -> papiRequirements.add(new PapiNoLess(papi, value)); - case "<=" -> papiRequirements.add(new PapiNoLarger(papi, value)); - case "<" -> papiRequirements.add(new PapiSmaller(papi, value)); - case ">" -> papiRequirements.add(new PapiGreater(papi, value)); - case "regex" -> papiRequirements.add(new PapiRegex(papi, value)); - } - } - } - }); - return papiRequirements; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiGreater.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiGreater.java deleted file mode 100644 index d9d9323..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiGreater.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -public record PapiGreater(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(OfflinePlayer player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value > Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNoLarger.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNoLarger.java deleted file mode 100644 index 9fdffb4..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNoLarger.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -public record PapiNoLarger(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(OfflinePlayer player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value <= Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNoLess.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNoLess.java deleted file mode 100644 index bf4423c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNoLess.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -public record PapiNoLess(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(OfflinePlayer player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value >= Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNotEquals.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNotEquals.java deleted file mode 100644 index a386b3c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiNotEquals.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -import java.util.Objects; - -public record PapiNotEquals(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(OfflinePlayer player) { - String value = PlaceholderAPI.setPlaceholders(player, papi); - return !Objects.equals(value, PlaceholderAPI.setPlaceholders(player, requirement)); - } -} \ No newline at end of file diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiRegex.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiRegex.java deleted file mode 100644 index 0134675..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiRegex.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -public record PapiRegex(String papi, String regex) implements PapiRequirement { - - @Override - public boolean isMet(OfflinePlayer player) { - return PlaceholderAPI.setPlaceholders(player, papi).matches(regex); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiSmaller.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiSmaller.java deleted file mode 100644 index 8585129..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/requirement/papi/PapiSmaller.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.OfflinePlayer; - -public record PapiSmaller(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(OfflinePlayer player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value < Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/TeamManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/TeamManagerImpl.java deleted file mode 100644 index 048677f..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/TeamManagerImpl.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.team; - -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.events.PacketAdapter; -import com.google.common.io.ByteArrayDataInput; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.manager.TeamManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.message.MessageType; -import net.momirealms.customnameplates.common.team.TeamTagVisibility; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; -import net.momirealms.customnameplates.paper.mechanic.team.provider.*; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import net.momirealms.sparrow.heart.SparrowHeart; -import net.momirealms.sparrow.heart.feature.team.TeamCollisionRule; -import net.momirealms.sparrow.heart.feature.team.TeamColor; -import net.momirealms.sparrow.heart.feature.team.TeamVisibility; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.plugin.messaging.PluginMessageListener; -import org.bukkit.scoreboard.Scoreboard; -import org.bukkit.scoreboard.Team; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; -import java.util.Collections; -import java.util.Objects; -import java.util.Optional; - -public class TeamManagerImpl implements TeamManager, PluginMessageListener { - - private final CustomNameplatesPlugin plugin; - private TeamProvider teamProvider; - private static final String CHANNEL = "customnameplates:cnp"; - - public TeamManagerImpl(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - } - - /** - * This method would only be called when CustomNameplates manage the team - * - * @param player player - */ - @Override - public void createTeam(Player player) { - if (CNConfig.disableTeamManage) return; - if (CNConfig.isOtherTeamPluginHooked()) return; - String team = teamProvider.getTeam(player, null); - if (!team.equals("CNP_" + player.getName())) return; -// if (CNConfig.createRealTeam) { - Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); - Team playerTeam = scoreboard.getTeam(team); - if (playerTeam == null) { - playerTeam = scoreboard.registerNewTeam(team); - } - playerTeam.addPlayer(player); -// } else { -// for (Player online : Bukkit.getOnlinePlayers()) { -// SparrowHeart.getInstance().addClientSideTeam(online, team, -// Collections.singletonList(player.getName()), -// "{\"text\":\"\"}", -// "{\"text\":\"\"}", -// "{\"text\":\"\"}", -// TeamVisibility.ALWAYS, -// TeamVisibility.ALWAYS, -// TeamCollisionRule.ALWAYS, -// TeamColor.WHITE, -// false, -// false -// ); -// if (online == player) continue; -// String onlineTeam = teamProvider.getTeam(online, null); -// SparrowHeart.getInstance().addClientSideTeam(player, onlineTeam, -// Collections.singletonList(online.getName()), -// "{\"text\":\"\"}", -// "{\"text\":\"\"}", -// "{\"text\":\"\"}", -// TeamVisibility.ALWAYS, -// TeamVisibility.ALWAYS, -// TeamCollisionRule.ALWAYS, -// TeamColor.WHITE, -// false, -// false -// ); -// } -// } - } - - /** - * This method would only be called when CustomNameplates manage the team - * - * @param player player - */ - @Override - public void removeTeam(Player player) { - if (CNConfig.disableTeamManage) return; - if (CNConfig.isOtherTeamPluginHooked()) return; - String team = teamProvider.getTeam(player, null); - // If the team is created by other plugins - if (!team.equals("CNP_" + player.getName())) return; -// if (CNConfig.createRealTeam) { - Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); - Optional.ofNullable(scoreboard.getTeam(team)).ifPresent(Team::unregister); -// } else { -// for (Player online : Bukkit.getOnlinePlayers()) { -// if (player == online) continue; -// SparrowHeart.getInstance().removeClientSideTeam(online, team); -// } -// } - } - - @Override - public void updateTeam(Player owner, Player viewer, String prefix, String suffix, net.momirealms.customnameplates.common.team.TeamColor color, TeamTagVisibility visibility) { - if (CNConfig.disableTeamManage) return; - String team = teamProvider.getTeam(owner, viewer); - if (team == null) { - LogUtils.warn("Failed to get player " + owner.getName() + "'s team for viewer " + viewer.getName()); - return; - } - if (color == net.momirealms.customnameplates.common.team.TeamColor.NONE || color == net.momirealms.customnameplates.common.team.TeamColor.CUSTOM) - color = net.momirealms.customnameplates.common.team.TeamColor.WHITE; - if (plugin.getNameplateManager().isProxyMode()) { - this.sendPluginMessage( - MessageType.UPDATE, - owner.getName(), - viewer.getName(), - prefix, - suffix, - color.name(), - visibility.name() - ); - } else { - SparrowHeart.getInstance().updateClientSideTeam( - viewer, team, - "{\"text\":\"\"}", - AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(prefix), - AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(suffix), - TeamVisibility.valueOf(visibility.name()), - TeamVisibility.ALWAYS, - TeamCollisionRule.ALWAYS, - TeamColor.valueOf(color.name()), - false, - false - ); - } - } - - public void reload() { - unload(); - load(); - } - - public void unload() { - if (teamProvider instanceof Listener listener) { - HandlerList.unregisterAll(listener); - } - if (teamProvider instanceof PacketAdapter adapter) { - ProtocolLibrary.getProtocolManager().removePacketListener(adapter); - } - Bukkit.getServer().getMessenger().unregisterIncomingPluginChannel(plugin, CHANNEL); - Bukkit.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin, CHANNEL); - } - - public void load() { - if (CNConfig.tabTeam) { - teamProvider = new TABProvider(); - } else if (CNConfig.cmiTeam) { - teamProvider = new CMIProvider(); - } else if (CNConfig.unknownTeam) { - teamProvider = new UnknownProvider(); - } else { - teamProvider = new DefaultProvider(); - } - if (teamProvider instanceof Listener listener) { - Bukkit.getPluginManager().registerEvents(listener, plugin); - } - if (teamProvider instanceof PacketAdapter adapter) { - ProtocolLibrary.getProtocolManager().addPacketListener(adapter); - } - Bukkit.getServer().getMessenger().registerOutgoingPluginChannel(plugin, CHANNEL); - Bukkit.getServer().getMessenger().registerIncomingPluginChannel(plugin, CHANNEL, this); - } - - @Override - public String getTeamName(Player player, Player viewer) { - return teamProvider.getTeam(player, viewer); - } - - private void handleMessage(String... message) { - - } - - @Override - @SuppressWarnings("UnstableApiUsage") - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) { - if (!Objects.equals(CHANNEL, channel)) { - return; - } - ByteArrayDataInput dataInput = ByteStreams.newDataInput(message); - byte args = dataInput.readByte(); - String[] messages = new String[args]; - for (int i = 0; i < args; i++) { - messages[i] = dataInput.readUTF(); - } - handleMessage(messages); - } - - @SuppressWarnings("UnstableApiUsage") - public void sendPluginMessage(String... messages) { - ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput(); - dataOutput.writeByte(messages.length); - for (String message : messages) { - dataOutput.writeUTF(message); - } - Bukkit.getOnlinePlayers().stream().findAny().ifPresent(player -> { - player.sendPluginMessage(plugin, CHANNEL, dataOutput.toByteArray()); - }); - } - - public void disable() { - unload(); - if (CNConfig.disableTeamManage) return; - if (CNConfig.isOtherTeamPluginHooked()) return; - for (Player player : Bukkit.getOnlinePlayers()) { - removeTeam(player); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/TABProvider.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/TABProvider.java deleted file mode 100644 index 7ca3148..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/TABProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.team.provider; - -import me.neznamy.tab.api.TabAPI; -import me.neznamy.tab.api.TabPlayer; -import me.neznamy.tab.api.tablist.SortingManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import org.bukkit.entity.Player; - -public class TABProvider implements TeamProvider { - - private final TabAPI api; - private final SortingManager sortingManager; - - public TABProvider() { - this.api = TabAPI.getInstance(); - this.sortingManager = api.getSortingManager(); - if (sortingManager == null) { - LogUtils.warn("Detected that team management is disabled in TAB. Using player name as team name."); - } - } - - @Override - public String getTeam(Player player, Player ignore) { - TabPlayer tabPlayer = api.getPlayer(player.getUniqueId()); - if (tabPlayer == null) { - return null; - } - String forced = sortingManager.getForcedTeamName(tabPlayer); - if (forced != null) { - return forced; - } - return sortingManager.getOriginalTeamName(tabPlayer); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/UnknownProvider.java b/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/UnknownProvider.java deleted file mode 100644 index ac93a4c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/mechanic/team/provider/UnknownProvider.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.mechanic.team.provider; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scoreboard.Scoreboard; -import org.bukkit.scoreboard.Team; - -import java.util.*; - -public class UnknownProvider extends PacketAdapter implements TeamProvider, Listener { - - private final HashMap teamViewMap; - - public UnknownProvider() { - super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGHEST, PacketType.Play.Server.SCOREBOARD_TEAM); - this.teamViewMap = new HashMap<>(); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - teamViewMap.put(event.getPlayer().getUniqueId(), new PlayerTeamView()); - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - teamViewMap.remove(event.getPlayer().getUniqueId()); - } - - @Override - public void onPacketSending(PacketEvent event) { - Player receiver = event.getPlayer(); - PlayerTeamView view = teamViewMap.get(receiver.getUniqueId()); - if (view == null) { - return; - } - - PacketContainer packet = event.getPacket(); - String team = (String) packet.getModifier().read(1); - switch ((int) packet.getModifier().read(0)) { - case 0, 3 -> view.addMembers(team, (Collection) packet.getModifier().read(2)); - case 1 -> view.removeTeam(team); - case 4 -> view.removeMembers(team, (Collection) packet.getModifier().read(2)); - default -> { - // ignore 2 update team info - } - } - } - - @Override - public String getTeam(Player player, Player viewer) { - PlayerTeamView view = teamViewMap.get(viewer.getUniqueId()); - if (view != null) { - String team = view.getTeam(player); - if (team != null) - return team; - } - - // should not reach here - CustomNameplatesPlugin.get().debug("Getting real teams for " + player.getName() + " viewer: " + viewer.getName()); - Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); - Team team = scoreboard.getPlayerTeam(player); - if (team == null) { - return null; - } - return team.getName(); - } - - public static class PlayerTeamView { - - private final HashMap playerToTeamMap; - private final HashMap teamToPlayerMap; - - public PlayerTeamView() { - this.playerToTeamMap = new HashMap<>(); - this.teamToPlayerMap = new HashMap<>(); - } - - public String getTeam(Player player) { - return playerToTeamMap.get(player.getName()); - } - - public void removeTeam(String team) { - String[] removedMembers = teamToPlayerMap.remove(team); - if (removedMembers != null) { - for (String member : removedMembers) { - playerToTeamMap.remove(member); - } - } - } - - public void addMembers(String team, Collection members) { - String[] oldMembers = teamToPlayerMap.remove(team); - if (oldMembers != null) { - String[] newMembers = new String[members.size() + oldMembers.length]; - int i = 0; - for (String m : oldMembers) { - newMembers[i] = m; - i++; - } - for (String m : members) { - newMembers[i] = m; - i++; - } - teamToPlayerMap.put(team, newMembers); - for (String member : newMembers) { - playerToTeamMap.put(member, team); - } - } else { - teamToPlayerMap.put(team, members.toArray(new String[0])); - for (String member : members) { - playerToTeamMap.put(member, team); - } - } - } - - public void removeMembers(String team, Collection members) { - String[] oldMembers = teamToPlayerMap.remove(team); - if (oldMembers != null) { - ArrayList newMembers = new ArrayList<>(Arrays.asList(oldMembers)); - for (String member : members) { - playerToTeamMap.remove(member); - newMembers.remove(member); - } - teamToPlayerMap.put(team, newMembers.toArray(new String[0])); - } - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/BukkitSchedulerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/BukkitSchedulerImpl.java deleted file mode 100644 index bce03bf..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/BukkitSchedulerImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.scheduler; - -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.scheduler.BukkitTask; - -/** - * A scheduler implementation for synchronous tasks using Bukkit's Scheduler. - */ -public class BukkitSchedulerImpl implements SyncScheduler { - - private final CustomNameplatesPluginImpl plugin; - - public BukkitSchedulerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - } - - /** - * Runs a synchronous task on the main server thread using Bukkit's Scheduler. - * If already on the main thread, the task is executed immediately. - * - * @param runnable The task to run. - * @param location The location associated with the task. - */ - @Override - public void runSyncTask(Runnable runnable, Location location) { - if (Bukkit.isPrimaryThread()) - runnable.run(); - else - Bukkit.getScheduler().runTask(plugin, runnable); - } - - /** - * Runs a synchronous task repeatedly with a specified delay and period using Bukkit's Scheduler. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay in ticks before the first execution. - * @param period The period between subsequent executions in ticks. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { - return new BukkitCancellableTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period)); - } - - /** - * Runs a synchronous task with a specified delay using Bukkit's Scheduler. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay in ticks before the task execution. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { - return new BukkitCancellableTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); - } - - /** - * Represents a scheduled task using Bukkit's Scheduler that can be cancelled. - */ - public static class BukkitCancellableTask implements CancellableTask { - - private final BukkitTask bukkitTask; - - public BukkitCancellableTask(BukkitTask bukkitTask) { - this.bukkitTask = bukkitTask; - } - - @Override - public void cancel() { - this.bukkitTask.cancel(); - } - - @Override - public boolean isCancelled() { - return this.bukkitTask.isCancelled(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/FoliaSchedulerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/FoliaSchedulerImpl.java deleted file mode 100644 index 535c83b..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/FoliaSchedulerImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.scheduler; - -import io.papermc.paper.threadedregions.scheduler.ScheduledTask; -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import org.bukkit.Bukkit; -import org.bukkit.Location; - -/** - * A scheduler implementation for "synchronous" tasks using Folia's RegionScheduler. - */ -public class FoliaSchedulerImpl implements SyncScheduler { - - private final CustomNameplatesPluginImpl plugin; - - public FoliaSchedulerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - } - - /** - * Runs a "synchronous" task on the region thread using Folia's RegionScheduler. - * - * @param runnable The task to run. - * @param location The location associated with the task. - */ - @Override - public void runSyncTask(Runnable runnable, Location location) { - if (location == null) { - Bukkit.getGlobalRegionScheduler().execute(plugin, runnable); - } else { - Bukkit.getRegionScheduler().execute(plugin, location, runnable); - } - } - - /** - * Runs a "synchronous" task repeatedly with a specified delay and period using Folia's RegionScheduler. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay in ticks before the first execution. - * @param period The period between subsequent executions in ticks. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, (scheduledTask -> runnable.run()), delay, period)); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, location, (scheduledTask -> runnable.run()), delay, period)); - } - - /** - * Runs a "synchronous" task with a specified delay using Folia's RegionScheduler. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay in ticks before the task execution. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { - if (delay == 0) { - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().run(plugin, (scheduledTask -> runnable.run()))); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().run(plugin, location, (scheduledTask -> runnable.run()))); - } - if (location == null) { - return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, (scheduledTask -> runnable.run()), delay)); - } - return new FoliaCancellableTask(Bukkit.getRegionScheduler().runDelayed(plugin, location, (scheduledTask -> runnable.run()), delay)); - } - - /** - * Represents a scheduled task using Folia's RegionScheduler that can be cancelled. - */ - public static class FoliaCancellableTask implements CancellableTask { - - private final ScheduledTask scheduledTask; - - public FoliaCancellableTask(ScheduledTask scheduledTask) { - this.scheduledTask = scheduledTask; - } - - @Override - public void cancel() { - this.scheduledTask.cancel(); - } - - @Override - public boolean isCancelled() { - return this.scheduledTask.isCancelled(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/SchedulerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/SchedulerImpl.java deleted file mode 100644 index 4403b36..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/SchedulerImpl.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.scheduler; - -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import net.momirealms.customnameplates.api.scheduler.Scheduler; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.setting.CNConfig; -import org.bukkit.Location; - -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * A scheduler implementation responsible for scheduling and managing tasks in a multi-threaded environment. - */ -public class SchedulerImpl implements Scheduler { - - private final SyncScheduler syncScheduler; - private final ScheduledThreadPoolExecutor schedule; - private final CustomNameplatesPluginImpl plugin; - - public SchedulerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - this.syncScheduler = plugin.getVersionManager().isFolia() ? - new FoliaSchedulerImpl(plugin) : new BukkitSchedulerImpl(plugin); - this.schedule = new ScheduledThreadPoolExecutor(1); - this.schedule.setMaximumPoolSize(1); - this.schedule.setKeepAliveTime(30, TimeUnit.SECONDS); - this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); - } - - /** - * Reloads the scheduler configuration based on settings. - */ - public void reload() { - try { - this.schedule.setMaximumPoolSize(CNConfig.maximumPoolSize); - this.schedule.setCorePoolSize(CNConfig.corePoolSize); - this.schedule.setKeepAliveTime(CNConfig.keepAliveTime, TimeUnit.SECONDS); - } catch (IllegalArgumentException e) { - plugin.getLogger().severe("Failed to create thread pool. Please lower the corePoolSize in config.yml."); - } - } - - /** - * Shuts down the scheduler. - */ - public void shutdown() { - if (this.schedule != null && !this.schedule.isShutdown()) - this.schedule.shutdown(); - } - - /** - * Runs a task synchronously on the main server thread or region thread. - * - * @param runnable The task to run. - * @param location The location associated with the task. - */ - @Override - public void runTaskSync(Runnable runnable, Location location) { - this.syncScheduler.runSyncTask(runnable, location); - } - - /** - * Runs a task asynchronously. - * - * @param runnable The task to run. - */ - @Override - public void runTaskAsync(Runnable runnable) { - this.schedule.execute(runnable); - } - - /** - * Runs a task synchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the first execution. - * @param periodTicks The period between subsequent executions in ticks. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks) { - return this.syncScheduler.runTaskSyncTimer(runnable, location, delayTicks, periodTicks); - } - - /** - * Runs a task asynchronously with a specified delay. - * - * @param runnable The task to run. - * @param delay The delay before the task execution. - * @param timeUnit The time unit for the delay. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit) { - return new ScheduledTask(schedule.schedule(runnable, delay, timeUnit)); - } - - /** - * Runs a task synchronously with a specified delay. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delay The delay before the task execution. - * @param timeUnit The time unit for the delay. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit) { - return new ScheduledTask(schedule.schedule(() -> { - runTaskSync(runnable, location); - }, delay, timeUnit)); - } - - /** - * Runs a task synchronously with a specified delay in ticks. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the task execution. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks) { - return this.syncScheduler.runTaskSyncLater(runnable, location, delayTicks); - } - - /** - * Runs a task asynchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param delay The delay before the first execution. - * @param period The period between subsequent executions. - * @param timeUnit The time unit for the delay and period. - * @return A CancellableTask for managing the scheduled task. - */ - @Override - public CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit) { - return new ScheduledTask(schedule.scheduleAtFixedRate(runnable, delay, period, timeUnit)); - } - - /** - * Represents a thread-pool task that can be cancelled. - */ - public static class ScheduledTask implements CancellableTask { - - private final ScheduledFuture scheduledFuture; - - public ScheduledTask(ScheduledFuture scheduledFuture) { - this.scheduledFuture = scheduledFuture; - } - - @Override - public void cancel() { - this.scheduledFuture.cancel(false); - } - - @Override - public boolean isCancelled() { - return this.scheduledFuture.isCancelled(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/SyncScheduler.java b/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/SyncScheduler.java deleted file mode 100644 index b6a5864..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/scheduler/SyncScheduler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.scheduler; - -import net.momirealms.customnameplates.api.scheduler.CancellableTask; -import org.bukkit.Location; - -public interface SyncScheduler { - - /** - * Runs a task synchronously on the main server thread or region thread. - * - * @param runnable The task to run. - * @param location The location associated with the task. - */ - void runSyncTask(Runnable runnable, Location location); - - /** - * Runs a task synchronously with a specified delay and period. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the first execution. - * @param periodTicks The period between subsequent executions in ticks. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); - - /** - * Runs a task synchronously with a specified delay in ticks. - * - * @param runnable The task to run. - * @param location The location associated with the task. - * @param delayTicks The delay in ticks before the task execution. - * @return A CancellableTask for managing the scheduled task. - */ - CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/setting/CNConfig.java b/paper/src/main/java/net/momirealms/customnameplates/paper/setting/CNConfig.java deleted file mode 100644 index 2237b0c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/setting/CNConfig.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.setting; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -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 net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger; -import net.momirealms.customnameplates.api.util.FontUtils; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.mechanic.bossbar.BarColor; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.Locale; -import java.util.Objects; - -public class CNConfig { - - public static String configVersion = "27"; - public static int cacheSize; - public static int corePoolSize; - public static long keepAliveTime; - public static int maximumPoolSize; - public static boolean debug; - public static String language; - public static boolean updateChecker; - public static boolean metrics; - public static boolean legacyColorSupport; - public static boolean generatePackOnStart; - public static int sendDelay; - public static String namespace; - public static String font; - public static char initChar; - public static boolean copyPackIA; - public static boolean copyPackIAOld; - public static boolean copyPackOraxen; - public static boolean trChatChannel; - public static boolean ventureChatChannel; - public static boolean nameplateModule; - public static boolean bossBarModule; - public static boolean actionBarModule; - public static boolean bubbleModule; - public static boolean backgroundModule; - public static boolean imageModule; - public static boolean tabTeam; - public static boolean cmiTeam; - public static String folderNameplate; - public static String folderImage; - public static String folderBubble; - public static String folderBackground; - public static String folderSplit; - public static boolean legacyUnicodes; - public static BarColor barColorToRemove; - public static boolean legacyBossBarImage; - public static boolean newBossBarImage; - public static boolean hideScoreboardNumber; - public static boolean animatedImage; - public static boolean textEffects; - public static boolean disableTeamManage; - public static boolean velocitab; - public static boolean unknownTeam; - public static boolean createRealTeam; - public static boolean enableShader; - public static boolean huskChatChannel; - public static boolean carbonChatChannel; - public static boolean advancedChatChannel; - public static boolean hasLibsDisguise; - public static boolean catchOtherActionBar; - - public static void load() { - try { - YamlDocument.create( - new File(CustomNameplatesPlugin.getInstance().getDataFolder(), "config.yml"), - Objects.requireNonNull(CustomNameplatesPlugin.getInstance().getResource("config.yml")), - GeneralSettings.DEFAULT, - LoaderSettings - .builder() - .setAutoUpdate(true) - .build(), - DumperSettings.DEFAULT, - UpdaterSettings - .builder() - .setVersioning(new BasicVersioning("config-version")) - .build() - ); - loadSettings(CustomNameplatesPlugin.getInstance().getConfig("config.yml")); - } catch (IOException e) { - LogUtils.warn(e.getMessage()); - } - } - - private static void loadSettings(YamlConfiguration config) { - debug = config.getBoolean("debug", false); - - language = config.getString("lang", "english"); - updateChecker = config.getBoolean("update-checker", true); - metrics = config.getBoolean("metrics"); - - ConfigurationSection moduleSection = config.getConfigurationSection("modules"); - if (moduleSection != null) { - nameplateModule = moduleSection.getBoolean("nameplates"); - bossBarModule = moduleSection.getBoolean("bossbars"); - actionBarModule = moduleSection.getBoolean("actionbars"); - bubbleModule = moduleSection.getBoolean("bubbles"); - backgroundModule = moduleSection.getBoolean("backgrounds"); - imageModule = moduleSection.getBoolean("images"); - } - - ConfigurationSection integrationSection = config.getConfigurationSection("integrations"); - if (integrationSection != null) { - copyPackIA = integrationSection.getBoolean("resource-pack.ItemsAdder", false); - copyPackIAOld = integrationSection.getBoolean("resource-pack.ItemsAdder-old-method", false); - copyPackOraxen = integrationSection.getBoolean("resource-pack.Oraxen", false); - trChatChannel = integrationSection.getBoolean("chat.TrChat", false); - ventureChatChannel = integrationSection.getBoolean("chat.VentureChat", false); - huskChatChannel = integrationSection.getBoolean("chat.HuskChat", false); - carbonChatChannel = integrationSection.getBoolean("chat.CarbonChat", false); - advancedChatChannel = integrationSection.getBoolean("chat.AdvancedChat", false); - tabTeam = integrationSection.getBoolean("team.TAB", false); - cmiTeam = integrationSection.getBoolean("team.CMI", false); - velocitab = integrationSection.getBoolean("team.Velocitab", false); - unknownTeam = integrationSection.getBoolean("team.unknown", false); - } - - ConfigurationSection packSection = config.getConfigurationSection("resource-pack"); - if (packSection != null) { - generatePackOnStart = !packSection.getBoolean("disable-generation-on-start", false); - namespace = packSection.getString("namespace", "nameplates"); - font = packSection.getString("font", "default"); - FontUtils.setNameSpaceAndFont(namespace, font); - - initChar = packSection.getString("initial-char", "뀁").charAt(0); - CharacterArranger.reset(initChar); - - folderNameplate = packSection.getString("image-path.nameplates","font\\nameplates\\"); - folderBubble = packSection.getString("image-path.bubbles","font\\bubbles\\"); - folderBackground = packSection.getString("image-path.backgrounds","font\\backgrounds\\"); - folderImage = packSection.getString("image-path.images","font\\images\\"); - folderSplit = packSection.getString("image-path.space-split","font\\base\\"); - - barColorToRemove = BarColor.valueOf(packSection.getString("transparent-bossbar.color", "yellow").toUpperCase(Locale.ENGLISH)); - legacyBossBarImage = packSection.getBoolean("transparent-bossbar.1_17-1_20_1", true); - newBossBarImage = packSection.getBoolean("transparent-bossbar.1_20_2+", true); - - legacyUnicodes = packSection.getBoolean("legacy-unicodes", true); - - enableShader = packSection.getBoolean("shader.enable", true); - hideScoreboardNumber = packSection.getBoolean("shader.hide-scoreboard-number", false); - animatedImage = packSection.getBoolean("shader.animated-text", false); - textEffects = packSection.getBoolean("shader.ItemsAdder-text-effects", false); - } - - disableTeamManage = config.getBoolean("other-settings.disable-team-management", false); - corePoolSize = config.getInt("other-settings.thread-pool-settings.corePoolSize", 10); - maximumPoolSize = config.getInt("other-settings.thread-pool-settings.maximumPoolSize", 10); - keepAliveTime = config.getInt("other-settings.thread-pool-settings.keepAliveTime", 30); - cacheSize = config.getInt("other-settings.cache-size", 100); - - legacyColorSupport = config.getBoolean("other-settings.legacy-color-code-support"); - createRealTeam = config.getBoolean("other-settings.create-real-teams", false); - - hasLibsDisguise = Bukkit.getPluginManager().getPlugin("LibsDisguises") != null; - - catchOtherActionBar = config.getBoolean("other-settings.catch-other-plugin-actionbar", true); - - sendDelay = config.getInt("other-settings.send-delay", 0); - } - - public static boolean isOtherTeamPluginHooked() { - return tabTeam || cmiTeam || unknownTeam; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/setting/CNLocale.java b/paper/src/main/java/net/momirealms/customnameplates/paper/setting/CNLocale.java deleted file mode 100644 index 243467c..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/setting/CNLocale.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.setting; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -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 net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.util.LogUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -public class CNLocale { - - public static String MSG_RELOAD; - public static String MSG_PREFIX; - public static String MSG_PREVIEW_COOLDOWN; - public static String MSG_PREVIEW_START; - public static String MSG_GENERATING; - public static String MSG_NO_NAMEPLATE; - public static String MSG_PACK_GENERATED; - public static String MSG_EQUIP_NAMEPLATE; - public static String MSG_UNEQUIP_NAMEPLATE; - public static String MSG_FORCE_EQUIP_NAMEPLATE; - public static String MSG_FORCE_UNEQUIP_NAMEPLATE; - public static String MSG_NAMEPLATE_NOT_EXISTS; - public static String MSG_NAMEPLATE_NOT_AVAILABLE; - public static String MSG_AVAILABLE_NAMEPLATE; - public static String MSG_HAVE_NO_NAMEPLATE; - public static String MSG_FORCE_PREVIEW; - public static String MSG_EQUIP_BUBBLE; - public static String MSG_UNEQUIP_BUBBLE; - public static String MSG_FORCE_EQUIP_BUBBLE; - public static String MSG_FORCE_UNEQUIP_BUBBLE; - public static String MSG_BUBBLE_NOT_EXIST; - public static String MSG_BUBBLE_NOT_AVAILABLE; - public static String MSG_AVAILABLE_BUBBLE; - public static String MSG_HAVE_NO_BUBBLE; - - public static void load() { - InputStream inputStream = CustomNameplatesPlugin.getInstance().getResource("messages/" + CNConfig.language + ".yml"); - if (inputStream != null) { - try { - YamlDocument.create( - new File(CustomNameplatesPlugin.getInstance().getDataFolder(), "messages/" + CNConfig.language + ".yml"), - inputStream, - GeneralSettings.DEFAULT, - LoaderSettings - .builder() - .setAutoUpdate(true) - .build(), - DumperSettings.DEFAULT, - UpdaterSettings - .builder() - .setVersioning(new BasicVersioning("config-version")) - .build() - ); - inputStream.close(); - } catch (IOException e) { - LogUtils.warn(e.getMessage()); - } - } - loadSettings(CustomNameplatesPlugin.get().getConfig("messages/" + CNConfig.language + ".yml")); - } - - public static void loadSettings(YamlConfiguration config) { - ConfigurationSection section = config.getConfigurationSection("messages"); - if (section != null) { - MSG_RELOAD = section.getString("reload"); - MSG_PREFIX = section.getString("prefix"); - MSG_PREVIEW_COOLDOWN = config.getString("messages.cooldown"); - MSG_PREVIEW_START = config.getString("messages.preview"); - MSG_GENERATING = config.getString("messages.generate"); - MSG_PACK_GENERATED = config.getString("messages.generate-done"); - MSG_NO_NAMEPLATE = config.getString("messages.no-nameplate"); - MSG_EQUIP_NAMEPLATE = config.getString("messages.equip-nameplates"); - MSG_UNEQUIP_NAMEPLATE = config.getString("messages.unequip-nameplates"); - MSG_FORCE_EQUIP_NAMEPLATE = config.getString("messages.force-equip-nameplates"); - MSG_FORCE_UNEQUIP_NAMEPLATE = config.getString("messages.force-unequip-nameplates"); - MSG_NAMEPLATE_NOT_EXISTS = config.getString("messages.not-exist-nameplates"); - MSG_NAMEPLATE_NOT_AVAILABLE = config.getString("messages.not-available-nameplates"); - MSG_AVAILABLE_NAMEPLATE = config.getString("messages.available-nameplates"); - MSG_HAVE_NO_NAMEPLATE = config.getString("messages.have-no-nameplates"); - MSG_FORCE_PREVIEW = config.getString("messages.force-preview"); - MSG_EQUIP_BUBBLE = config.getString("messages.equip-bubbles"); - MSG_UNEQUIP_BUBBLE = config.getString("messages.unequip-bubbles"); - MSG_FORCE_EQUIP_BUBBLE = config.getString("messages.force-equip-bubbles"); - MSG_FORCE_UNEQUIP_BUBBLE = config.getString("messages.force-unequip-bubbles"); - MSG_BUBBLE_NOT_EXIST = config.getString("messages.not-exist-bubbles"); - MSG_BUBBLE_NOT_AVAILABLE = config.getString("messages.not-available-bubbles"); - MSG_AVAILABLE_BUBBLE = config.getString("messages.available-bubbles"); - MSG_HAVE_NO_BUBBLE = config.getString("messages.have-no-bubbles"); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/StorageManagerImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/StorageManagerImpl.java deleted file mode 100644 index 29e3376..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/StorageManagerImpl.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; -import net.momirealms.customnameplates.api.data.DataStorageInterface; -import net.momirealms.customnameplates.api.data.OnlineUser; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent; -import net.momirealms.customnameplates.api.manager.StorageManager; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.storage.method.NoneStorage; -import net.momirealms.customnameplates.paper.storage.method.database.nosql.MongoDBImpl; -import net.momirealms.customnameplates.paper.storage.method.database.nosql.RedisManager; -import net.momirealms.customnameplates.paper.storage.method.database.sql.H2Impl; -import net.momirealms.customnameplates.paper.storage.method.database.sql.MariaDBImpl; -import net.momirealms.customnameplates.paper.storage.method.database.sql.MySQLImpl; -import net.momirealms.customnameplates.paper.storage.method.database.sql.SQLiteImpl; -import net.momirealms.customnameplates.paper.storage.method.file.JsonImpl; -import net.momirealms.customnameplates.paper.storage.method.file.YAMLImpl; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; - -/** - * This class implements the StorageManager interface and is responsible for managing player data storage. - * It includes methods to handle player data retrieval, storage, and serialization. - */ -public class StorageManagerImpl implements Listener, StorageManager { - - private final CustomNameplatesPluginImpl plugin; - private DataStorageInterface dataSource; - private StorageType previousType; - private boolean hasRedis; - private RedisManager redisManager; - private final Gson gson; - private final ConcurrentHashMap onlineUserMap; - - public StorageManagerImpl(CustomNameplatesPluginImpl plugin) { - this.plugin = plugin; - this.gson = new GsonBuilder().create(); - this.onlineUserMap = new ConcurrentHashMap<>(64); - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - var uuid = event.getPlayer().getUniqueId(); - this.getPlayerData(uuid).thenAccept(optData -> { - if (optData.isPresent()) { - var playerData = optData.get(); - var player = Bukkit.getPlayer(uuid); - if (player == null || !player.isOnline()) return; - var onlineUser = new OnlineUser(player, playerData); - this.putOnlineUserInMap(onlineUser); - plugin.getScheduler().runTaskAsync(() -> { - NameplateDataLoadEvent syncEvent = new NameplateDataLoadEvent(uuid, onlineUser); - plugin.getServer().getPluginManager().callEvent(syncEvent); - }); - } - }); - } - - @Override - public Collection getOnlineUsers() { - return onlineUserMap.values(); - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - this.removeOnlineUserFromMap(event.getPlayer().getUniqueId()); - } - - public void putOnlineUserInMap(OnlineUser onlineUser) { - onlineUserMap.put(onlineUser.getUUID(), onlineUser); - } - - public void removeOnlineUserFromMap(UUID uuid) { - onlineUserMap.remove(uuid); - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - OnlineUser onlineUser = onlineUserMap.get(uuid); - if (onlineUser != null) { - return CompletableFuture.completedFuture(Optional.of(onlineUser.toPlayerData())); - } - if (hasRedis) { - return redisManager.getPlayerData(uuid).thenCompose( - result -> { - if (result.isEmpty()) { - return dataSource.getPlayerData(uuid); - } else { - return CompletableFuture.completedFuture(result); - } - } - ); - } else { - return dataSource.getPlayerData(uuid); - } - } - - @Override - public CompletableFuture saveOnlinePlayerData(UUID uuid) { - OnlineUser onlineUser = onlineUserMap.get(uuid); - if (onlineUser == null) { - return CompletableFuture.completedFuture(false); - } - return savePlayerData(uuid, onlineUser.toPlayerData()); - } - - @Override - public CompletableFuture savePlayerData(UUID uuid, PlayerData playerData) { - if (hasRedis) { - return redisManager.updatePlayerData(uuid, playerData).thenCompose( - result -> { - if (!result) { - return CompletableFuture.completedFuture(false); - } - return dataSource.updatePlayerData(uuid, playerData); - } - ); - } else { - return dataSource.updatePlayerData(uuid, playerData); - } - } - - @Override - public Optional getOnlineUser(UUID uuid) { - return Optional.ofNullable(onlineUserMap.get(uuid)); - } - - /** - * Reloads the storage manager configuration. - */ - public void reload() { - YamlConfiguration config = plugin.getConfig("database.yml"); - - // Check if storage type has changed and reinitialize if necessary - StorageType storageType = StorageType.valueOf(config.getString("data-storage-method", "H2")); - if (storageType != previousType) { - if (this.dataSource != null) this.dataSource.disable(); - this.previousType = storageType; - switch (storageType) { - case H2 -> this.dataSource = new H2Impl(plugin); - case JSON -> this.dataSource = new JsonImpl(plugin); - case YAML -> this.dataSource = new YAMLImpl(plugin); - case SQLite -> this.dataSource = new SQLiteImpl(plugin); - case MySQL -> this.dataSource = new MySQLImpl(plugin); - case MariaDB -> this.dataSource = new MariaDBImpl(plugin); - case MongoDB -> this.dataSource = new MongoDBImpl(plugin); - case None -> this.dataSource = new NoneStorage(plugin); - } - if (this.dataSource != null) this.dataSource.initialize(); - else LogUtils.severe("No storage type is set."); - } - - // Handle Redis configuration - if (!this.hasRedis && config.getBoolean("Redis.enable", false)) { - this.hasRedis = true; - this.redisManager = new RedisManager(plugin); - this.redisManager.initialize(); - } - - // Disable Redis if it was enabled but is now disabled - if (this.hasRedis && !config.getBoolean("Redis.enable", false) && this.redisManager != null) { - this.redisManager.disable(); - this.redisManager = null; - } - } - - /** - * Disables the storage manager and cleans up resources. - */ - public void disable() { - HandlerList.unregisterAll(this); - if (this.dataSource != null) - this.dataSource.disable(); - if (this.redisManager != null) - this.redisManager.disable(); - } - - /** - * Checks if Redis is enabled. - * - * @return True if Redis is enabled; otherwise, false. - */ - public boolean isRedisEnabled() { - return hasRedis; - } - - /** - * Gets the RedisManager instance. - * - * @return The RedisManager instance. - */ - @Nullable - public RedisManager getRedisManager() { - return redisManager; - } - - @NotNull - @Override - public PlayerData fromJson(String json) { - return gson.fromJson(json, PlayerData.class); - } - - @Override - public PlayerData fromBytes(byte[] data) { - try { - return gson.fromJson(new String(data, StandardCharsets.UTF_8), PlayerData.class); - } catch (JsonSyntaxException e) { - throw new DataSerializationException("Failed to get PlayerData from bytes", e); - } - } - - @Override - public byte[] toBytes(PlayerData playerData) { - return toJson(playerData).getBytes(StandardCharsets.UTF_8); - } - - @Override - @NotNull - public String toJson(@NotNull PlayerData playerData) { - return gson.toJson(playerData); - } - - @Override - public DataStorageInterface getDataSource() { - return dataSource; - } - - /** - * Custom exception class for data serialization errors. - */ - public static class DataSerializationException extends RuntimeException { - protected DataSerializationException(String message, Throwable cause) { - super(message, cause); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/AbstractStorage.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/AbstractStorage.java deleted file mode 100644 index bead139..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/AbstractStorage.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.DataStorageInterface; -import net.momirealms.customnameplates.api.data.PlayerData; - -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -/** - * An abstract class that implements the DataStorageInterface and provides common functionality for data storage. - */ -public abstract class AbstractStorage implements DataStorageInterface { - - protected CustomNameplatesPlugin plugin; - - public AbstractStorage(CustomNameplatesPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void initialize() { - // This method can be overridden in subclasses to perform initialization tasks specific to the storage type. - } - - @Override - public void disable() { - // This method can be overridden in subclasses to perform cleanup or shutdown tasks specific to the storage type. - } - - @Override - public CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData) { - // By default, delegate to the updatePlayerData method to update or insert player data. - return updatePlayerData(uuid, playerData); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/NoneStorage.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/NoneStorage.java deleted file mode 100644 index 045c11a..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/NoneStorage.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.momirealms.customnameplates.paper.storage.method; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; - -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -public class NoneStorage extends AbstractStorage { - - public NoneStorage(CustomNameplatesPlugin plugin) { - super(plugin); - } - - @Override - public StorageType getStorageType() { - return StorageType.None; - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - return CompletableFuture.completedFuture(Optional.of(PlayerData.empty())); - } - - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - return CompletableFuture.completedFuture(true); - } - - @Override - public Set getUniqueUsers(boolean legacy) { - return Set.of(); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/nosql/MongoDBImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/nosql/MongoDBImpl.java deleted file mode 100644 index c0f27b0..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/nosql/MongoDBImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method.database.nosql; - -import com.mongodb.*; -import com.mongodb.client.*; -import com.mongodb.client.model.Filters; -import com.mongodb.client.model.Projections; -import com.mongodb.client.model.UpdateOptions; -import com.mongodb.client.model.Updates; -import com.mongodb.client.result.UpdateResult; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.storage.method.AbstractStorage; -import org.bson.Document; -import org.bson.UuidRepresentation; -import org.bson.conversions.Bson; -import org.bson.types.Binary; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.util.*; -import java.util.concurrent.CompletableFuture; - -/** - * An implementation of AbstractStorage that uses MongoDB for player data storage. - */ -public class MongoDBImpl extends AbstractStorage { - - private MongoClient mongoClient; - private MongoDatabase database; - private String collectionPrefix; - - public MongoDBImpl(CustomNameplatesPluginImpl plugin) { - super(plugin); - } - - /** - * Initialize the MongoDB connection and configuration based on the plugin's YAML configuration. - */ - @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); - ConfigurationSection section = config.getConfigurationSection("MongoDB"); - if (section == null) { - LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); - return; - } - - collectionPrefix = section.getString("collection-prefix", "nameplates"); - var settings = MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD); - if (!section.getString("connection-uri", "").isEmpty()) { - settings.applyConnectionString(new ConnectionString(section.getString("connection-uri", ""))); - this.mongoClient = MongoClients.create(settings.build()); - this.database = mongoClient.getDatabase(section.getString("database", "minecraft")); - return; - } - - if (section.contains("user")) { - MongoCredential credential = MongoCredential.createCredential( - section.getString("user", "root"), - section.getString("database", "minecraft"), - section.getString("password", "password").toCharArray() - ); - settings.credential(credential); - } - - settings.applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(new ServerAddress( - section.getString("host", "localhost"), - section.getInt("port", 27017) - )))); - this.mongoClient = MongoClients.create(settings.build()); - this.database = mongoClient.getDatabase(section.getString("database", "minecraft")); - } - - /** - * Disable the MongoDB connection by closing the MongoClient. - */ - @Override - public void disable() { - if (this.mongoClient != null) { - this.mongoClient.close(); - } - } - - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - MongoCollection collection = database.getCollection(getCollectionName("data")); - try { - Document query = new Document("uuid", uuid); - Bson updates = Updates.combine( - Updates.set( - "data", - new Binary(plugin.getStorageManager().toBytes(playerData)) - ) - ); - UpdateOptions options = new UpdateOptions().upsert(true); - UpdateResult result = collection.updateOne(query, updates, options); - future.complete(result.wasAcknowledged()); - } catch (MongoException e) { - future.completeExceptionally(e); - } - }); - return future; - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { - MongoCollection collection = database.getCollection(getCollectionName("data")); - Document doc = collection.find(Filters.eq("uuid", uuid)).first(); - if (doc == null) { - future.complete(Optional.empty()); - } else if (Bukkit.getPlayer(uuid) != null) { - Binary binary = (Binary) doc.get("data"); - future.complete(Optional.of(plugin.getStorageManager().fromBytes(binary.getData()))); - } else { - future.complete(Optional.empty()); - } - }); - return future; - } - - /** - * Get a set of unique player UUIDs from the MongoDB database. - * - * @param legacy Flag indicating whether to retrieve legacy data. - * @return A set of unique player UUIDs. - */ - @Override - public Set getUniqueUsers(boolean legacy) { - // no legacy files - Set uuids = new HashSet<>(); - MongoCollection collection = database.getCollection(getCollectionName("data")); - try { - Bson projectionFields = Projections.fields(Projections.include("uuid")); - try (MongoCursor cursor = collection.find().projection(projectionFields).iterator()) { - while (cursor.hasNext()) { - uuids.add(cursor.next().get("uuid", UUID.class)); - } - } - } catch (MongoException e) { - LogUtils.warn("Failed to get unique data.", e); - } - return uuids; - } - - /** - * Get the collection name for a specific subcategory of data. - * - * @param value The subcategory identifier. - * @return The full collection name including the prefix. - */ - public String getCollectionName(String value) { - return getCollectionPrefix() + "_" + value; - } - - /** - * Get the collection prefix used for MongoDB collections. - * - * @return The collection prefix. - */ - public String getCollectionPrefix() { - return collectionPrefix; - } - - @Override - public StorageType getStorageType() { - return StorageType.MongoDB; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/nosql/RedisManager.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/nosql/RedisManager.java deleted file mode 100644 index 45766c4..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/nosql/RedisManager.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method.database.nosql; - -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl; -import net.momirealms.customnameplates.paper.storage.method.AbstractStorage; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolConfig; -import redis.clients.jedis.exceptions.JedisException; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -/** - * A RedisManager class responsible for managing interactions with a Redis server for data storage. - */ -public class RedisManager extends AbstractStorage { - - private static RedisManager instance; - private JedisPool jedisPool; - private String password; - private int port; - private String host; - private boolean useSSL; - - public RedisManager(CustomNameplatesPluginImpl plugin) { - super(plugin); - instance = this; - } - - /** - * Get the singleton instance of the RedisManager. - * - * @return The RedisManager instance. - */ - public static RedisManager getInstance() { - return instance; - } - - /** - * Get a Jedis resource for interacting with the Redis server. - * - * @return A Jedis resource. - */ - public Jedis getJedis() { - return jedisPool.getResource(); - } - - @Override - public Set getUniqueUsers(boolean legacy) { - return new HashSet<>(); - } - - /** - * Initialize the Redis connection and configuration based on the plugin's YAML configuration. - */ - @Override - public void initialize() { - YamlConfiguration config = plugin.getConfig("database.yml"); - ConfigurationSection section = config.getConfigurationSection("Redis"); - if (section == null) { - LogUtils.warn("Failed to load database config. It seems that your config is broken. Please regenerate a new one."); - return; - } - - JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); - jedisPoolConfig.setTestWhileIdle(true); - jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(30000)); - jedisPoolConfig.setNumTestsPerEvictionRun(-1); - jedisPoolConfig.setMinEvictableIdleDuration(Duration.ofMillis(section.getInt("MinEvictableIdleTimeMillis", 1800000))); - jedisPoolConfig.setMaxTotal(section.getInt("MaxTotal",8)); - jedisPoolConfig.setMaxIdle(section.getInt("MaxIdle",8)); - jedisPoolConfig.setMinIdle(section.getInt("MinIdle",1)); - jedisPoolConfig.setMaxWait(Duration.ofMillis(section.getInt("MaxWaitMillis"))); - - password = section.getString("password", ""); - port = section.getInt("port", 6379); - host = section.getString("host", "localhost"); - useSSL = section.getBoolean("use-ssl", false); - - if (password.isBlank()) { - jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, useSSL); - } else { - jedisPool = new JedisPool(jedisPoolConfig, host, port, 0, password, useSSL); - } - try (Jedis jedis = jedisPool.getResource()) { - jedis.ping(); - LogUtils.info("Redis server connected."); - } catch (JedisException e) { - LogUtils.warn("Failed to connect redis."); - } - } - - /** - * Disable the Redis connection by closing the JedisPool. - */ - @Override - public void disable() { - if (jedisPool != null && !jedisPool.isClosed()) - jedisPool.close(); - } - - /** - * Send a message to Redis on a specified channel. - * - * @param channel The Redis channel to send the message to. - * @param message The message to send. - */ - public void sendRedisMessage(@NotNull String channel, @NotNull String message) { - try (Jedis jedis = getJedis()) { - jedis.publish(channel, message); - plugin.debug("Sent Redis message: " + message); - } - } - - @Override - public StorageType getStorageType() { - return StorageType.Redis; - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { - try (Jedis jedis = getJedis()) { - byte[] key = getRedisKey("cn_data", uuid); - byte[] data = jedis.get(key); - if (data != null) { - future.complete(Optional.of(plugin.getStorageManager().fromBytes(data))); - plugin.debug("Redis data retrieved for " + uuid + "; normal data"); - } else { - future.complete(Optional.empty()); - plugin.debug("Redis data retrieved for " + uuid + "; empty data"); - } - } catch (Exception e) { - future.complete(Optional.empty()); - LogUtils.warn("Failed to get redis data for " + uuid, e); - } - }); - return future; - } - - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - try (Jedis jedis = getJedis()) { - jedis.setex( - getRedisKey("cn_data", uuid), - 600, - plugin.getStorageManager().toBytes(playerData) - ); - future.complete(true); - plugin.debug("Redis data set for " + uuid); - } catch (Exception e) { - future.complete(false); - LogUtils.warn("Failed to set redis data for player " + uuid, e); - } - }); - return future; - } - - /** - * Generate a Redis key for a specified key and UUID. - * - * @param key The key identifier. - * @param uuid The UUID to include in the key. - * @return A byte array representing the Redis key. - */ - private byte[] getRedisKey(String key, @NotNull UUID uuid) { - return (key + ":" + uuid).getBytes(StandardCharsets.UTF_8); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/AbstractSQLDatabase.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/AbstractSQLDatabase.java deleted file mode 100644 index 7d4a137..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/database/sql/AbstractSQLDatabase.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method.database.sql; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.storage.method.AbstractStorage; -import org.bukkit.Bukkit; -import org.jetbrains.annotations.NotNull; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.sql.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -/** - * An abstract base class for SQL database implementations that handle player data storage. - */ -public abstract class AbstractSQLDatabase extends AbstractStorage { - - protected String tablePrefix; - - public AbstractSQLDatabase(CustomNameplatesPlugin plugin) { - super(plugin); - } - - /** - * Get a connection to the SQL database. - * - * @return A database connection. - * @throws SQLException If there is an error establishing a connection. - */ - public abstract Connection getConnection() throws SQLException; - - /** - * Create tables for storing data if they don't exist in the database. - */ - public void createTableIfNotExist() { - try (Connection connection = getConnection()) { - final String[] databaseSchema = getSchema(getStorageType().name().toLowerCase(Locale.ENGLISH)); - try (Statement statement = connection.createStatement()) { - for (String tableCreationStatement : databaseSchema) { - statement.execute(tableCreationStatement); - } - } catch (SQLException e) { - LogUtils.warn("Failed to create tables"); - } - } catch (SQLException e) { - LogUtils.warn("Failed to get sql connection"); - } catch (IOException e) { - LogUtils.warn("Failed to get schema resource"); - } - } - - /** - * Update or insert a player's data into the SQL database. - * - * @param uuid The UUID of the player. - * @param playerData The player data to update or insert. - * @return A CompletableFuture indicating the success of the operation. - */ - @Override - public CompletableFuture updateOrInsertPlayerData(UUID uuid, PlayerData playerData) { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) - ) { - statement.setString(1, uuid.toString()); - ResultSet rs = statement.executeQuery(); - if (rs.next()) { - updatePlayerData(uuid, playerData).thenRun(() -> future.complete(true)); - } else { - insertPlayerData(uuid, playerData); - future.complete(true); - } - } catch (SQLException e) { - LogUtils.warn("Failed to update " + uuid + "'s data.", e); - } - }); - return future; - } - - /** - * Get the SQL schema from a resource file. - * - * @param fileName The name of the schema file. - * @return An array of SQL statements to create tables. - * @throws IOException If there is an error reading the schema resource. - */ - private String[] getSchema(@NotNull String fileName) throws IOException { - return replaceSchemaPlaceholder(new String(Objects.requireNonNull(plugin.getResource("schema/" + fileName + ".sql")) - .readAllBytes(), StandardCharsets.UTF_8)).split(";"); - } - - /** - * Replace placeholder values in SQL schema with the table prefix. - * - * @param sql The SQL schema string. - * @return The SQL schema string with placeholders replaced. - */ - private String replaceSchemaPlaceholder(@NotNull String sql) { - return sql.replace("{prefix}", tablePrefix); - } - - /** - * Get the name of a database table based on a sub-table name and the table prefix. - * - * @param sub The sub-table name. - * @return The full table name. - */ - public String getTableName(String sub) { - return getTablePrefix() + "_" + sub; - } - - /** - * Get the current table prefix. - * - * @return The table prefix. - */ - public String getTablePrefix() { - return tablePrefix; - } - - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - var future = new CompletableFuture(); - plugin.getScheduler().runTaskAsync(() -> { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_UPDATE_BY_UUID, getTableName("data"))) - ) { - statement.setBlob(1, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData))); - statement.setString(2, uuid.toString()); - statement.executeUpdate(); - future.complete(true); - } catch (SQLException e) { - LogUtils.warn("Failed to update " + uuid + "'s data.", e); - future.completeExceptionally(e); - } - }); - return future; - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - var future = new CompletableFuture>(); - plugin.getScheduler().runTaskAsync(() -> { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_BY_UUID, getTableName("data"))) - ) { - statement.setString(1, uuid.toString()); - ResultSet rs = statement.executeQuery(); - if (rs.next()) { - Blob blob = rs.getBlob("data"); - byte[] dataByteArray = blob.getBytes(1, (int) blob.length()); - blob.free(); - future.complete(Optional.of(plugin.getStorageManager().fromBytes(dataByteArray))); - } else if (Bukkit.getPlayer(uuid) != null) { - var data = PlayerData.empty(); - this.insertPlayerData(uuid, data); - future.complete(Optional.of(data)); - } else { - future.complete(Optional.empty()); - } - } catch (SQLException e) { - LogUtils.warn("Failed to get " + uuid + "'s data.", e); - future.completeExceptionally(e); - } - }); - return future; - } - - public void insertPlayerData(UUID uuid, PlayerData playerData) { - try ( - Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_INSERT_DATA_BY_UUID, getTableName("data"))) - ) { - statement.setString(1, uuid.toString()); - statement.setBlob(2, new ByteArrayInputStream(plugin.getStorageManager().toBytes(playerData))); - statement.execute(); - } catch (SQLException e) { - LogUtils.warn("Failed to insert " + uuid + "'s data.", e); - } - } - - /** - * Get a set of unique user UUIDs from the SQL database. - * - * @param legacy Whether to include legacy data in the retrieval. - * @return A set of unique user UUIDs. - */ - @Override - public Set getUniqueUsers(boolean legacy) { - Set uuids = new HashSet<>(); - try (Connection connection = getConnection(); - PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_ALL_UUID, getTableName("data")))) { - try (ResultSet rs = statement.executeQuery()) { - while (rs.next()) { - UUID uuid = UUID.fromString(rs.getString("uuid")); - uuids.add(uuid); - } - } - } catch (SQLException e) { - LogUtils.warn("Failed to get unique data.", e); - } - return uuids; - } - - /** - * Constants defining SQL statements used for database operations. - */ - public static class SqlConstants { - public static final String SQL_SELECT_BY_UUID = "SELECT * FROM `%s` WHERE `uuid` = ?"; - public static final String SQL_SELECT_ALL_UUID = "SELECT uuid FROM `%s`"; - public static final String SQL_UPDATE_BY_UUID = "UPDATE `%s` SET `data` = ? WHERE `uuid` = ?"; - public static final String SQL_INSERT_DATA_BY_UUID = "INSERT INTO `%s`(`uuid`, `data`) VALUES(?, ?)"; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/file/YAMLImpl.java b/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/file/YAMLImpl.java deleted file mode 100644 index d5cb344..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/storage/method/file/YAMLImpl.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.storage.method.file; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.data.LegacyDataStorageInterface; -import net.momirealms.customnameplates.api.data.PlayerData; -import net.momirealms.customnameplates.api.data.StorageType; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.paper.storage.method.AbstractStorage; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -/** - * A data storage implementation that uses YAML files to store player data, with support for legacy data. - */ -public class YAMLImpl extends AbstractStorage implements LegacyDataStorageInterface { - - @SuppressWarnings("ResultOfMethodCallIgnored") - public YAMLImpl(CustomNameplatesPlugin plugin) { - super(plugin); - File folder = new File(plugin.getDataFolder(), "data"); - if (!folder.exists()) folder.mkdirs(); - } - - @Override - public StorageType getStorageType() { - return StorageType.YAML; - } - - @Override - public CompletableFuture> getPlayerData(UUID uuid) { - File dataFile = getPlayerDataFile(uuid); - if (!dataFile.exists()) { - if (Bukkit.getPlayer(uuid) != null) { - return CompletableFuture.completedFuture(Optional.of(PlayerData.empty())); - } else { - return CompletableFuture.completedFuture(Optional.empty()); - } - } - YamlConfiguration data = readData(dataFile); - PlayerData playerData = new PlayerData.Builder() - .setBubble(data.getString("bubble", "")) - .setNameplate(data.getString("nameplate", "")) - .build(); - return CompletableFuture.completedFuture(Optional.of(playerData)); - } - - /** - * Get the file associated with a player's UUID for storing YAML data. - * - * @param uuid The UUID of the player. - * @return The file for the player's data. - */ - public File getPlayerDataFile(UUID uuid) { - return new File(plugin.getDataFolder(), "data" + File.separator + uuid + ".yml"); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public YamlConfiguration readData(File file) { - if (!file.exists()) { - try { - file.getParentFile().mkdirs(); - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - LogUtils.warn("Failed to generate data files!"); - } - } - return YamlConfiguration.loadConfiguration(file); - } - - @Override - public CompletableFuture updatePlayerData(UUID uuid, PlayerData playerData) { - YamlConfiguration data = new YamlConfiguration(); - data.set("bubble", playerData.getBubble()); - data.set("nameplate", playerData.getNameplate()); - try { - data.save(getPlayerDataFile(uuid)); - } catch (IOException e) { - LogUtils.warn("Failed to save player data", e); - } - return CompletableFuture.completedFuture(true); - } - - @Override - public Set getUniqueUsers(boolean legacy) { - File folder; - if (legacy) { - folder = new File(plugin.getDataFolder(), "player_data"); - } else { - folder = new File(plugin.getDataFolder(), "data"); - } - Set uuids = new HashSet<>(); - if (folder.exists()) { - File[] files = folder.listFiles(); - if (files != null) { - for (File file : files) { - uuids.add(UUID.fromString(file.getName().substring(0, file.getName().length() - 4))); - } - } - } - return uuids; - } - - @Override - public CompletableFuture> getLegacyPlayerData(UUID uuid) { - File dataFile = new File(plugin.getDataFolder(), "player_data" + File.separator + uuid + ".yml"); - YamlConfiguration data = readData(dataFile); - PlayerData playerData = new PlayerData.Builder() - .setBubble(data.getString("bubbles", "")) - .setNameplate(data.getString("nameplate", "")) - .build(); - return CompletableFuture.completedFuture(Optional.of(playerData)); - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ConfigUtils.java b/paper/src/main/java/net/momirealms/customnameplates/paper/util/ConfigUtils.java deleted file mode 100644 index b91276b..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ConfigUtils.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.util; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.requirement.Requirement; -import net.momirealms.customnameplates.api.util.LogUtils; -import net.momirealms.customnameplates.common.Pair; -import net.momirealms.customnameplates.paper.mechanic.misc.TimeLimitText; -import org.bukkit.configuration.ConfigurationSection; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.net.URL; -import java.net.URLConnection; -import java.util.*; - -public class ConfigUtils { - - private ConfigUtils() {} - - /** - * Converts an object into an ArrayList of strings. - * - * @param object The input object - * @return An ArrayList of strings - */ - @SuppressWarnings("unchecked") - public static ArrayList stringListArgs(Object object) { - ArrayList list = new ArrayList<>(); - if (object instanceof String member) { - list.add(member); - } else if (object instanceof List members) { - list.addAll((Collection) members); - } else if (object instanceof String[] strings) { - list.addAll(List.of(strings)); - } - return list; - } - - /** - * Splits a string into a pair of integers using the "~" delimiter. - * - * @param value The input string - * @return A Pair of integers - */ - public static Pair splitStringIntegerArgs(String value, String regex) { - String[] split = value.split(regex); - return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1])); - } - - /** - * Converts an object into a double value. - * - * @param arg The input object - * @return A double value - */ - public static double getDoubleValue(Object arg) { - if (arg instanceof Double d) { - return d; - } else if (arg instanceof Integer i) { - return Double.valueOf(i); - } - return 0; - } - - public static TimeLimitText[] getTimeLimitTexts(ConfigurationSection section) { - - TreeMap map = new TreeMap<>(); - if (section == null) { - LogUtils.warn("No text display order set, this might cause bugs!"); - map.put(1, new TimeLimitText(100, -1, "", new Requirement[0])); - return getOrderedTexts(map); - } - - for (Map.Entry entry : section.getValues(false).entrySet()) { - if (!(entry.getValue() instanceof ConfigurationSection textSection)) - continue; - - var text = TimeLimitText.Builder.of() - .text(textSection.getString("text", "")) - .refreshFrequency(textSection.getInt("refresh-frequency", -1)) - .duration(textSection.getInt("duration", 200)) - .requirement(CustomNameplatesPlugin.get().getRequirementManager().getRequirements(textSection.getConfigurationSection("conditions"))) - .build(); - - int order; - try { - order = Integer.parseInt(entry.getKey()); - } catch (NumberFormatException e) { - LogUtils.warn("Invalid order format: " + entry.getKey()); - continue; - } - - map.put(order, text); - } - - return getOrderedTexts(map); - } - - private static TimeLimitText[] getOrderedTexts(TreeMap map) { - return map.values().toArray(new TimeLimitText[0]); - } - - public static void saveResource(@NotNull String resourcePath) { - if (resourcePath.equals("")) { - throw new IllegalArgumentException("ResourcePath cannot be null or empty"); - } - - resourcePath = resourcePath.replace('\\', '/'); - InputStream in = getResource(resourcePath); - if (in == null) { - return; - } - - File outFile = new File(CustomNameplatesPlugin.get().getDataFolder(), resourcePath); - int lastIndex = resourcePath.lastIndexOf('/'); - File outDir = new File(CustomNameplatesPlugin.get().getDataFolder(), resourcePath.substring(0, Math.max(lastIndex, 0))); - - if (!outDir.exists()) { - outDir.mkdirs(); - } - - try { - if (!outFile.exists()) { - OutputStream out = new FileOutputStream(outFile); - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - out.close(); - in.close(); - } - } catch (IOException ex) { - LogUtils.warn("Could not save " + outFile.getName() + " to " + outFile); - } - } - - @Nullable - public static InputStream getResource(@NotNull String filename) { - try { - URL url = CustomNameplatesPlugin.get().getClass().getClassLoader().getResource(filename); - if (url == null) { - return null; - } - URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - return connection.getInputStream(); - } catch (IOException ex) { - return null; - } - } - - public static String native2ascii(char c) { - StringBuilder stringBuilder_1 = new StringBuilder("\\u"); - StringBuilder stringBuilder_2 = new StringBuilder(Integer.toHexString(c)); - stringBuilder_2.reverse(); - for (int n = 4 - stringBuilder_2.length(), i = 0; i < n; i++) stringBuilder_2.append('0'); - for (int j = 0; j < 4; j++) stringBuilder_1.append(stringBuilder_2.charAt(3 - j)); - return stringBuilder_1.toString(); - } - - public static char[] convertUnicodeStringToChars(String unicodeString) { - String processedString = unicodeString.replace("\\u", ""); - int length = processedString.length() / 4; - char[] chars = new char[length]; - for (int i = 0; i < length; i++) { - int codePoint = Integer.parseInt(processedString.substring(i * 4, i * 4 + 4), 16); - if (Character.isSupplementaryCodePoint(codePoint)) { - chars[i] = Character.highSurrogate(codePoint); - chars[++i] = Character.lowSurrogate(codePoint); - } else { - chars[i] = (char) codePoint; - } - } - return chars; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/FakeEntityUtils.java b/paper/src/main/java/net/momirealms/customnameplates/paper/util/FakeEntityUtils.java deleted file mode 100644 index d84a474..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/FakeEntityUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.util; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import com.comphenix.protocol.wrappers.WrappedDataValue; -import com.comphenix.protocol.wrappers.WrappedDataWatcher; -import com.google.common.collect.Lists; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -public class FakeEntityUtils { - - private static int entityID = 114514520; - - public static int getAndIncrease() { - return entityID++; - } - - public static PacketContainer getMetaPacket(int entityID, String text, boolean sneak) { - PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); - metaPacket.getIntegers().write(0, entityID); - if (CustomNameplatesPlugin.getInstance().getVersionManager().isVersionNewerThan1_19_R2()) { - WrappedDataWatcher wrappedDataWatcher = createArmorStandDataWatcher(text, sneak); - List wrappedDataValueList = Lists.newArrayList(); - wrappedDataWatcher.getWatchableObjects().stream().filter(Objects::nonNull).forEach(entry -> wrappedDataValueList.add(new WrappedDataValue(entry.getWatcherObject().getIndex(), entry.getWatcherObject().getSerializer(), entry.getRawValue()))); - metaPacket.getDataValueCollectionModifier().write(0, wrappedDataValueList); - } else { - metaPacket.getWatchableCollectionModifier().write(0, createArmorStandDataWatcher(text, sneak).getWatchableObjects()); - } - return metaPacket; - } - - private static WrappedDataWatcher createArmorStandDataWatcher(String miniMessage, boolean sneak) { - WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); - WrappedDataWatcher.Serializer serializer1 = WrappedDataWatcher.Registry.get(Boolean.class); - WrappedDataWatcher.Serializer serializer2 = WrappedDataWatcher.Registry.get(Byte.class); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(2, WrappedDataWatcher.Registry.getChatComponentSerializer(true)), Optional.of(WrappedChatComponent.fromJson(AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(miniMessage)).getHandle())); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, serializer1), true); - byte flag = 0x20; - if (sneak) flag += (byte) 0x02; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, serializer2), flag); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, serializer2), (byte) 0x01); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, serializer1), true); - return wrappedDataWatcher; - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ImageUtils.java b/paper/src/main/java/net/momirealms/customnameplates/paper/util/ImageUtils.java deleted file mode 100644 index 4579306..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ImageUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.momirealms.customnameplates.paper.util; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -public class ImageUtils { - - public static void removeImageShadow(File file) { - try { - BufferedImage inputImage = ImageIO.read(file); - - int width = inputImage.getWidth(); - int height = inputImage.getHeight(); - BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - int[] pixels = new int[width * height]; - inputImage.getRGB(0, 0, width, height, pixels, 0, width); - for (int i = 0; i < pixels.length; i++) { - int alpha = (pixels[i] >> 24) & 0xFF; - int red = (pixels[i] >> 16) & 0xFF; - int green = (pixels[i] >> 8) & 0xFF; - int blue = pixels[i] & 0xFF; - if (alpha > 0) { - alpha = 254; - } - int newPixel = (alpha << 24) | (red << 16) | (green << 8) | blue; - pixels[i] = newPixel; - } - outputImage.setRGB(0, 0, width, height, pixels, 0, width); - ImageIO.write(outputImage, "png", file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static void setAnimatedImage(File file, int frames) { - try { - BufferedImage inputImage = ImageIO.read(file); - int width = inputImage.getWidth(); - int height = inputImage.getHeight(); - int frameHeight = height / frames; - BufferedImage outputImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - int newPixel = (1 << 24) | (10 << 16) | (width << 8) | frameHeight; - for (int i = 0; i < frames; i++) { - outputImage.setRGB(0, frameHeight * i, newPixel); - } - ImageIO.write(outputImage, "png", file); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/Migration.java b/paper/src/main/java/net/momirealms/customnameplates/paper/util/Migration.java deleted file mode 100644 index cbc1706..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/Migration.java +++ /dev/null @@ -1,238 +0,0 @@ -package net.momirealms.customnameplates.paper.util; - -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.util.LogUtils; -import org.apache.commons.io.FileUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.Map; - -public class Migration { - - public static boolean check() { - File readMe = new File(CustomNameplatesPlugin.get().getDataFolder(), "README.text"); - if (readMe.exists()) { - return true; - } - File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "config.yml"); - if (!file.exists()) { - return false; - } - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - boolean version = config.contains("other-settings.default-character-width"); - if (!version) { - return false; - } - config.set("other-settings.default-character-width", null); - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - CustomNameplatesPlugin.get().saveResource("README.txt", true); - updateBossBar(); - updateActionBar(); - updateBubble(); - updateNameplate(); - updateCustomPlaceholders(); - updateLanguages(); - deleteFiles(); - return true; - } - - private static void updateLanguages() { - File messageFolder = new File(CustomNameplatesPlugin.get().getDataFolder(), "messages"); - if (messageFolder.exists()) { - File[] files = messageFolder.listFiles(); - if (files == null) - return; - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - ConfigurationSection messageSection = config.getConfigurationSection("messages"); - if (messageSection != null) { - for (String key : messageSection.getKeys(false)) { - String original = messageSection.getString(key); - if (original != null && original.contains("{Bubbles}")) { - messageSection.set(key, original.replace("{Bubbles}", "{Bubble}")); - } - } - } - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - private static void deleteFiles() { - try { - FileUtils.delete(new File(CustomNameplatesPlugin.get().getDataFolder(), "database.yml")); - FileUtils.deleteDirectory(new File(CustomNameplatesPlugin.get().getDataFolder(), "unicodes")); - FileUtils.deleteDirectory(new File(CustomNameplatesPlugin.get().getDataFolder(), "templates")); - FileUtils.deleteDirectory(new File(CustomNameplatesPlugin.get().getDataFolder(), "ResourcePack")); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static void updateBossBar() { - File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "bossbar.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (Map.Entry entry : config.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection section) { - section.set("check-frequency", 1); - int refresh = section.getInt("refresh-rate"); - if (section.contains("text")) { - String text = section.getString("text"); - section.set("text-display-order.1.duration", 1000); - section.set("text-display-order.1.refresh-frequency", refresh); - section.set("text-display-order.1.text", text); - } else if (section.contains("dynamic-text")) { - int switchInt = section.getInt("switch-interval", 15) * 20; - int i = 0; - for (String text : section.getStringList("dynamic-text")) { - i++; - section.set("text-display-order."+i+".duration", switchInt); - section.set("text-display-order."+i+".refresh-frequency", refresh); - section.set("text-display-order."+i+".text", text); - } - } - section.set("switch-interval", null); - section.set("dynamic-text", null); - section.set("refresh-rate", null); - section.set("text", null); - } - } - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static void updateActionBar() { - File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "actionbar.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (Map.Entry entry : config.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection section) { - String text = section.getString("text", ""); - section.set("text", null); - section.set("check-frequency", 1); - section.set("text-display-order.1.text", text); - section.set("text-display-order.1.refresh-frequency", 1); - section.set("text-display-order.1.duration", 1000); - } - } - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static void updateBubble() { - File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "bubble.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - config.set("mode", null); - config.set("text-display-options", null); - config.set("default-bubble", config.getString("default-bubbles","none")); - config.set("default-bubbles", null); - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static void updateNameplate() { - File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "nameplate.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - String mode = config.getString("mode",""); - if (!mode.equalsIgnoreCase("team")) { - config.set("mode", "Unlimited"); - } - - config.set("nameplate.player-name", config.getString("player-name","%player_name%")); - config.set("nameplate.refresh-frequency", 10); - config.set("nameplate.prefix", config.getString("prefix", "")); - config.set("nameplate.prefix", config.getString("suffix","")); - - if (mode.equalsIgnoreCase("team")) { - config.createSection("unlimited"); - ConfigurationSection section = config.createSection("team"); - section.set("refresh-frequency", 20); - section.set("prefix", ""); - section.set("suffix", ""); - } else { - ConfigurationSection old; - if (mode.equalsIgnoreCase("armor_stand")) { - old = config.getConfigurationSection("armor_stand"); - } else if (mode.equalsIgnoreCase("text_display")) { - old = config.getConfigurationSection("text_display"); - } else { - LogUtils.severe("No mode is set for nameplate. Failed to migrate!"); - return; - } - ConfigurationSection newSection = config.createSection("unlimited", old.getValues(false)); - for (Map.Entry entry : newSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - ConfigurationSection conditions = inner.getConfigurationSection("conditions"); - if (conditions != null) { - inner.createSection("owner-conditions", conditions.getValues(false)); - inner.createSection("viewer-conditions"); - } - } - } - } - try { - config.set("player-name", null); - config.set("prefix", null); - config.set("suffix", null); - config.set("hide-prefix-when-equipping-nameplate", null); - config.set("hide-suffix-when-equipping-nameplate", null); - config.set("create-fake-team", null); - config.set("text_display", null); - config.set("armor_stand", null); - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static void updateCustomPlaceholders() { - File file = new File(CustomNameplatesPlugin.get().getDataFolder(), "configs" + File.separator + "custom-placeholders.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - config.createSection("switch-text"); - ConfigurationSection descentTSection = config.getConfigurationSection("descent-text"); - ConfigurationSection descentUSection = config.getConfigurationSection("descent-unicode"); - if (descentTSection != null && descentUSection != null) { - for (Map.Entry entry : descentUSection.getValues(false).entrySet()) { - if (entry.getValue() instanceof ConfigurationSection inner) { - String text = inner.getString("text"); - int descent = inner.getInt("descent"); - ConfigurationSection newSection; - if (descentTSection.contains(entry.getKey())) { - newSection = descentTSection.createSection(entry.getKey() + "_"); - LogUtils.warn("Duplicated key '" + entry.getKey() + "' found during migration. Please manually fix that."); - } else { - newSection = descentTSection.createSection(entry.getKey()); - } - newSection.set("text", text); - newSection.set("descent", descent); - newSection.set("is-unicode", true); - } - } - } - config.set("descent-unicode", null); - try { - config.save(file); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ReflectionUtils.java b/paper/src/main/java/net/momirealms/customnameplates/paper/util/ReflectionUtils.java deleted file mode 100644 index a911271..0000000 --- a/paper/src/main/java/net/momirealms/customnameplates/paper/util/ReflectionUtils.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.paper.util; - -import com.comphenix.protocol.utility.MinecraftReflection; -import net.momirealms.customnameplates.api.CustomNameplatesPlugin; -import net.momirealms.customnameplates.api.util.LogUtils; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class ReflectionUtils { - - private ReflectionUtils() {} - - private static Object removeBossBarPacket; - private static Constructor progressConstructor; - private static Constructor updateConstructor; - private static Method iChatComponentMethod; - private static Object emptyComponent; - private static Method serializeComponentMethod; - private static Method keyAsStringMethod; - private static Method keyFromStringMethod; - private static Object miniMessageInstance; - private static Class keyClass; - private static boolean isPaper; - - public static void load() { - if (CustomNameplatesPlugin.get().getVersionManager().isMojmap()) { - try { - Class bar = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); - Field remove = bar.getDeclaredField("REMOVE_OPERATION"); - remove.setAccessible(true); - removeBossBarPacket = remove.get(null); - Class packetBossClassF = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket$UpdateProgressOperation"); - progressConstructor = packetBossClassF.getDeclaredConstructor(float.class); - progressConstructor.setAccessible(true); - Class packetBossClassE = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket$UpdateNameOperation"); - updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass()); - updateConstructor.setAccessible(true); - Class craftChatMessageClass = Class.forName("org.bukkit.craftbukkit.util.CraftChatMessage"); - iChatComponentMethod = craftChatMessageClass.getDeclaredMethod("fromJSON", String.class); - iChatComponentMethod.setAccessible(true); - emptyComponent = iChatComponentMethod.invoke(null, "{\"text\":\"\"}"); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) { - LogUtils.severe("Error occurred while loading reflections", exception); - } - } else { - try { - Class bar = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss"); - Field remove = bar.getDeclaredField("f"); - remove.setAccessible(true); - removeBossBarPacket = remove.get(null); - Class packetBossClassF = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$f"); - progressConstructor = packetBossClassF.getDeclaredConstructor(float.class); - progressConstructor.setAccessible(true); - Class packetBossClassE = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$e"); - updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass()); - updateConstructor.setAccessible(true); - iChatComponentMethod = MinecraftReflection.getChatSerializerClass().getMethod("a", String.class); - iChatComponentMethod.setAccessible(true); - emptyComponent = iChatComponentMethod.invoke(null, "{\"text\":\"\"}"); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) { - LogUtils.severe("Error occurred while loading reflections", exception); - } - } - try { - Class componentClass = Class.forName("net;kyori;adventure;text;Component".replace(";", ".")); - Class miniMessageClass = Class.forName("net;kyori;adventure;text;minimessage;MiniMessage".replace(";", ".")); - Method miniMessageInstanceGetMethod = miniMessageClass.getMethod("miniMessage"); - miniMessageInstance = miniMessageInstanceGetMethod.invoke(null); - serializeComponentMethod = miniMessageClass.getMethod("serialize", componentClass); - keyClass = Class.forName("net;kyori;adventure;key;Key".replace(";", ".")); - keyAsStringMethod = keyClass.getMethod("asString"); - keyFromStringMethod = keyClass.getMethod("key", String.class); - isPaper = true; - } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | - IllegalAccessException ignored) { - } - } - - public static Object getRemoveBossBarPacket() { - return removeBossBarPacket; - } - - public static Constructor getProgressConstructor() { - return progressConstructor; - } - - public static Constructor getUpdateConstructor() { - return updateConstructor; - } - - public static Method getiChatComponentMethod() { - return iChatComponentMethod; - } - - public static Object getEmptyComponent() { - return emptyComponent; - } - - public static Class getKeyClass() { - return keyClass; - } - - public static boolean isPaper() { - return isPaper; - } - - public static String getKeyAsString(Object key) { - try { - return (String) keyAsStringMethod.invoke(key); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } - return ""; - } - - public static Object getKerFromString(String key) { - try { - return keyFromStringMethod.invoke(null, key); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - - public static String getMiniMessageTextFromNonShadedComponent(Object component) { - try { - return (String) serializeComponentMethod.invoke(miniMessageInstance, component); - } catch (InvocationTargetException | IllegalAccessException e) { - e.printStackTrace(); - } - return ""; - } -} \ No newline at end of file diff --git a/paper/src/main/resources/README.txt b/paper/src/main/resources/README.txt deleted file mode 100644 index ffed111..0000000 --- a/paper/src/main/resources/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -Hi! It seems that you are updating the plugin from 2.2 to 2.3. -Please read this and follow the guide to update your configs -https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/2.2-to-2.3-migration-guide \ No newline at end of file diff --git a/paper/src/main/resources/config.yml b/paper/src/main/resources/config.yml deleted file mode 100644 index 9e39423..0000000 --- a/paper/src/main/resources/config.yml +++ /dev/null @@ -1,118 +0,0 @@ -# Do not change -config-version: '28' - -# Debug mode -debug: false - -# bStats -metrics: true - -# Check update -update-checker: true - -# Language -# https://github.com/Xiao-MoMi/Custom-Nameplates/tree/main/paper/src/main/resources/messages -lang: en - -# Modules -modules: - nameplates: true - backgrounds: true - bubbles: true - bossbars: true - actionbars: true - images: true - -# Integrations (Please read the wiki) -# https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/compatibility -integrations: - resource-pack: - ItemsAdder: false - ItemsAdder-old-method: false - Oraxen: false - team: - unknown: false - TAB: false - CMI: false - Velocitab: false - chat: - TrChat: false - VentureChat: false - HuskChat: false - CarbonChat: false - AdvancedChat: false - -resource-pack: - # disable resource pack generation on server start - disable-generation-on-start: false - # Namespace - namespace: "nameplates" - # Font name - font: "default" - # The initial character of the custom font - # Although Korean is used here, it does not affect the normal use of Korean in chatting, as they are not in the same font - initial-char: '뀁' - # Customize the folder where png files should be generated. - # This is useful for those who want to keep their resource pack structure in order. - image-path: - nameplates: 'font\nameplates\' - backgrounds: 'font\backgrounds\' - images: 'font\images\' - bubbles: 'font\bubbles\' - space-split: 'font\base\' - - shader: - # Enable shaders - enable: true - # Hide scoreboard numbers - hide-scoreboard-number: false - # Add Animated text support - animated-text: false - # Add ItemsAdder text effect support - ItemsAdder-text-effects: false - - transparent-bossbar: - # Make a certain bossbar transparent - color: YELLOW - # Generate the transparent bossbar for 1.20.2+ - 1_20_2+: true - # Generate the transparent bossbar for legacy versions - 1_17-1_20_1: true - - # Legacy unicode Support for 1.20+ clients - # This would make your resource pack about 900KB bigger - legacy-unicodes: true - -other-settings: - # Disable CustomNameplates' team management - # Never enable this if you are using "Team" mode for name tags - disable-team-management: false - - # Create real teams on serverside. It's not recommended to enable this unless you meet BungeeCord team bugs. - create-real-teams: false - - # It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below. - # Disabling this option would make color formatting faster - legacy-color-code-support: true - - # Thread pool settings - thread-pool-settings: - # The size of the core Thread pool, that is, the size of the Thread pool when there is no task to execute - corePoolSize: 10 - # The maximum number of threads allowed to be created in the Thread pool. The current number of threads in the Thread pool will not exceed this value - maximumPoolSize: 10 - # If a thread is idle for more than this attribute value, it will exit due to timeout - keepAliveTime: 30 - - # delay x ticks before actionbar/bossbar sent to players - send-delay: 0 - - # Set the size for the cache system. This is useful and would bring huge performance improvement if you keep it to a reasonable value - # Many developers don't seem to think that parsing text labels would incur significant performance overhead but actually it does through it's running asynchronously. - # With cache system, you can save much CPU resource when the same message is displayed to many players. - # The more people on your server, the higher the efficiency of the caching system's operation. - # In most cases you don't have to edit this value. You can increase this value appropriately, especially when there are many players on your server. - cache-size: 256 - - # Decides whether CustomNameplates should catch actionbar sent by other plugins - catch-other-plugin-actionbar: true \ No newline at end of file diff --git a/paper/src/main/resources/configs/actionbar.yml b/paper/src/main/resources/configs/actionbar.yml deleted file mode 100644 index b7d7780..0000000 --- a/paper/src/main/resources/configs/actionbar.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Player can only receive at most 1 actionbar at the same time -actionbar: - # The frequency of checking conditions for a bar, measured in ticks - check-frequency: 10 - conditions: - permission: "actionbar.show" - text-display-order: - 1: - # Measured in ticks - # -1 = infinite - duration: -1 - # Text content - text: '%nameplates_conditional_actionbar%' - # The frequency of refreshing the text, the lower the value is, the faster the placeholders are updated - # measured in ticks, -1 = disable refreshing - refresh-frequency: 1 - # Optional - # When enabling conditions, make sure that players would see at least one in the order, - # otherwise it will fall into a dead cycle - # If your player doesn't meet the conditions, it would be skipped - # This condition would only be checked once until the next turn - conditions: {} \ No newline at end of file diff --git a/paper/src/main/resources/configs/bossbar.yml b/paper/src/main/resources/configs/bossbar.yml deleted file mode 100644 index eeb5f00..0000000 --- a/paper/src/main/resources/configs/bossbar.yml +++ /dev/null @@ -1,38 +0,0 @@ -# You can create as many sections as you want for more bossbars -bossbar_1: - # Color (BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW) - color: YELLOW - # Overlay (It can be either "progress", "notched_6", "notched_10", "notched_12" or "notched_20") - overlay: PROGRESS - # The frequency of checking conditions for a bar, measured in ticks - check-frequency: 10 - conditions: - permission: "bossbar.show" - # Bar's content would be shown as the following order - text-display-order: - 1: - # Measured in ticks - duration: 100 - # Text content - text: '%nameplates_background_hello%' - # The frequency of refreshing the text, the lower the value is, the faster the placeholders are updated - # measured in ticks, -1 = disable refreshing - refresh-frequency: -1 - 2: - duration: 200 - text: '%nameplates_background_time% %nameplates_background_location% %nameplates_background_weather%' - refresh-frequency: 1 - 3: - duration: 100 - text: '%nameplates_background_update%' - refresh-frequency: -1 - # Optional - # When enabling conditions, make sure that players would see at least one in the order, - # otherwise it will fall into a dead cycle - # If your player doesn't meet the conditions, it would be skipped - # This condition would only be checked once until the next turn - conditions: - permission: nameplates.admin - equals: - value1: '%nameplates_is-latest%' - value2: 'false' \ No newline at end of file diff --git a/paper/src/main/resources/configs/bubble.yml b/paper/src/main/resources/configs/bubble.yml deleted file mode 100644 index 2ce72bf..0000000 --- a/paper/src/main/resources/configs/bubble.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Requirements for sending the bubble -requirements: - permission: bubbles.use - self-disguised: false - '!gamemode': spectator - potion-effect: "INVISIBILITY<0" - -# blacklist channels -blacklist-channels: - - Private - - Staff - -# ALL: all the players can see the bubble -# JOINED: players in the same channel can see each other's bubble -# CAN_JOIN: players that have permission to certain channels would see the bubble in that channel -channel-mode: ALL - -# Default bubble to display if player's bubble is "none" -default-bubble: 'chat' - -# Text format when bubble is "none" -default-format: - start: '' - end: '' - -# Add additional prefix and suffix to the text -text-prefix: '' -text-suffix: '' - -# Space between two bubbles -line-spacing: 0.4 - -# This decides where the bottom line is -bottom-line-Y-offset: 0.8 - -# This decides how long will the chat bubble remain (in seconds) -stay-time: 8 - -# Plugin would send another line of bubble if the text's length exceeds a certain value -characters-per-line: 30 - -# Plugin would ignore player's chat message when its length is over a certain value -max-character-length: 100 - -# Cool down (seconds) -cool-down: 1 \ No newline at end of file diff --git a/paper/src/main/resources/configs/nameplate.yml b/paper/src/main/resources/configs/nameplate.yml deleted file mode 100644 index c7ade6e..0000000 --- a/paper/src/main/resources/configs/nameplate.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Team / Unlimited / Disable -mode: TEAM - -# The duration (in seconds) that the nameplate preview will last for. -preview-duration: 5 - -# Default nameplate to display if player's nameplate is "none" -default-nameplate: 'none' - -# This decides what %nameplates_prefix/suffix% would return -nameplate: - refresh-frequency: 10 - prefix: '' - player-name: '%player_name%' - suffix: '' - -# Settings for Team mode -team: - # measured in ticks - refresh-frequency: 10 - prefix: '%nameplates_prefix%' - suffix: '%nameplates_suffix%' - # Don't enable this if you are using a custom Tab plugin - fix-Tab: false - -# Settings for Unlimited mode -unlimited: - tag_1: - # The texts to display - text: '%nameplates_nametag%' - # Verticle offset of the fake entity - vertical-offset: -1 - # Conditions (Owner side) - owner-conditions: - potion-effect: "INVISIBILITY<0" - self-disguised: false # Requires LibsDisguises - # Conditions (Viewer side) - viewer-conditions: { } - tag_2: - text: "IP: %player_ip% My IP: %viewer_player_ip%" # If a placeholder starts with "%viewer_", it would display the viewer's. - vertical-offset: -0.7 - refresh-frequency: 10 # Decides the frequency of the text refreshing - check-frequency: 20 # Decides the frequency of the condition check - owner-conditions: - potion-effect: "INVISIBILITY<0" - self-disguised: false - viewer-conditions: - condition_1: - type: permission - value: 'nameplates.admin' \ No newline at end of file diff --git a/paper/src/main/resources/contents/bubbles/chat.yml b/paper/src/main/resources/contents/bubbles/chat.yml deleted file mode 100644 index ad10457..0000000 --- a/paper/src/main/resources/contents/bubbles/chat.yml +++ /dev/null @@ -1,24 +0,0 @@ -text-format: - start: '' - end: '' -display-name: White Bubbles -left: - image: chat_left - height: 13 - ascent: 9 - width: 3 -middle: - image: chat_middle - height: 13 - ascent: 9 - width: 5 -right: - image: chat_right - height: 13 - ascent: 9 - width: 3 -tail: - image: chat_tail - height: 13 - ascent: 9 - width: 7 \ No newline at end of file diff --git a/paper/src/main/resources/contents/images/bell.yml b/paper/src/main/resources/contents/images/bell.yml deleted file mode 100644 index a628920..0000000 --- a/paper/src/main/resources/contents/images/bell.yml +++ /dev/null @@ -1,4 +0,0 @@ -image: bell -height: 10 -ascent: 4 -width: 10 diff --git a/paper/src/main/resources/contents/images/bubble.yml b/paper/src/main/resources/contents/images/bubble.yml deleted file mode 100644 index 5fe2085..0000000 --- a/paper/src/main/resources/contents/images/bubble.yml +++ /dev/null @@ -1,4 +0,0 @@ -image: bubble -height: 10 -ascent: 4 -width: 10 diff --git a/paper/src/main/resources/contents/images/clock.yml b/paper/src/main/resources/contents/images/clock.yml deleted file mode 100644 index c8845b8..0000000 --- a/paper/src/main/resources/contents/images/clock.yml +++ /dev/null @@ -1,4 +0,0 @@ -image: clock -height: 10 -ascent: 4 -width: 10 diff --git a/paper/src/main/resources/contents/images/coin.yml b/paper/src/main/resources/contents/images/coin.yml deleted file mode 100644 index 8423fe4..0000000 --- a/paper/src/main/resources/contents/images/coin.yml +++ /dev/null @@ -1,4 +0,0 @@ -image: coin -height: 10 -ascent: -14 -width: 10 diff --git a/paper/src/main/resources/contents/images/compass.yml b/paper/src/main/resources/contents/images/compass.yml deleted file mode 100644 index 78bcc91..0000000 --- a/paper/src/main/resources/contents/images/compass.yml +++ /dev/null @@ -1,4 +0,0 @@ -image: compass -height: 10 -ascent: 4 -width: 10 diff --git a/paper/src/main/resources/contents/images/weather.yml b/paper/src/main/resources/contents/images/weather.yml deleted file mode 100644 index d13066d..0000000 --- a/paper/src/main/resources/contents/images/weather.yml +++ /dev/null @@ -1,4 +0,0 @@ -image: weather -height: 10 -ascent: 4 -width: 10 diff --git a/paper/src/main/resources/font/default.json b/paper/src/main/resources/font/default.json deleted file mode 100644 index a6f291d..0000000 --- a/paper/src/main/resources/font/default.json +++ /dev/null @@ -1,191 +0,0 @@ -{ - "providers": [ - { - "type": "space", - "advances": { - " ": 4, - "\u200c": 0 - } - }, - { - "type": "bitmap", - "file": "minecraft:font/nonlatin_european.png", - "ascent": %ascent%, - "chars": [ - "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392", - "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3", - "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba", - "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402", - "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a", - "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a", - "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b", - "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b", - "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d", - "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8", - "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04c0\u0472\u0473", - "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122", - "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2", - "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd", - "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5", - "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036", - "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117", - "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665", - "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u02ac\u26a1", - "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2e41\u2e4b\u295d\u1614\u0190\u07c8", - "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\ua7b0\ua780\u0500\ua779\u1d1a\u27d8\u2229", - "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c", - "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540", - "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551", - "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a", - "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a", - "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538", - "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c", - "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5", - "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8", - "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535", - "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af", - "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0", - "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0", - "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0", - "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0", - "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0", - "\u05f1\u05f2\u05be\u05c3\u05c6\u00b4\u00a8\u1d00\u0299\u1d04\u1d05\u1d07\ua730\u0262\u029c\u1d0a", - "\u1d0b\u029f\u1d0d\u0274\u1d0f\u1d18\ua7af\u0280\ua731\u1d1b\u1d1c\u1d20\u1d21\u028f\u1d22\u00a7", - "\u0271\u0273\u0272\u0288\u0256\u0261\u02a1\u0255\u0291\u0278\u029d\u02a2\u027b\u0281\u0266\u028b", - "\u0270\u026c\u026e\u0298\u01c0\u01c3\u01c2\u01c1\u0253\u0257\u1d91\u0284\u0260\u029b\u0267\u026b", - "\u0268\u0289\u028a\u0258\u0275\u0264\u025c\u025e\u0251\u0252\u025a\u025d\u0181\u0189\u0191\u01a9", - "\u01b2\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae", - "\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be", - "\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c7\u10cd\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6", - "\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6", - "\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6", - "\u10f7\u10f8\u10f9\u10fa\u10fb\u10fc\u10fd\u10fe\u10ff\ufb4a\ufb2b\ufb4e\ufb44\ufb3b\ufb1f\ufb1d", - "\ufb4b\ufb35\ufb4c\ufb31\ua727\ua726\u027a\u2c71\u02a0\u0297\u0296\u026d\u0277\u027f\u0285\u0286", - "\u0293\u029a\u20aa\u20be\u058a\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d21\u2d07\u2d08\u2d09", - "\u2d0a\u2d0b\u2d0c\u2d22\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d23\u2d13\u2d14\u2d15\u2d16\u2d17", - "\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d24\u2d1f\u2d20\u2d25\u215b\u215c\u215d\u215e\u2153", - "\u2154\u2709\u2602\u2614\u2604\u26c4\u2603\u231b\u231a\u2690\u270e\u2763\u2664\u2667\u2661\u2662", - "\u26c8\u2630\u2631\u2633\u2634\u2636\u2637\u2194\u21d2\u21cf\u21d4\u21f5\u2200\u2203\u2204\u2209", - "\u220b\u220c\u2282\u2283\u2284\u2285\u2227\u2228\u22bb\u22bc\u22bd\u2225\u2262\u22c6\u2211\u22a4", - "\u22a5\u22a2\u22a8\u2254\u2201\u2234\u2235\u221b\u221c\u2202\u22c3\u2286\u2287\u25a1\u25b3\u25b7", - "\u25bd\u25c1\u25c6\u25c7\u25cb\u25ce\u2606\u2605\u2718\u2080\u2081\u2082\u2083\u2084\u2085\u2086", - "\u2087\u2088\u2089\u208a\u208b\u208c\u208d\u208e\u222b\u222e\u221d\u2300\u2302\u2318\u3012\u027c", - "\u0184\u0185\u1e9f\u023d\u019a\u019b\u0220\u019e\u019f\u01a7\u01a8\u01aa\u01b8\u01b9\u01bb\u01bc", - "\u01bd\u01be\u0221\u0234\u0235\u0236\u023a\u2c65\u023b\u023c\u0246\u0247\u023e\u2c66\u0241\u0242", - "\u0243\u0244\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u1e9c\u1e9d\u1efc\u1efd\u1efe\u1eff", - "\ua7a8\ua7a9\ud800\udf30\ud800\udf31\ud800\udf32\ud800\udf33\ud800\udf34\ud800\udf35\ud800\udf36\ud800\udf37\ud800\udf38\ud800\udf39\ud800\udf3a\ud800\udf3b\ud800\udf3c\ud800\udf3d", - "\ud800\udf3e\ud800\udf3f\ud800\udf40\ud800\udf41\ud800\udf42\ud800\udf43\ud800\udf44\ud800\udf45\ud800\udf46\ud800\udf47\ud800\udf48\ud800\udf49\ud800\udf4a\ud83c\udf27\ud83d\udd25\ud83c\udf0a", - "\u2150\u2151\u2155\u2156\u2157\u2159\u215a\u215f\u2189\ud83d\udde1\ud83c\udff9\ud83e\ude93\ud83d\udd31\ud83c\udfa3\ud83e\uddea\u2697", - "\u2bea\u2beb\u2c6d\ud83d\udee1\u2702\ud83c\udf56\ud83e\udea3\ud83d\udd14\u23f3\u2691\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5", - "\u20a6\u20a9\u20ab\u20ad\u20ae\u20b0\u20b1\u20b2\u20b3\u20b5\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb", - "\u20bc\u20bf\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" - ] - }, - { - "type": "bitmap", - "file": "minecraft:font/accented.png", - "height": 12, - "ascent": %ASCENT%, - "chars": [ - "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf", - "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3", - "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa", - "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b", - "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b", - "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129", - "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c", - "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d", - "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d", - "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d", - "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d", - "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f", - "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403", - "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f", - "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57", - "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb", - "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116", - "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc", - "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761", - "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda", - "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3", - "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf", - "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7", - "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e", - "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499", - "\u04a0\u04a1\u04aa\u04ab\u01f6\u26a0\u24ea\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468", - "\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u24b6\u24b7\u24b8\u24b9\u24ba", - "\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca", - "\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da", - "\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u0327", - "\u0282\u0290\u0276\u01cd\u01ce\u01de\u01df\u01fa\u01fb\u0202\u0203\u0226\u0227\u01e0\u01e1\u1e00", - "\u1e01\u0200\u0201\u1e06\u1e07\u1e04\u1e05\u1d6c\u1e08\u1e09\u1e10\u1e11\u1e12\u1e13\u1e0e\u1e0f", - "\u1e0c\u1d6d\u1e14\u1e15\u1e16\u1e17\u1e18\u1e19\u1e1c\u1e1d\u0228\u0229\u1e1a\u1e1b\u0204\u0205", - "\u0206\u1d6e\u01f4\u01f5\u01e6\u1e26\u1e27\u1e28\u1e29\u1e2a\u1e2b\u021e\u021f\u1e24\u1e96\u1e2e", - "\u1e2f\u020a\u020b\u01cf\u01d0\u0208\u0209\u1e2c\u1e2d\u01f0\u0237\u01e8\u01e9\u1e32\u1e33\u1e34", - "\u1e35\u1e3a\u1e3b\u1e3c\u1e3d\u1e36\u1e37\u1e38\u1e39\u2c62\u1e3e\u1e3f\u1e42\u1e43\u1d6f\u1e44", - "\u1e45\u1e46\u1e47\u1e4a\u1e4b\u01f8\u01f9\u1e48\u1e49\u1d70\u01ec\u01ed\u022c\u022d\u1e4c\u1e4d", - "\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u020e\u020f\u022a\u022b\u01d1\u01d2\u022e\u022f\u0230\u0231", - "\u020c\u020d\u01ea\u1e54\u1e55\u1d71\u0212\u0213\u1e58\u1e59\u1e5c\u1e5d\u1e5e\u1e5f\u0210\u0211", - "\u1e5a\u1d73\u1d72\u1e64\u1e65\u1e66\u1e67\u1e62\u1e63\u1e68\u1e69\u1d74\u1e70\u1e71\u1e6e\u1e6f", - "\u1e6c\u1e97\u1d75\u1e72\u1e73\u1e76\u1e77\u1e78\u1e79\u1e7a\u1e7b\u01d3\u01d4\u01d5\u01d6\u01d7", - "\u01d8\u01d9\u01da\u01db\u01dc\u1e74\u1e75\u0214\u0215\u0216\u1e7e\u1e7f\u1e7c\u1e7d\u1e86\u1e87", - "\u1e88\u1e89\u1e98\u1e8c\u1e8d\u1e8a\u1e8b\u0232\u0233\u1e8e\u1e8f\u1e99\u1e94\u1e95\u1e90\u1e91", - "\u1e93\u1d76\u01ee\u01ef\u1e9b\ua73e\ua73f\u01e2\u01e3\u1d7a\u1efb\u1d02\u1d14\uab63\u0238\u02a3", - "\u02a5\u02a4\u02a9\u02aa\u02ab\u0239\u02a8\u02a6\u02a7\uab50\uab51\u20a7\u1efa\ufb2e\ufb2f\u0180", - "\u0182\u0183\u0187\u0188\u018a\u018b\u018c\u0193\u01e4\u01e5\u0197\u0196\u0269\u0198\u0199\u019d", - "\u01a4\u01a5\u027d\u01a6\u01ac\u01ad\u01ab\u01ae\u0217\u01b1\u019c\u01b3\u01b4\u01b5\u01b6\u01a2", - "\u01a3\u0222\u0223\u02ad\u02ae\u02af\ufb14\ufb15\ufb17\ufb16\ufb13\u04d0\u04d1\u04d2\u04d3\u04f6", - "\u04f7\u0494\u0495\u04d6\u04d7\u04bc\u04bd\u04be\u04bf\u04da\u04db\u04dc\u04dd\u04c1\u04c2\u04de", - "\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04ea\u04eb\u04f0\u04f1\u04ee\u04ef\u04f2\u04f3\u04f4", - "\u04f5\u04f8\u04f9\u04ec\u04ed\u0476\u0477\u04d4\u04fa\u0502\ua682\ua680\ua688\u052a\u052c\ua684", - "\u0504\u0510\u04e0\u0506\u048a\u04c3\u049e\u049c\u051e\u051a\u04c5\u052e\u0512\u0520\u0508\u0514", - "\u04cd\u04c9\u0528\u04c7\u04a4\u0522\u050a\u04a8\u0524\u04a6\u048e\u0516\u050c\ua690\u04ac\ua68a", - "\ua68c\u050e\u04b2\u04fc\u04fe\u0526\ua694\u04b4\ua68e\u04b6\u04cb\u04b8\ua692\ua696\ua686\u048c", - "\u0518\u051c\u04d5\u04fb\u0503\ua683\ua681\ua689\u052b\u052d\ua685\u0505\u0511\u04e1\u0507\u048b", - "\u04c4\u049f\u049d\u051f\u051b\u04c6\u052f\u0513\u0521\u0509\u0515\u04ce\u04ca\u0529\u04c8\u04a5", - "\u0523\u050b\u04a9\u0525\u04a7\u048f\u0517\u050d\ua691\u04ad\ua68b\ua68d\u050f\u04b3\u04fd\u04ff", - "\u0527\ua695\u04b5\ua68f\u04b7\u04cc\u04b9\ua693\ua697\ua687\u048d\u0519\u051d\u1f08\u1f00\u1f09", - "\u1f01\u1f0a\u1f02\u1f0b\u1f03\u1f0c\u1f04\u1f0d\u1f05\u1f0e\u1f06\u1f0f\u1f07\u1fba\u1f70\u1fb8", - "\u1fb0\u1fb9\u1fb1\u1fbb\u1f71\u1f88\u1f80\u1f89\u1f81\u1f8a\u1f82\u1f8b\u1f83\u1f8c\u1f84\u1f8d", - "\u1f85\u1f8e\u1f86\u1f8f\u1f87\u1fbc\u1fb4\u1fb6\u1fb7\u1fb2\u1fb3\u1f18\u1f10\u1f19\u1f11\u1f1a", - "\u1f12\u1f1b\u1f13\u1f1c\u1f14\u1f1d\u1f15\u1fc8\u1fc9\u1f72\u1f73\u1f28\u1f20\u1fca\u1f74\u1f29", - "\u1f21\u1f2a\u1f22\u1f2b\u1f23\u1f2c\u1f24\u1f2d\u1f25\u1f2e\u1f26\u1f2f\u1f27\u1f98\u1f90\u1f99", - "\u1f91\u1f9a\u1f92\u1f9b\u1f93\u1f9c\u1f94\u1f9d\u1f95\u1f9e\u1f96\u1f9f\u1f97\u1fcb\u1f75\u1fcc", - "\u1fc3\u1fc2\u1fc4\u1fc6\u1fc7\u1fda\u1f76\u1fdb\u1f77\u1f38\u1f30\u1f39\u1f31\u1f3a\u1f32\u1f3b", - "\u1f33\u1f3c\u1f34\u1f3d\u1f35\u1f3e\u1f36\u1f3f\u1f37\u1fd8\u1fd0\u1fd9\u1fd1\u1fd2\u1fd3\u1fd6", - "\u1fd7\u1ff8\u1f78\u1ff9\u1f79\u1f48\u1f40\u1f49\u1f41\u1f4a\u1f42\u1f4b\u1f43\u1f4c\u1f44\u1f4d", - "\u1f45\u1fec\u1fe4\u1fe5\u1fea\u1f7a\u1feb\u1f7b\u1f59\u1f51\u1f5b\u1f53\u1f5d\u1f55\u1f5f\u1f57", - "\u1fe8\u1fe0\u1fe9\u1fe1\u03d3\u03d4\u1fe2\u1fe3\u1fe7\u1f50\u1f52\u1f54\u1fe6\u1f56\u1ffa\u1f7c", - "\u1ffb\u1f7d\u1f68\u1f60\u1f69\u1f61\u1f6a\u1f62\u1f6b\u1f63\u1f6c\u1f64\u1f6d\u1f65\u1f6e\u1f66", - "\u1f6f\u1f67\u1fa8\u1fa0\u1fa9\u1fa1\u1faa\u1fa2\u1fab\u1fa3\u1fac\u1fa4\u1fad\u1fa5\u1fae\u1fa6", - "\u1faf\u1fa7\u1ffc\u1ff3\u1ff2\u1ff4\u1ff6\u1ff7\u262f\u2610\u2611\u2612\u018d\u01ba\u2c7e\u023f", - "\u2c7f\u0240\u1d80\ua7c4\ua794\u1d81\u1d82\u1d83\ua795\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a", - "\u1d8b\u1d8c\u1d8d\ua7c6\u1d8e\u1d8f\u1d90\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a", - "\u1e9a\u2152\u2158\u20a8\u20af\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" - ] - }, - { - "type": "bitmap", - "file": "minecraft:font/ascii.png", - "ascent": %ascent%, - "chars": [ - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f", - "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f", - "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f", - "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f", - "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f", - "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00a3\u0000\u0000\u0192", - "\u0000\u0000\u0000\u0000\u0000\u0000\u00aa\u00ba\u0000\u0000\u00ac\u0000\u0000\u0000\u00ab\u00bb", - "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510", - "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567", - "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580", - "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u2205\u2208\u0000", - "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u0000\u221a\u207f\u00b2\u25a0\u0000" - ] - } - ] -} diff --git a/paper/src/main/resources/font/unifont.zip b/paper/src/main/resources/font/unifont.zip deleted file mode 100644 index daac8e1..0000000 Binary files a/paper/src/main/resources/font/unifont.zip and /dev/null differ diff --git a/paper/src/main/resources/messages/en.yml b/paper/src/main/resources/messages/en.yml deleted file mode 100644 index 5334968..0000000 --- a/paper/src/main/resources/messages/en.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: 'Reloaded! ' - cooldown: 'Previewing is still Ongoing!' - generate: 'Generating resource pack...' - generate-done: 'Resource Pack has been generated! Don''t forget to reinstall your resource pack.' - no-nameplate: 'No Nameplate' - preview: 'Now previewing your nameplate (go to third person to view)!' - - equip-nameplates: 'Successfully equipped nameplate {Nameplate}. [click to preview]' - force-equip-nameplates: 'Successfully equipped nameplate {Nameplate} to {Player}.' - unequip-nameplates: 'You removed your nameplate' - force-unequip-nameplates: 'Successfully removed {Player}''s nameplate!' - not-exist-nameplates: 'This nameplate does not exist!' - not-available-nameplates: 'This nameplate is currently not available!' - available-nameplates: 'Available nameplates: {Nameplate}.' - have-no-nameplates: 'You don''t have any nameplate yet' - force-preview: 'Forced {Player} to preview the nameplate' - - equip-bubbles: 'Successfully equipped chatting bubbles {Bubble}' - force-equip-bubbles: 'Successfully equipped chatting bubbles {Bubble}' - unequip-bubbles: 'You removed your chatting bubbles' - force-unequip-bubbles: 'Successfully removed {Player}''s bubbles!' - not-exist-bubbles: 'This bubble does not exist!' - not-available-bubbles: 'This bubble is currently not available!' - available-bubbles: 'Available bubbles: {Bubble}' - have-no-bubbles: 'You don''t have any bubble yet' \ No newline at end of file diff --git a/paper/src/main/resources/messages/es.yml b/paper/src/main/resources/messages/es.yml deleted file mode 100644 index ee2235f..0000000 --- a/paper/src/main/resources/messages/es.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: 'Recargado! ' - cooldown: '¡La previsualización sigue en curso!' - generate: 'Generando paquete de recursos...' - generate-done: 'Se ha generado el paquete de recursos. No olvides reinstalarlo!' - no-nameplate: 'Sin Nameplate' - preview: '¡Ya puedes ver tu Nameplate (cambia a tercera persona para ver)!' - - equip-nameplates: 'Se ha equipado la Nameplate {Nameplate}. [click para preview]' - force-equip-nameplates: 'Se le ha equipado la Nameplate {Nameplate} a {Player}.' - unequip-nameplates: 'Has quitado tu nameplate' - force-unequip-nameplates: 'Le has quitado su nameplate a {Player}!' - not-exist-nameplates: 'Esta nameplate no existe!' - not-available-nameplates: 'Esta nameplate no está disponible!' - available-nameplates: 'Nameplates Disponibles: {Nameplate}.' - have-no-nameplates: 'Aún no tienes una nameplate' - force-preview: 'Forzaste a {Player} a ver su nameplate' - - equip-bubbles: 'Has activado los globos de diálogo {Bubble}' - force-equip-bubbles: 'Has activado los globos de diálogo {Bubble}' - unequip-bubbles: 'Se han desactivado los globos de diálogo' - force-unequip-bubbles: 'Has desactivado los globos de diálogo de {Player}!' - not-exist-bubbles: 'Este globo no existe!' - not-available-bubbles: 'Este globo no está disponible!' - available-bubbles: 'Globos Disponibles: {Bubble}' - have-no-bubbles: 'Aún no tienes un globo de diálogo' \ No newline at end of file diff --git a/paper/src/main/resources/messages/fr.yml b/paper/src/main/resources/messages/fr.yml deleted file mode 100644 index 18cedde..0000000 --- a/paper/src/main/resources/messages/fr.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: 'Rechargé ! ' - cooldown: 'La prévisualisation est toujours en cours !' - generate: 'Le pack de ressources est en cours de génération...' - generate-done: "Le pack de ressources a été généré ! N'oubliez pas de réinstaller votre pack de ressources." - no-nameplate: 'Pas de plaque de nom' - preview: 'Prévisualisation en cours de votre plaque de nom (passez en vue à la troisième personne pour la voir) !' - - equip-nameplates: 'Plaque de nom {Nameplate} équipée avec succès. [cliquez pour prévisualiser]' - force-equip-nameplates: 'Plaque de nom {Nameplate} équipée avec succès pour {Player}.' - unequip-nameplates: 'Vous avez retiré votre plaque de nom' - force-unequip-nameplates: 'La plaque de nom de {Player} a été retirée avec succès !' - not-exist-nameplates: 'Cette plaque de nom n'existe pas !' - not-available-nameplates: 'Cette plaque de nom n'est actuellement pas disponible !' - available-nameplates: 'Plaques de nom disponibles : {Nameplate}.' - have-no-nameplates: 'Vous n'avez pas encore de plaque de nom' - force-preview: 'Forced {Player} to preview the nameplate' - - equip-bubbles: 'Bulles de discussion {Bubble} équipées avec succès' - force-equip-bubbles: 'Bulles de discussion {Bubble} équipées avec succès' - unequip-bubbles: 'Vous avez retiré vos bulles de discussion' - force-unequip-bubbles: 'Les bulles de discussion de {Player} ont été retirées avec succès !' - not-exist-bubbles: 'Cette bulle de discussion n'existe pas !' - not-available-bubbles: 'Cette bulle de discussion n'est actuellement pas disponible !' - available-bubbles: 'Bulles de discussion disponibles : {Bubble}.' - have-no-bubbles: 'Vous n'avez pas encore de bulles de discussion' diff --git a/paper/src/main/resources/messages/kr.yml b/paper/src/main/resources/messages/kr.yml deleted file mode 100644 index 0706691..0000000 --- a/paper/src/main/resources/messages/kr.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: '리로드 했습니다! ' - cooldown: '미리보기가 아직 켜져있습니다!' - generate: '리소스팩을 생성하는 중입니다...' - generate-done: '리소스팩이 생성되었습니다! 리소스팩을 재설치하는것을 잊지 마세요.' - no-nameplate: '이름표 없음' - preview: '이제 이름표 미리보기가 표시됩니다 (3인칭 보기로 전환하세요)!' - - equip-nameplates: '이름표 {Nameplate}을(를) 착용했습니다. [클릭하여 미리보기]' - force-equip-nameplates: '이름표 {Nameplate}을(를) {Player}에게 착용시켰습니다.' - unequip-nameplates: '이름표를 착용 해제했습니다' - force-unequip-nameplates: '{Player}의 이름표를 제거했습니다!' - not-exist-nameplates: '해당 이름표가 존재하지 않습니다!' - not-available-nameplates: '해당 이름표는 현재 이용할 수 없습니다!' - available-nameplates: '이용 가능한 이름표: {Nameplate}.' - have-no-nameplates: '아직 아무 이름표도 가지고 있지 않습니다' - force-preview: '{Player}에게 이름표 미리보기를 표시했습니다' - - equip-bubbles: '대화 말풍선 {Bubble}을(를) 착용했습니다' - force-equip-bubbles: '말풍선 {Bubble}을(를) 착용했습니다' - unequip-bubbles: '말풍선을 착용 해제했습니다' - force-unequip-bubbles: '{Player}의 말풍선을 착용 해제했습니다!' - not-exist-bubbles: '해당 말풍선이 존재하지 않습니다!' - not-available-bubbles: '해당 말풍선은 현재 이용할 수 없습니다!' - available-bubbles: '이용 가능한 말풍선: {Bubble}' - have-no-bubbles: '아직 아무 말풍선도 가지고 있지 않습니다' \ No newline at end of file diff --git a/paper/src/main/resources/messages/pl.yml b/paper/src/main/resources/messages/pl.yml deleted file mode 100644 index 469d562..0000000 --- a/paper/src/main/resources/messages/pl.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: 'Pliki konfiguracyjne przeładowane! ' - cooldown: 'Podgląd nadal trwa!' - generate: 'Generowanie resource packa...' - generate-done: 'Resource Pack został wygenerowany! Nie zapomnij go zainstalować jeszcze raz.' - no-nameplate: 'Bez nameplate''a' - preview: 'Podgląd nameplate''a (zobacz w trybie trzecioosobowym)!' - - equip-nameplates: 'Pomyślnie założono nameplate''a: {Nameplate}. [Kliknij aby zobaczyć]' - force-equip-nameplates: 'Pomyślnie założono nameplat''a {Nameplate} graczowi {Player}.' - unequip-nameplates: 'Zdjąłeś nameplate''a' - force-unequip-nameplates: 'Pomyślnie usunięto nameplate''a graczowi {Player}' - not-exist-nameplates: 'Nie istnieje taki nameplate!' - not-available-nameplates: 'Ten nameplate obecnie nie jest dostępny!' - available-nameplates: 'Dostępne nameplate''y: {Nameplate}.' - have-no-nameplates: 'Na razie nie posiadasz żadnych nameplat''ów' - force-preview: 'Zmuszono {Player} do podglądu nameplate''a' - - equip-bubbles: 'Pomyślnie założono bąbelki {Bubble}' - force-equip-bubbles: 'Pomyślnie założono bąbelki dla chatu {Bubble}' - unequip-bubbles: 'Usunąłeś bąbelki dla chatu' - force-unequip-bubbles: 'Usunąłeś bąbelki gracza {Player}' - not-exist-bubbles: 'Nie istnieją takie bąbelki!' - not-available-bubbles: 'Te bąbelki na razie nie są dostępne!' - available-bubbles: 'Dostępne bąbelki: {Bubble}' - have-no-bubbles: 'Nie posiadasz na razie żadnych bąbelków' diff --git a/paper/src/main/resources/messages/ru.yml b/paper/src/main/resources/messages/ru.yml deleted file mode 100644 index 743be00..0000000 --- a/paper/src/main/resources/messages/ru.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: 'Перезагружено! ' - cooldown: 'Предпросмотр до сих пор активен!' - generate: 'Ресурспак генерируется...' - generate-done: 'Ресурспак сгенерирован! Не забудьте переустановить свой ресурспак.' - no-nameplate: 'Нет Нэймплейта' - preview: 'Сейчас вам отображается ваш Нэймплейт (перейдите в 3-е лицо, чтобы его увидеть)!' - - equip-nameplates: 'Успешно установлен Нэймплейт {Nameplate}. [нажмите для предпросмотра]' - force-equip-nameplates: 'Успешно установлен Нэймплейт {Nameplate} у {Player}.' - unequip-nameplates: 'Вы сняли свой Нэймплейт' - force-unequip-nameplates: 'Успешно снят Нэймплейт у {Player}!' - not-exist-nameplates: 'Такой Нэйплейт не существует!' - not-available-nameplates: 'Этот Нэймплейт сейчас недоступен!' - available-nameplates: 'Доступные Нэймплейты: {Nameplate}.' - have-no-nameplates: 'У вас еще нет никаких Нэймплейтов.' - force-preview: 'Forced {Player} to preview the nameplate' - - equip-bubbles: 'Успешно установлен стиль чата над головой: {Bubble}' - force-equip-bubbles: 'Успешно установлен стиль чата над головой: {Bubble}' - unequip-bubbles: 'Вы сняли стиль чата над головой.' - force-unequip-bubbles: 'Успешно снят стиль чата над головой у {Player}!' - not-exist-bubbles: 'Такого стиля чата над головой не существует!' - not-available-bubbles: 'Этот стиль чата над головой сейчас недоступен!' - available-bubbles: 'Доступные стили чата над головой: {Bubble}' - have-no-bubbles: 'У вас еще нет никаких стилей чата над головой.' diff --git a/paper/src/main/resources/messages/tr.yml b/paper/src/main/resources/messages/tr.yml deleted file mode 100644 index 2c3c1b8..0000000 --- a/paper/src/main/resources/messages/tr.yml +++ /dev/null @@ -1,30 +0,0 @@ -config-version: "23" - -#Turkish language file by WinTone01 -messages: - prefix: '[CustomNameplates] ' - reload: 'Yeniden Yüklendi! ' - cooldown: 'Önizleme hala devam ediyor!' - generate: 'Kaynak paketi oluşturuluyor...' - generate-done: 'Kaynak paketi oluşturuldu! Kaynak paketinizi yeniden yüklemeyi unutmayın.' - no-nameplate: 'İsim Etiketi Yok' - preview: 'Şu anda isim etiketinizi önizliyorsunuz (görüntülemek için üçüncü şahsa geçin)!' - - equip-nameplates: 'İsim etiketi {Nameplate} başarıyla eklendi. [Önizlemek için tıklayın]' - force-equip-nameplates: '{Player} oyuncusuna başarıyla isim etiketi {Nameplate} eklendi.' - unequip-nameplates: 'İsim etiketinizi kaldırdınız' - force-unequip-nameplates: '{Player} oyuncusunun isim etiketi başarıyla kaldırıldı!' - not-exist-nameplates: 'Bu isim etiketi mevcut değil!' - not-available-nameplates: 'Bu isim etiketi şu anda kullanılabilir değil!' - available-nameplates: 'Mevcut isim etiketleri: {Nameplate}.' - have-no-nameplates: 'Henüz hiç isim etiketiniz yok' - force-preview: 'Forced {Player} to preview the nameplate' - - equip-bubbles: 'Sohbet balonlarınız başarıyla eklendi {Bubble}' - force-equip-bubbles: '{Bubble} sohbet balonları başarıyla eklendi.' - unequip-bubbles: 'Sohbet balonlarınızı kaldırdınız' - force-unequip-bubbles: '{Player} oyuncusunun sohbet balonları başarıyla kaldırıldı!' - not-exist-bubbles: 'Bu balon mevcut değil!' - not-available-bubbles: 'Bu balon şu anda kullanılabilir değil!' - available-bubbles: 'Mevcut sohbet balonları: {Bubble}.' - have-no-bubbles: 'Henüz hiç sohbet balonunuz yok' \ No newline at end of file diff --git a/paper/src/main/resources/messages/zh_cn.yml b/paper/src/main/resources/messages/zh_cn.yml deleted file mode 100644 index 1c783dc..0000000 --- a/paper/src/main/resources/messages/zh_cn.yml +++ /dev/null @@ -1,29 +0,0 @@ -config-version: "23" - -messages: - prefix: '[CustomNameplates] ' - reload: '插件已重载!.' - cooldown: '上一个预览还没结束!' - generate: '正在生成资源包...' - generate-done: '资源包生成完毕...请不要忘记重载你的资源包' - no-nameplate: '无铭牌' - preview: '你正在预览你的铭牌(切换到第三人称)' - - equip-nameplates: '你已佩戴铭牌 {Nameplate} [点击预览]' - force-equip-nameplates: '你已为玩家 {Player} 强制佩戴了铭牌 {Nameplate}' - unequip-nameplates: '你已卸下铭牌!' - force-unequip-nameplates: '你已强制卸下玩家 {Player} 的铭牌!' - not-exist-nameplates: '那个铭牌不存在!' - not-available-nameplates: '你还未拥有这个铭牌!' - available-nameplates: '可用铭牌: {Nameplate}' - have-no-nameplates: '你还没有拥有任何铭牌.' - force-preview: '正在强制玩家 {Player} 预览铭牌' - - equip-bubbles: '你已佩戴聊天气泡 {Bubble}' - unequip-bubbles: '你已卸下聊天气泡' - available-bubbles: '可用聊天气泡: {Bubble}' - force-equip-bubbles: '成功使用聊天气泡 {Bubble}' - force-unequip-bubbles: '你移除了 {Player} 的聊天气泡' - not-exist-bubbles: '那个聊天气泡不存在!' - not-available-bubbles: '你还未拥有这个气泡!' - have-no-bubbles: '你还没有拥有任何气泡.' diff --git a/paper/src/main/resources/plugin.yml b/paper/src/main/resources/plugin.yml deleted file mode 100644 index e9e4d53..0000000 --- a/paper/src/main/resources/plugin.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: CustomNameplates -version: '${version}' -main: net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl -api-version: 1.17 -authors: [ XiaoMoMi ] -folia-supported: true -depend: [ ProtocolLib ,PlaceholderAPI ] -softdepend: [ MagicCosmetics, TAB, CMI, TrChat, ItemsAdder, Oraxen, CarbonChat, HuskChat, AdvancedChat ] -permissions: - nameplates.command.*: - description: Gives access to all nameplate user commands - children: - nameplates.command.equip: true - nameplates.command.unequip: true - nameplates.command.preview: true - nameplates.command.list: true - nameplates.command.equip: - default: true - nameplates.command.unequip: - default: true - nameplates.command.preview: - default: true - nameplates.command.list: - default: true - customnameplates.admin: - default: op - bubbles.command.*: - description: Gives access to bubble all user commands - children: - bubbles.equip: true - bubbles.unequip: true - bubbles.list: true - bubbles.command.equip: - default: true - bubbles.command.unequip: - default: true - bubbles.command.list: - default: true - bubbles.use: - default: true \ No newline at end of file diff --git a/paper/src/main/resources/space_split.png b/paper/src/main/resources/space_split.png deleted file mode 100644 index 6e70543..0000000 Binary files a/paper/src/main/resources/space_split.png and /dev/null differ diff --git a/settings.gradle.kts b/settings.gradle.kts index d5dbff5..c2c898c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,6 @@ rootProject.name = "CustomNameplates" include("api") -include("bungeecord") -include("velocity") -include("paper") +include("bukkit") include("common") -include("universe") +include("backend") +include("compatibility") diff --git a/universe/.gitignore b/universe/.gitignore deleted file mode 100644 index b63da45..0000000 --- a/universe/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/universe/build.gradle.kts b/universe/build.gradle.kts deleted file mode 100644 index b3fd73a..0000000 --- a/universe/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -dependencies { - implementation(project(":api")) - implementation(project(":common")) - implementation(project(":paper")) - implementation(project(":velocity")) - implementation(project(":bungeecord")) -} - -tasks { - shadowJar { - relocate ("net.kyori", "net.momirealms.customnameplates.libraries") - relocate ("org.bstats", "net.momirealms.customnameplates.libraries.bstats") - relocate ("net.momirealms.sparrow.heart", "net.momirealms.customnameplates.libraries.sparrow") - relocate ("org.apache.commons.pool2", "net.momirealms.customnameplates.libraries.commonspool2") - relocate ("com.mysql", "net.momirealms.customnameplates.libraries.mysql") - relocate ("org.mariadb", "net.momirealms.customnameplates.libraries.mariadb") - relocate ("com.zaxxer.hikari", "net.momirealms.customnameplates.libraries.hikari") - relocate ("redis.clients.jedis", "net.momirealms.customnameplates.libraries.jedis") - relocate ("com.mongodb", "net.momirealms.customnameplates.libraries.mongodb") - relocate ("org.bson", "net.momirealms.customnameplates.libraries.bson") - relocate ("dev.jorel.commandapi", "net.momirealms.customnameplates.libraries.commandapi") - relocate ("dev.dejvokep.boostedyaml", "net.momirealms.customnameplates.libraries.boostedyaml") - } -} \ No newline at end of file diff --git a/velocity/.gitignore b/velocity/.gitignore deleted file mode 100644 index b63da45..0000000 --- a/velocity/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts deleted file mode 100644 index bdbae33..0000000 --- a/velocity/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -dependencies { - implementation(project(":common")) - compileOnly("io.netty:netty-codec-http:4.1.105.Final") - // velocity - compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") - compileOnly("com.velocitypowered:velocity-proxy:3.3.0-SNAPSHOT") - annotationProcessor("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") - // velocitab - compileOnly("net.william278:velocitab:1.6.1") -} \ No newline at end of file diff --git a/velocity/src/main/java/net/momirealms/customnameplates/velocity/CustomNameplatesVelocity.java b/velocity/src/main/java/net/momirealms/customnameplates/velocity/CustomNameplatesVelocity.java deleted file mode 100644 index 53e0920..0000000 --- a/velocity/src/main/java/net/momirealms/customnameplates/velocity/CustomNameplatesVelocity.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.velocity; - -import com.google.common.io.ByteArrayDataInput; -import com.google.common.io.ByteStreams; -import com.google.inject.Inject; -import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.connection.PluginMessageEvent; -import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; -import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; -import com.velocitypowered.api.plugin.Dependency; -import com.velocitypowered.api.plugin.Plugin; -import com.velocitypowered.api.plugin.PluginContainer; -import com.velocitypowered.api.proxy.Player; -import com.velocitypowered.api.proxy.ProxyServer; -import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.momirealms.customnameplates.common.message.MessageType; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.common.team.TeamTagVisibility; -import net.momirealms.customnameplates.velocity.team.VelocitabManager; -import net.momirealms.customnameplates.velocity.team.VelocityTeamManager; -import org.slf4j.Logger; - -import java.time.Duration; -import java.util.Optional; - -@Plugin( - id = "customnameplates", - name = "CustomNameplates", - version = "2.3", - authors = {"XiaoMoMi"}, - dependencies = {@Dependency(id = "velocitab", optional = true)} -) -public class CustomNameplatesVelocity { - - private static final String CHANNEL = "customnameplates:cnp"; - private static CustomNameplatesVelocity instance; - private final ProxyServer server; - private final Logger logger; - private VelocityTeamManager teamManager; - - @Inject - public CustomNameplatesVelocity(ProxyServer server, Logger logger) { - this.server = server; - this.logger = logger; - } - - @Subscribe - public void onInit(ProxyInitializeEvent event) { - instance = this; - server.getChannelRegistrar().register(MinecraftChannelIdentifier.from(CHANNEL)); - Optional optContainer = server.getPluginManager().getPlugin("velocitab"); - if (optContainer.isEmpty()) { - logger.warn("You don't have to install CustomNameplates on Velocity if you don't use Velocitab"); - server.shutdown(); - } else { - teamManager = new VelocitabManager(optContainer.get()); - } - } - - @Subscribe - public void onShutdown(ProxyShutdownEvent event) { - server.getChannelRegistrar().unregister(MinecraftChannelIdentifier.from(CHANNEL)); - } - - @Subscribe - @SuppressWarnings("UnstableApiUsage") - public void onPluginMessage(PluginMessageEvent event) { - if (!event.getIdentifier().getId().equals(CHANNEL)) { - return; - } - if (teamManager == null) { - this.logger.warn("No team manager available"); - return; - } - ByteArrayDataInput dataInput = ByteStreams.newDataInput(event.getData()); - byte length = dataInput.readByte(); - if (length != 7) { - return; - } - String type = dataInput.readUTF(); - if (!type.equals(MessageType.UPDATE)) { - return; - } - Optional optionalOwner = server.getPlayer(dataInput.readUTF()); - if (optionalOwner.isEmpty()) return; - Player owner = optionalOwner.get(); - - Optional optionalViewer = server.getPlayer(dataInput.readUTF()); - if (optionalViewer.isEmpty()) return; - Player viewer = optionalViewer.get(); - - String prefix = dataInput.readUTF(); - String suffix = dataInput.readUTF(); - - TeamColor teamColor = TeamColor.valueOf(dataInput.readUTF()); - TeamTagVisibility visibility = TeamTagVisibility.valueOf(dataInput.readUTF()); - - // delay 500ms because Velocitab delays 300ms - if (teamManager instanceof VelocitabManager) { - server.getScheduler().buildTask(this, () -> { - if (!owner.isActive() || !viewer.isActive()) { - return; - } - String teamNameRetry = teamManager.getTeam(owner, null); - if (teamNameRetry != null) { - teamManager.sendTeamUpdatePacket(viewer, teamNameRetry, teamColor, visibility, - MiniMessage.miniMessage().deserialize(prefix), - MiniMessage.miniMessage().deserialize(suffix) - ); - } - else - logger.warn("Failed to get player " + owner.getUsername() + "'s team name."); - }).delay(Duration.ofMillis(500)).schedule(); - } - } - - public static CustomNameplatesVelocity get() { - return instance; - } - - public static CustomNameplatesVelocity getPlugin() { - return instance; - } -} diff --git a/velocity/src/main/java/net/momirealms/customnameplates/velocity/team/VelocitabManager.java b/velocity/src/main/java/net/momirealms/customnameplates/velocity/team/VelocitabManager.java deleted file mode 100644 index a5063fe..0000000 --- a/velocity/src/main/java/net/momirealms/customnameplates/velocity/team/VelocitabManager.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) <2022> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.velocity.team; - -import com.velocitypowered.api.plugin.PluginContainer; -import com.velocitypowered.api.proxy.Player; -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import net.kyori.adventure.text.Component; -import net.momirealms.customnameplates.common.team.TeamColor; -import net.momirealms.customnameplates.common.team.TeamTagVisibility; -import net.william278.velocitab.Velocitab; -import net.william278.velocitab.api.VelocitabAPI; -import net.william278.velocitab.packet.UpdateTeamsPacket; -import net.william278.velocitab.player.TabPlayer; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Optional; - -public class VelocitabManager implements VelocityTeamManager { - - private final Velocitab velocitab; - private final VelocitabAPI velocitabAPI; - - public VelocitabManager(PluginContainer pluginContainer) { - velocitab = (Velocitab) pluginContainer.getInstance().get(); - velocitabAPI = VelocitabAPI.getInstance(); - } - - @Override - @Nullable - public String getTeam(Player player, Player viewer) { - Optional playerOptional = velocitabAPI.getUser(player); - return playerOptional.map(TabPlayer::getTeamName).orElse(null); - } - - @Override - public void sendTeamUpdatePacket(Player receiver, String team, TeamColor color, TeamTagVisibility visibility, Component prefix, Component suffix) { - UpdateTeamsPacket packet = new UpdateTeamsPacket(velocitab) - .teamName(team.length() > 16 ? team.substring(0, 16) : team) - .mode(UpdateTeamsPacket.UpdateMode.UPDATE_INFO) - .displayName(Component.empty()) - .friendlyFlags(List.of(UpdateTeamsPacket.FriendlyFlag.CAN_HURT_FRIENDLY)) - .nametagVisibility(UpdateTeamsPacket.NametagVisibility.valueOf(visibility.name())) - .collisionRule(UpdateTeamsPacket.CollisionRule.ALWAYS) - .color(UpdateTeamsPacket.TeamColor.valueOf(color.name()).ordinal()) - .prefix(prefix) - .suffix(suffix); - - ConnectedPlayer connectedPlayer = (ConnectedPlayer) receiver; - connectedPlayer.getConnection().write(packet); - } -}