Compare commits

...
21 Commits
Author SHA1 Message Date
XiaoMoMi 8fb4b8317d 2.3.3.7
修复旧版颜色相关问题
2024-04-07 01:27:11 +08:00
XiaoMoMi e68a333527 Fix minimessage alias 2024-03-31 03:16:51 +08:00
XiaoMoMi 53317c3e4b better bold support 2024-03-17 17:42:12 +08:00
XiaoMoMi c2c1b703b7 2.3.3.6 2024-03-17 16:26:26 +08:00
XiaoMoMi abcece373f 2.3.3.6 2024-03-13 19:19:18 +08:00
XiaoMoMi be8cf27f37 fix bold 2024-03-10 08:13:34 +08:00
XiaoMoMi 6fafb85cec 2.3.3.3 2024-03-07 23:51:32 +08:00
XiaoMoMi d18bf83ecb Update FoliaSchedulerImpl.java 2024-03-04 15:55:59 +08:00
XiaoMoMi 2c8aad7a75 disguise support 2024-02-27 16:47:36 +08:00
XiaoMoMi 9e8e57c6df adjust priority 2024-02-27 02:55:40 +08:00
XiaoMoMi 1c433d1891 update to gradle 8.6 2024-02-26 12:48:21 +08:00
XiaoMoMi 6e77eb55d0 Update README.md 2024-02-25 01:39:42 +08:00
XiaoMoMi 3afa1bfb58 compatibility with Libsdisguises 2024-02-25 00:50:03 +08:00
XiaoMoMi 39e9d40e3e small changes 2024-02-24 21:16:17 +08:00
XiaoMoMi 2e90d02166 fix some minor issues 2024-02-22 20:18:16 +08:00
XiaoMoMi 9d88ead4e3 remap libs 2024-02-20 04:59:14 +08:00
XiaoMoMi 810b8f0593 version number 2024-02-18 03:23:44 +08:00
XiaoMoMi 930cdf8c00 Fix concurrent modification 2024-02-18 03:16:33 +08:00
XiaoMoMi a26e6dc784 optimize imports 2024-02-10 18:44:22 +08:00
XiaoMoMi e450b1429d channel mode 2024-02-04 18:43:55 +08:00
XiaoMoMi 7811cea007 Update gradle.properties 2024-02-03 03:51:39 +08:00
80 changed files with 2674 additions and 554 deletions

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@
![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)
<a href="https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/" alt="GitBook">
<a href="https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates" alt="GitBook">
<img src="https://img.shields.io/badge/docs-gitbook-brightgreen" alt="Gitbook"/>
</a>
@@ -18,6 +18,7 @@
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;
@@ -26,6 +27,21 @@ 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
*
@@ -118,6 +134,8 @@ public interface BubbleManager {
*/
String getDefaultBubble();
void onChat(Player player, String text, String channel);
/**
* Get all the bubbles' keys
*
@@ -39,6 +39,10 @@ public interface NameplateManager {
@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
@@ -241,7 +245,7 @@ public interface NameplateManager {
* @param key key
* @return nameplate
*/
@NotNull
@Nullable
Nameplate getNameplate(@NotNull String key);
/**
@@ -19,6 +19,7 @@ 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;
@@ -32,7 +33,7 @@ public interface TeamTagManager {
*
* @return team tag
*/
@Nullable
@NotNull
TeamTagPlayer createTagForPlayer(Player player, String prefix, String suffix);
/**
@@ -0,0 +1,25 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.mechanic.bubble;
public enum ChannelMode {
ALL,
JOINED,
CAN_JOIN
}
@@ -15,20 +15,25 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.mechanic.bubble.listener;
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 AbstractChatListener implements Listener {
public abstract class AbstractChatProvider implements Listener {
protected BubbleManager chatBubblesManager;
public AbstractChatListener(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);
}
@@ -44,9 +44,11 @@ public class ViewerText {
for (String placeholder : placeholders) {
processedText = processedText.replace(placeholder, "%s");
if (placeholder.startsWith("%viewer_")) {
this.placeholders[i] = new ClaimedText(null, "%" + placeholder.substring("%viewer_".length()));
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);
this.placeholders[i] = new ClaimedText(owner, placeholder, false);
}
i++;
}
@@ -109,23 +111,31 @@ public class ViewerText {
private final String placeholder;
private final Player owner;
private String latestValue;
private final boolean relational;
public ClaimedText(Player owner, String placeholder) {
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) return;
if (owner == null || relational) return;
this.latestValue = PlaceholderAPI.setPlaceholders(owner, placeholder);
}
public String getValue(Player viewer) {
return Objects.requireNonNullElseGet(
latestValue,
() -> PlaceholderAPI.setPlaceholders(owner == null ? viewer : owner, placeholder)
() -> {
if (relational) {
return PlaceholderAPI.setRelationalPlaceholders(viewer, owner, placeholder);
} else {
return PlaceholderAPI.setPlaceholders(owner == null ? viewer : owner, placeholder);
}
}
);
}
}
@@ -49,6 +49,8 @@ public interface DynamicTextEntity {
void setSneak(boolean sneaking, boolean respawn);
void updateVisibility();
void removePlayerFromViewers(Player player);
void addPlayerToViewers(Player player);
@@ -20,7 +20,7 @@ package net.momirealms.customnameplates.api.mechanic.tag.unlimited;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import java.util.Vector;
import java.util.Collection;
public interface EntityTagEntity {
@@ -30,7 +30,7 @@ public interface EntityTagEntity {
void removeTag(StaticTextEntity tag);
Vector<StaticTextEntity> getStaticTags();
Collection<StaticTextEntity> getStaticTags();
void forceAddNearbyPlayer(Player player);
@@ -19,7 +19,7 @@ package net.momirealms.customnameplates.api.mechanic.tag.unlimited;
import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer;
import java.util.Vector;
import java.util.Collection;
public interface EntityTagPlayer extends NameplatePlayer {
@@ -35,9 +35,9 @@ public interface EntityTagPlayer extends NameplatePlayer {
void removeTag(StaticTextEntity tag);
Vector<DynamicTextEntity> getDynamicTags();
Collection<DynamicTextEntity> getDynamicTags();
Vector<StaticTextEntity> getStaticTags();
Collection<StaticTextEntity> getStaticTags();
void setHatOffset(double hatOffset);
@@ -24,7 +24,7 @@ import java.util.UUID;
public interface StaticTextEntity {
String getPlugin();
String getID();
boolean isShownTo(Player viewer);
@@ -73,4 +73,6 @@ public interface StaticTextEntity {
UUID getUUID();
void handlePose(Pose previous, Pose pose);
void setSneak(boolean sneaking, boolean onGround);
}
@@ -24,14 +24,14 @@ public class StaticTextTagSetting {
private NearbyRule comeRule;
private NearbyRule leaveRule;
private String defaultText;
private String plugin;
private String id;
public StaticTextTagSetting(double verticalOffset, NearbyRule comeRule, NearbyRule leaveRule, String defaultText, String plugin) {
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.plugin = plugin;
this.id = id;
}
private StaticTextTagSetting() {
@@ -39,7 +39,7 @@ public class StaticTextTagSetting {
comeRule = (player, entity) -> true;
leaveRule = (player, entity) -> true;
defaultText = "";
plugin = "";
id = "";
}
public static Builder builder() {
@@ -78,8 +78,8 @@ public class StaticTextTagSetting {
return this;
}
public Builder plugin(String type) {
this.setting.plugin = type;
public Builder id(String id) {
this.setting.id = id;
return this;
}
@@ -100,8 +100,8 @@ public class StaticTextTagSetting {
return defaultText;
}
public String getPlugin() {
return plugin;
public String getID() {
return id;
}
public NearbyRule getLeaveRule() {
+2 -1
View File
@@ -7,7 +7,7 @@ plugins {
allprojects {
version = "2.3.2.0"
version = "2.3.3.7"
apply<JavaPlugin>()
apply(plugin = "java")
@@ -44,6 +44,7 @@ allprojects {
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/")
}
}
+8 -8
View File
@@ -1,8 +1,8 @@
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
#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
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+15 -9
View File
@@ -12,7 +12,10 @@ dependencies {
compileOnly("me.clip:placeholderapi:2.11.5")
// config
compileOnly("dev.dejvokep:boosted-yaml:1.3.1")
compileOnly("dev.dejvokep:boosted-yaml:1.3.2")
// bStats
compileOnly("org.bstats:bstats-bukkit:3.0.2")
// team
compileOnly("me.neznamy:tab-api:4.0.2")
@@ -34,6 +37,8 @@ dependencies {
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")
implementation("com.github.Xiao-MoMi:BiomeAPI:0.3")
// chat channels
compileOnly(files("libs/VentureChat-3.7.1.jar"))
@@ -48,14 +53,6 @@ dependencies {
// adventure
implementation("net.kyori:adventure-api:4.15.0")
implementation("net.kyori:adventure-platform-bukkit:4.3.2")
implementation("net.kyori:adventure-text-minimessage:4.15.0")
implementation("net.kyori:adventure-text-serializer-legacy:4.15.0")
// bStats
implementation("org.bstats:bstats-bukkit:3.0.2")
// local lib
implementation(files("libs/BiomeAPI.jar"))
}
tasks {
@@ -63,5 +60,14 @@ tasks {
relocate ("net.kyori", "net.momirealms.customnameplates.libraries")
relocate ("org.bstats", "net.momirealms.customnameplates.libraries.bstats")
relocate ("net.momirealms.biomeapi", "net.momirealms.customnameplates.libraries.biomeapi")
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")
}
}
@@ -22,7 +22,10 @@ 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.helper.LibraryLoader;
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;
@@ -50,16 +53,41 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import java.io.File;
import java.util.TimeZone;
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.loadLibraries();
this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader()));
this.dependencyManager.loadDependencies(new ArrayList<>(
List.of(
Dependency.GSON,
Dependency.SLF4J_API,
Dependency.SLF4J_SIMPLE,
Dependency.COMMAND_API,
Dependency.BOOSTED_YAML,
Dependency.ADVENTURE_TEXT_MINIMESSAGE,
Dependency.ADVENTURE_LEGACY_SERIALIZER,
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();
}
@@ -155,27 +183,6 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
}
}
private void loadLibraries() {
String mavenRepo = TimeZone.getDefault().getID().startsWith("Asia") ?
"https://maven.aliyun.com/repository/public/" : "https://repo.maven.apache.org/maven2/";
LibraryLoader.loadDependencies(
"org.apache.commons:commons-pool2:2.12.0", mavenRepo,
"redis.clients:jedis:5.1.0", mavenRepo,
"dev.dejvokep:boosted-yaml:1.3.1", mavenRepo,
"com.zaxxer:HikariCP:5.0.1", mavenRepo,
"org.mariadb.jdbc:mariadb-java-client:3.3.0", mavenRepo,
"com.mysql:mysql-connector-j:8.2.0", mavenRepo,
"commons-io:commons-io:2.15.1", mavenRepo,
"com.google.code.gson:gson:2.10.1", mavenRepo,
"com.h2database:h2:2.2.224", mavenRepo,
"org.mongodb:mongodb-driver-sync:4.11.1", mavenRepo,
"org.mongodb:mongodb-driver-core:4.11.1", mavenRepo,
"org.mongodb:bson:4.11.1", mavenRepo,
"org.xerial:sqlite-jdbc:3.43.2.2", mavenRepo,
"dev.jorel:commandapi-bukkit-shade:9.3.0", mavenRepo
);
}
public CoolDownManager getCoolDownManager() {
return coolDownManager;
}
@@ -183,4 +190,8 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
public PacketManager getPacketManager() {
return packetManager;
}
public DependencyManager getDependencyManager() {
return dependencyManager;
}
}
@@ -195,12 +195,12 @@ public class AdventureManagerImpl implements AdventureManager {
case 'd' -> stringBuilder.append("<light_purple>");
case 'e' -> stringBuilder.append("<yellow>");
case 'f' -> stringBuilder.append("<white>");
case 'r' -> stringBuilder.append("<r><!i>");
case 'r' -> stringBuilder.append("<reset>");
case 'l' -> stringBuilder.append("<b>");
case 'm' -> stringBuilder.append("<s>");
case 'm' -> stringBuilder.append("<st>");
case 'o' -> stringBuilder.append("<i>");
case 'n' -> stringBuilder.append("<u>");
case 'k' -> stringBuilder.append("<o>");
case 'k' -> stringBuilder.append("<obf>");
case 'x' -> {
if (i + 13 >= chars.length
|| !isColorCode(chars[i+2])
@@ -1,182 +0,0 @@
/*
* This file is part of helper, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.helper;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
* into the classloader.
*/
public final class LibraryLoader {
@SuppressWarnings("Guava")
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) CustomNameplatesPluginImpl.getInstance().getClass().getClassLoader()));
/**
* Resolves all {@link MavenLibrary} annotations on the given object.
*
* @param object the object to load libraries for.
*/
public static void loadAll(Object object) {
loadAll(object.getClass());
}
/**
* Resolves all {@link MavenLibrary} annotations on the given class.
*
* @param clazz the class to load libraries for.
*/
public static void loadAll(Class<?> clazz) {
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
for (MavenLibrary lib : libs) {
load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url());
}
}
public static void load(String groupId, String artifactId, String version, String repoUrl) {
load(new Dependency(groupId, artifactId, version, repoUrl));
}
public static void loadDependencies(String... libs) {
if (libs == null || libs.length % 2 != 0)
return;
for (int i = 0; i < libs.length; i+=2) {
String[] split = libs[i].split(":");
load(new Dependency(
split[0],
split[1],
split[2],
libs[i+1]
));
}
}
public static void load(Dependency d) {
// LogUtils.info(String.format("Loading dependency %s:%s:%s", d.groupId, d.artifactId, d.version));
String name = d.artifactId() + "-" + d.version();
File saveLocation = new File(getLibFolder(d), name + ".jar");
if (!saveLocation.exists()) {
try {
LogUtils.info("Dependency '" + name + "' is not already in the libraries folder. Attempting to download...");
URL url = d.getUrl();
try (InputStream is = url.openStream()) {
Files.copy(is, saveLocation.toPath());
LogUtils.info("Dependency '" + name + "' successfully downloaded.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (!saveLocation.exists())
throw new RuntimeException("Unable to download dependency: " + d);
try {
URL_INJECTOR.get().addURL(saveLocation.toURI().toURL());
} catch (Exception e) {
throw new RuntimeException("Unable to load dependency: " + saveLocation, e);
}
}
@SuppressWarnings("all")
private static File getLibFolder(Dependency dependency) {
File pluginDataFolder = CustomNameplatesPlugin.getInstance().getDataFolder();
File serverDir = pluginDataFolder.getParentFile().getParentFile();
File helperDir = new File(serverDir, "libraries");
String[] split = dependency.groupId().split("\\.");
File jarDir;
StringJoiner stringJoiner = new StringJoiner(File.separator);
for (String str : split) {
stringJoiner.add(str);
}
jarDir = new File(helperDir, stringJoiner + File.separator + dependency.artifactId + File.separator + dependency.version);
jarDir.mkdirs();
return jarDir;
}
public record Dependency(String groupId, String artifactId, String version, String repoUrl) {
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
this.groupId = Objects.requireNonNull(groupId, "groupId");
this.artifactId = Objects.requireNonNull(artifactId, "artifactId");
this.version = Objects.requireNonNull(version, "version");
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
}
public URL getUrl() throws MalformedURLException {
String repo = this.repoUrl;
if (!repo.endsWith("/")) {
repo += "/";
}
repo += "%s/%s/%s/%s-%s.jar";
String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version);
return new URL(url);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Dependency other)) return false;
return this.groupId().equals(other.groupId()) &&
this.artifactId().equals(other.artifactId()) &&
this.version().equals(other.version()) &&
this.repoUrl().equals(other.repoUrl());
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.groupId().hashCode();
result = result * PRIME + this.artifactId().hashCode();
result = result * PRIME + this.version().hashCode();
result = result * PRIME + this.repoUrl().hashCode();
return result;
}
@Override
public String toString() {
return "LibraryLoader.Dependency(" +
"groupId=" + this.groupId() + ", " +
"artifactId=" + this.artifactId() + ", " +
"version=" + this.version() + ", " +
"repoUrl=" + this.repoUrl() + ")";
}
}
}
@@ -1,5 +1,5 @@
/*
* This file is part of helper, licensed under the MIT License.
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
@@ -23,21 +23,19 @@
* SOFTWARE.
*/
package net.momirealms.customnameplates.paper.helper;
package net.momirealms.customnameplates.paper.libraries.classpath;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.*;
import java.nio.file.Path;
/**
* Annotation to indicate the required libraries for a class.
* Interface which allows access to add URLs to the plugin classpath at runtime.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MavenLibraries {
public interface ClassPathAppender extends AutoCloseable {
@NotNull
MavenLibrary[] value() default {};
void addJarToClasspath(Path file);
@Override
default void close() {
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.classpath;
import net.momirealms.customnameplates.paper.libraries.loader.JarInJarClassLoader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
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;
}
@Override
public void addJarToClasspath(Path file) {
try {
this.classLoader.addJarToClasspath(file.toUri().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() {
this.classLoader.deleteJarResource();
try {
this.classLoader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,52 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.classpath;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.nio.file.Path;
public class ReflectionClassPathAppender implements ClassPathAppender {
private final URLClassLoaderAccess classLoaderAccess;
public ReflectionClassPathAppender(ClassLoader classLoader) throws IllegalStateException {
if (classLoader instanceof URLClassLoader) {
this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader);
} else {
throw new IllegalStateException("ClassLoader is not instance of URLClassLoader");
}
}
@Override
public void addJarToClasspath(Path file) {
try {
this.classLoaderAccess.addURL(file.toUri().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}
@@ -1,5 +1,5 @@
/*
* This file is part of helper, licensed under the MIT License.
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
@@ -23,11 +23,12 @@
* SOFTWARE.
*/
package net.momirealms.customnameplates.paper.helper;
package net.momirealms.customnameplates.paper.libraries.classpath;
import org.jetbrains.annotations.NotNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
@@ -43,8 +44,10 @@ public abstract class URLClassLoaderAccess {
* @param classLoader the class loader
* @return the access object
*/
static URLClassLoaderAccess create(URLClassLoader classLoader) {
if (Unsafe.isSupported()) {
public static URLClassLoaderAccess create(URLClassLoader classLoader) {
if (Reflection.isSupported()) {
return new Reflection(classLoader);
} else if (Unsafe.isSupported()) {
return new Unsafe(classLoader);
} else {
return Noop.INSTANCE;
@@ -63,7 +66,48 @@ public abstract class URLClassLoaderAccess {
*
* @param url the URL to add
*/
public abstract void addURL(@NotNull URL url);
public abstract void addURL(@NonNull URL url);
private static void throwError(Throwable cause) throws UnsupportedOperationException {
throw new UnsupportedOperationException("CustomNameplates is unable to inject into the plugin URLClassLoader.\n" +
"You may be able to fix this problem by adding the following command-line argument " +
"directly after the 'java' command in your start script: \n'--add-opens java.base/java.lang=ALL-UNNAMED'", cause);
}
/**
* Accesses using reflection, not supported on Java 9+.
*/
private static class Reflection extends URLClassLoaderAccess {
private static final Method ADD_URL_METHOD;
static {
Method addUrlMethod;
try {
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
} catch (Exception e) {
addUrlMethod = null;
}
ADD_URL_METHOD = addUrlMethod;
}
private static boolean isSupported() {
return ADD_URL_METHOD != null;
}
Reflection(URLClassLoader classLoader) {
super(classLoader);
}
@Override
public void addURL(@NonNull URL url) {
try {
ADD_URL_METHOD.invoke(super.classLoader, url);
} catch (ReflectiveOperationException e) {
URLClassLoaderAccess.throwError(e);
}
}
}
/**
* Accesses using sun.misc.Unsafe, supported on Java 9+.
@@ -106,6 +150,7 @@ public abstract class URLClassLoaderAccess {
unopenedURLs = null;
pathURLs = null;
}
this.unopenedURLs = unopenedURLs;
this.pathURLs = pathURLs;
}
@@ -117,9 +162,15 @@ public abstract class URLClassLoaderAccess {
}
@Override
public void addURL(@NotNull URL url) {
this.unopenedURLs.add(url);
this.pathURLs.add(url);
public void addURL(@NonNull URL url) {
if (this.unopenedURLs == null || this.pathURLs == null) {
URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs"));
}
synchronized (this.unopenedURLs) {
this.unopenedURLs.add(url);
this.pathURLs.add(url);
}
}
}
@@ -131,8 +182,8 @@ public abstract class URLClassLoaderAccess {
}
@Override
public void addURL(@NotNull URL url) {
throw new UnsupportedOperationException();
public void addURL(@NonNull URL url) {
URLClassLoaderAccess.throwError(null);
}
}
@@ -0,0 +1,368 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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 LuckPerms.
*/
public enum Dependency {
ASM(
"org.ow2.asm",
"asm",
"9.1",
null,
"asm"
),
ASM_COMMONS(
"org.ow2.asm",
"asm-commons",
"9.1",
null,
"asm-commons"
),
JAR_RELOCATOR(
"me.lucko",
"jar-relocator",
"1.7",
null,
"jar-relocator"
),
KYORI_OPTION(
"net{}kyori",
"option",
"1.0.0",
null,
"kyori-option",
Relocation.of("option", "net{}kyori{}option")
),
ADVENTURE_API(
"net{}kyori",
"adventure-api",
"4.15.0",
null,
"adventure-api",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_KEY(
"net{}kyori",
"adventure-key",
"4.15.0",
null,
"adventure-key",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_NBT(
"net{}kyori",
"adventure-nbt",
"4.15.0",
null,
"adventure-nbt",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_LEGACY_SERIALIZER(
"net{}kyori",
"adventure-text-serializer-legacy",
"4.15.0",
null,
"adventure-text-serializer-legacy",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_TEXT_LOGGER(
"net{}kyori",
"adventure-text-logger-slf4j",
"4.15.0",
null,
"adventure-text-logger-slf4j",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_GSON(
"net{}kyori",
"adventure-text-serializer-gson",
"4.15.0",
null,
"adventure-text-serializer-gson",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_GSON_LEGACY(
"net{}kyori",
"adventure-text-serializer-gson-legacy-impl",
"4.15.0",
null,
"adventure-text-serializer-gson-legacy-impl",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_PLATFORM(
"net{}kyori",
"adventure-platform-api",
"4.3.2",
null,
"adventure-platform-api",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_PLATFORM_BUKKIT(
"net{}kyori",
"adventure-platform-bukkit",
"4.3.2",
null,
"adventure-platform-bukkit",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_PLATFORM_FACET(
"net{}kyori",
"adventure-platform-facet",
"4.3.2",
null,
"adventure-platform-facet",
Relocation.of("adventure", "net{}kyori{}adventure")
),
ADVENTURE_TEXT_MINIMESSAGE(
"net{}kyori",
"adventure-text-minimessage",
"4.15.0",
null,
"adventure-text-minimessage",
Relocation.of("adventure", "net{}kyori{}adventure")
),
COMMAND_API(
"dev{}jorel",
"commandapi-bukkit-shade",
"9.3.0",
null,
"commandapi-bukkit",
Relocation.of("commandapi", "dev{}jorel{}commandapi")
),
MARIADB_DRIVER(
"org{}mariadb{}jdbc",
"mariadb-java-client",
"3.3.2",
null,
"mariadb-java-client",
Relocation.of("mariadb", "org{}mariadb")
),
BOOSTED_YAML(
"dev{}dejvokep",
"boosted-yaml",
"1.3.2",
null,
"boosted-yaml",
Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml")
),
MYSQL_DRIVER(
"com{}mysql",
"mysql-connector-j",
"8.3.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.45.1.0",
null,
"sqlite-jdbc"
),
HIKARI(
"com{}zaxxer",
"HikariCP",
"5.0.1",
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",
"4.11.1",
null,
"mongodb-driver-core",
Relocation.of("mongodb", "com{}mongodb"),
Relocation.of("bson", "org{}bson")
),
MONGODB_DRIVER_SYNC(
"org{}mongodb",
"mongodb-driver-sync",
"4.11.1",
null,
"mongodb-driver-sync",
Relocation.of("mongodb", "com{}mongodb"),
Relocation.of("bson", "org{}bson")
),
MONGODB_DRIVER_BSON(
"org{}mongodb",
"bson",
"4.11.1",
null,
"mongodb-bson",
Relocation.of("mongodb", "com{}mongodb"),
Relocation.of("bson", "org{}bson")
),
JEDIS(
"redis{}clients",
"jedis",
"5.1.0",
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"
),
BIOME_API(
"com{}github{}Xiao-MoMi",
"BiomeAPI",
"0.3",
"jitpack",
"biome-api",
Relocation.of("biomeapi", "net{}momirealms{}biomeapi")
);
private final String mavenRepoPath;
private final String version;
private final List<Relocation> 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<Relocation> 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;
}
}
@@ -0,0 +1,48 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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;
/**
* Exception thrown if a dependency cannot be downloaded.
*/
public class DependencyDownloadException extends Exception {
public DependencyDownloadException() {
}
public DependencyDownloadException(String message) {
super(message);
}
public DependencyDownloadException(String message, Throwable cause) {
super(message, cause);
}
public DependencyDownloadException(Throwable cause) {
super(cause);
}
}
@@ -1,5 +1,5 @@
/*
* This file is part of helper, licensed under the MIT License.
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
@@ -23,51 +23,31 @@
* SOFTWARE.
*/
package net.momirealms.customnameplates.paper.helper;
package net.momirealms.customnameplates.paper.libraries.dependencies;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.*;
import java.util.Collection;
import java.util.Set;
/**
* Annotation to indicate a required library for a class.
* Loads and manages runtime dependencies for the plugin.
*/
@Documented
@Repeatable(MavenLibraries.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MavenLibrary {
public interface DependencyManager extends AutoCloseable {
/**
* The group id of the library
* Loads dependencies.
*
* @return the group id of the library
* @param dependencies the dependencies to load
*/
@NotNull
String groupId();
void loadDependencies(Collection<Dependency> dependencies);
/**
* The artifact id of the library
* Obtains an isolated classloader containing the given dependencies.
*
* @return the artifact id of the library
* @param dependencies the dependencies
* @return the classloader
*/
@NotNull
String artifactId();
/**
* The version of the library
*
* @return the version of the library
*/
@NotNull
String version();
/**
* The repo where the library can be obtained from
*
* @return the repo where the library can be obtained from
*/
@NotNull
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies);
@Override
void close();
}
@@ -0,0 +1,231 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.ImmutableSet;
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 org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
* Loads and manages runtime dependencies for the plugin.
*/
public class DependencyManagerImpl implements DependencyManager {
/** A registry containing plugin specific behaviour for dependencies. */
private final DependencyRegistry registry;
/** The path where library jars are cached. */
private final Path cacheDirectory;
/** The classpath appender to preload dependencies into */
private final ClassPathAppender classPathAppender;
/** A map of dependencies which have already been loaded. */
private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class);
/** A map of isolated classloaders which have been created. */
private final Map<ImmutableSet<Dependency>, IsolatedClassLoader> loaders = new HashMap<>();
/** Cached relocation handler instance. */
private @MonotonicNonNull RelocationHandler relocationHandler = null;
public DependencyManagerImpl(CustomNameplatesPluginImpl plugin, ClassPathAppender classPathAppender) {
this.registry = new DependencyRegistry();
this.cacheDirectory = setupCacheDirectory(plugin);
this.classPathAppender = classPathAppender;
}
private synchronized RelocationHandler getRelocationHandler() {
if (this.relocationHandler == null) {
this.relocationHandler = new RelocationHandler(this);
}
return this.relocationHandler;
}
@Override
public ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies) {
ImmutableSet<Dependency> set = ImmutableSet.copyOf(dependencies);
for (Dependency dependency : dependencies) {
if (!this.loaded.containsKey(dependency)) {
throw new IllegalStateException("Dependency " + dependency + " is not loaded.");
}
}
synchronized (this.loaders) {
IsolatedClassLoader classLoader = this.loaders.get(set);
if (classLoader != null) {
return classLoader;
}
URL[] urls = set.stream()
.map(this.loaded::get)
.map(file -> {
try {
return file.toUri().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
})
.toArray(URL[]::new);
classLoader = new IsolatedClassLoader(urls);
this.loaders.put(set, classLoader);
return classLoader;
}
}
@Override
public void loadDependencies(Collection<Dependency> dependencies) {
CountDownLatch latch = new CountDownLatch(dependencies.size());
for (Dependency dependency : dependencies) {
if (this.loaded.containsKey(dependency)) {
latch.countDown();
continue;
}
try {
loadDependency(dependency);
} catch (Throwable e) {
new RuntimeException("Unable to load dependency " + dependency.name(), e).printStackTrace();
} finally {
latch.countDown();
}
}
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void loadDependency(Dependency dependency) throws Exception {
if (this.loaded.containsKey(dependency)) {
return;
}
Path file = remapDependency(dependency, downloadDependency(dependency));
this.loaded.put(dependency, file);
if (this.classPathAppender != null && this.registry.shouldAutoLoad(dependency)) {
this.classPathAppender.addJarToClasspath(file);
}
}
private Path downloadDependency(Dependency dependency) throws DependencyDownloadException {
Path file = this.cacheDirectory.resolve(dependency.getFileName(null));
// if the file already exists, don't attempt to re-download it.
if (Files.exists(file)) {
return file;
}
DependencyDownloadException lastError = null;
String forceRepo = dependency.getRepo();
if (forceRepo == null) {
// attempt to download the dependency from each repo in order.
for (DependencyRepository repo : DependencyRepository.values()) {
if (repo.getId().equals("maven") && TimeZone.getDefault().getID().startsWith("Asia")) {
continue;
}
try {
repo.download(dependency, file);
return file;
} catch (DependencyDownloadException e) {
lastError = e;
}
}
} else {
DependencyRepository repository = DependencyRepository.getByID(forceRepo);
if (repository != null) {
try {
repository.download(dependency, file);
return file;
} catch (DependencyDownloadException e) {
lastError = e;
}
}
}
throw Objects.requireNonNull(lastError);
}
private Path remapDependency(Dependency dependency, Path normalFile) throws Exception {
List<Relocation> rules = new ArrayList<>(dependency.getRelocations());
if (rules.isEmpty()) {
return normalFile;
}
Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName(DependencyRegistry.isGsonRelocated() ? "remapped-legacy" : "remapped"));
// if the remapped source exists already, just use that.
if (Files.exists(remappedFile)) {
return remappedFile;
}
getRelocationHandler().remap(normalFile, remappedFile, rules);
return remappedFile;
}
private static Path setupCacheDirectory(CustomNameplatesPluginImpl plugin) {
File folder = new File(plugin.getDataFolder(), "libs");
folder.mkdirs();
return folder.toPath();
}
@Override
public void close() {
IOException firstEx = null;
for (IsolatedClassLoader loader : this.loaders.values()) {
try {
loader.close();
} catch (IOException ex) {
if (firstEx == null) {
firstEx = ex;
} else {
firstEx.addSuppressed(ex);
}
}
}
if (firstEx != null) {
firstEx.printStackTrace();
}
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.gson.JsonElement;
/**
* Applies LuckPerms specific behaviour for {@link Dependency}s.
*/
public class DependencyRegistry {
public boolean shouldAutoLoad(Dependency dependency) {
return switch (dependency) {
// all used within 'isolated' classloaders, and are therefore not
// relocated.
case ASM, ASM_COMMONS, JAR_RELOCATOR, H2_DRIVER, SQLITE_DRIVER -> false;
default -> true;
};
}
@SuppressWarnings("ConstantConditions")
public static boolean isGsonRelocated() {
return JsonElement.class.getName().startsWith("net.momirealms");
}
private static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
private static boolean slf4jPresent() {
return classExists("org.slf4j.Logger") && classExists("org.slf4j.LoggerFactory");
}
}
@@ -0,0 +1,145 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
/**
* Represents a repository which contains {@link Dependency}s.
*/
public enum DependencyRepository {
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));
return connection;
}
},
/**
* Maven Central
*/
MAVEN_CENTRAL_MIRROR("aliyun", "https://maven.aliyun.com/repository/public/"),
/**
* Code MC
*/
CODE_MC("codemc", "https://repo.codemc.io/repository/maven-public/"),
/**
* Jitpack
*/
JITPACK("jitpack", "https://jitpack.io/");
private final String url;
private final String id;
DependencyRepository(String id, String url) {
this.url = url;
this.id = id;
}
public static DependencyRepository getByID(String id) {
for (DependencyRepository repository : values()) {
if (id.equals(repository.id)) {
return repository;
}
}
return null;
}
/**
* Opens a connection to the given {@code dependency}.
*
* @param dependency the dependency to download
* @return the connection
* @throws IOException if unable to open a connection
*/
protected URLConnection openConnection(Dependency dependency) throws IOException {
URL dependencyUrl = new URL(this.url + dependency.getMavenRepoPath());
return dependencyUrl.openConnection();
}
/**
* Downloads the raw bytes of the {@code dependency}.
*
* @param dependency the dependency to download
* @return the downloaded bytes
* @throws DependencyDownloadException if unable to download
*/
public byte[] downloadRaw(Dependency dependency) throws DependencyDownloadException {
try {
URLConnection connection = openConnection(dependency);
try (InputStream in = connection.getInputStream()) {
byte[] bytes = ByteStreams.toByteArray(in);
if (bytes.length == 0) {
throw new DependencyDownloadException("Empty stream");
}
return bytes;
}
} catch (Exception e) {
throw new DependencyDownloadException(e);
}
}
/**
* @param dependency the dependency to download
* @return the downloaded bytes
* @throws DependencyDownloadException if unable to download
*/
public byte[] download(Dependency dependency) throws DependencyDownloadException {
return downloadRaw(dependency);
}
/**
* Downloads the the {@code dependency} to the {@code file}, ensuring the
* downloaded bytes match the checksum.
*
* @param dependency the dependency to download
* @param file the file to write to
* @throws DependencyDownloadException if unable to download
*/
public void download(Dependency dependency, Path file) throws DependencyDownloadException {
try {
Files.write(file, download(dependency));
} catch (IOException e) {
throw new DependencyDownloadException(e);
}
}
public String getId() {
return id;
}
}
@@ -0,0 +1,54 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.classloader;
import java.net.URL;
import java.net.URLClassLoader;
/**
* A classloader "isolated" from the rest of the Minecraft server.
*
* <p>Used to load specific LuckPerms dependencies without causing conflicts
* with other plugins, or libraries provided by the server implementation.</p>
*/
public class IsolatedClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public IsolatedClassLoader(URL[] urls) {
/*
* ClassLoader#getSystemClassLoader returns the AppClassLoader
*
* Calling #getParent on this returns the ExtClassLoader (Java 8) or
* the PlatformClassLoader (Java 9). Since we want this classloader to
* be isolated from the Minecraft server (the app), we set the parent
* to be the platform class loader.
*/
super(urls, ClassLoader.getSystemClassLoader().getParent());
}
}
@@ -0,0 +1,66 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.relocation;
import java.util.Objects;
public final class Relocation {
private static final String RELOCATION_PREFIX = "net.momirealms.customnameplates.libraries.";
public static Relocation of(String id, String pattern) {
return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id);
}
private final String pattern;
private final String relocatedPattern;
private Relocation(String pattern, String relocatedPattern) {
this.pattern = pattern;
this.relocatedPattern = relocatedPattern;
}
public String getPattern() {
return this.pattern;
}
public String getRelocatedPattern() {
return this.relocatedPattern;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Relocation that = (Relocation) o;
return Objects.equals(this.pattern, that.pattern) &&
Objects.equals(this.relocatedPattern, that.relocatedPattern);
}
@Override
public int hashCode() {
return Objects.hash(this.pattern, this.relocatedPattern);
}
}
@@ -0,0 +1,91 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.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 java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;
/**
* Handles class runtime relocation of packages in downloaded dependencies
*/
public class RelocationHandler {
public static final Set<Dependency> DEPENDENCIES = EnumSet.of(Dependency.ASM, Dependency.ASM_COMMONS, Dependency.JAR_RELOCATOR);
private static final String JAR_RELOCATOR_CLASS = "me.lucko.jarrelocator.JarRelocator";
private static final String JAR_RELOCATOR_RUN_METHOD = "run";
private final Constructor<?> jarRelocatorConstructor;
private final Method jarRelocatorRunMethod;
public RelocationHandler(DependencyManager dependencyManager) {
ClassLoader classLoader = null;
try {
// download the required dependencies for remapping
dependencyManager.loadDependencies(DEPENDENCIES);
// get a classloader containing the required dependencies as sources
classLoader = dependencyManager.obtainClassLoaderWith(DEPENDENCIES);
// load the relocator class
Class<?> jarRelocatorClass = classLoader.loadClass(JAR_RELOCATOR_CLASS);
// prepare the the reflected constructor & method instances
this.jarRelocatorConstructor = jarRelocatorClass.getDeclaredConstructor(File.class, File.class, Map.class);
this.jarRelocatorConstructor.setAccessible(true);
this.jarRelocatorRunMethod = jarRelocatorClass.getDeclaredMethod(JAR_RELOCATOR_RUN_METHOD);
this.jarRelocatorRunMethod.setAccessible(true);
} catch (Exception e) {
try {
if (classLoader instanceof IsolatedClassLoader) {
((IsolatedClassLoader) classLoader).close();
}
} catch (IOException ex) {
e.addSuppressed(ex);
}
throw new RuntimeException(e);
}
}
public void remap(Path input, Path output, List<Relocation> relocations) throws Exception {
Map<String, String> mappings = new HashMap<>();
for (Relocation relocation : relocations) {
mappings.put(relocation.getPattern(), relocation.getRelocatedPattern());
}
// create and invoke a new relocator
Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings);
this.jarRelocatorRunMethod.invoke(relocator);
}
}
@@ -0,0 +1,37 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.relocation;
public final class RelocationHelper {
// screw maven shade
public static final String OKIO_STRING = String.valueOf(new char[]{'o', 'k', 'i', 'o'});
public static final String OKHTTP3_STRING = String.valueOf(new char[]{'o', 'k', 'h', 't', 't', 'p', '3'});
private RelocationHelper() {
}
}
@@ -0,0 +1,155 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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.
*
* <p>The "loader" jar contains the loading code & public API classes,
* and is class-loaded by the platform.</p>
*
* <p>The inner "plugin" jar contains the plugin itself, and is class-loaded
* by the loading code & this classloader.</p>
*/
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 <T> the type of the loader plugin
* @return the instantiated bootstrap plugin
*/
public <T> LoaderBootstrap instantiatePlugin(String bootstrapClass, Class<T> loaderPluginType, T loaderPlugin) throws LoadingException {
Class<? extends LoaderBootstrap> plugin;
try {
plugin = loadClass(bootstrapClass).asSubclass(LoaderBootstrap.class);
} catch (ReflectiveOperationException e) {
throw new LoadingException("Unable to load bootstrap class", e);
}
Constructor<? extends LoaderBootstrap> 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);
}
}
}
@@ -1,5 +1,5 @@
/*
* This file is part of helper, licensed under the MIT License.
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
@@ -23,23 +23,17 @@
* SOFTWARE.
*/
package net.momirealms.customnameplates.paper.helper;
import java.lang.annotation.*;
package net.momirealms.customnameplates.paper.libraries.loader;
/**
* Represents a maven repository.
* Minimal bootstrap plugin, called by the loader plugin.
*/
@Documented
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
public interface LoaderBootstrap {
/**
* Gets the base url of the repository.
*
* @return the base url of the repository
*/
String url();
void onLoad();
default void onEnable() {}
default void onDisable() {}
}
@@ -0,0 +1,41 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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;
/**
* Runtime exception used if there is a problem during loading
*/
public class LoadingException extends RuntimeException {
public LoadingException(String message) {
super(message);
}
public LoadingException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -24,6 +24,7 @@ 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;
@@ -35,6 +36,7 @@ import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.SystemC
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;
@@ -60,11 +62,15 @@ public class ActionBarManagerImpl implements ActionBarManager, Listener {
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 {
@@ -176,13 +182,23 @@ public class ActionBarManagerImpl implements ActionBarManager, Listener {
if (receiver != null) {
event.setCancelled(true);
String json = packet.getStrings().readSafely(0);
if (json != null && !json.equals("")) {
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(AdventureManagerImpl.getInstance().getMiniMessageFormat(component), System.currentTimeMillis());
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());
}
}
}
}
@@ -204,7 +220,7 @@ public class ActionBarManagerImpl implements ActionBarManager, Listener {
// We can't get TranslatableComponent's width :(
return;
}
receiver.setOtherPluginText(AdventureManagerImpl.getInstance().getMiniMessageFormat(component), System.currentTimeMillis());
receiver.setOtherPluginText(miniMessage.serialize(component), System.currentTimeMillis());
}
}
}
@@ -212,10 +228,10 @@ public class ActionBarManagerImpl implements ActionBarManager, Listener {
public void onReceiveActionBarPacket(PacketEvent event) {
PacketContainer packet = event.getPacket();
WrappedChatComponent wrappedChatComponent = packet.getChatComponents().read(0);
if (wrappedChatComponent != null) {
ActionBarReceiver receiver = getReceiver(event.getPlayer().getUniqueId());
if (receiver != null) {
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\"")) {
@@ -223,10 +239,14 @@ public class ActionBarManagerImpl implements ActionBarManager, Listener {
}
event.setCancelled(true);
receiver.setOtherPluginText(
AdventureManagerImpl.getInstance().getMiniMessageFormat(
GsonComponentSerializer.gson().deserialize(strJson)
), System.currentTimeMillis()
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());
}
}
}
}
@@ -24,13 +24,17 @@ 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.listener.AbstractChatListener;
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.EntityTagPlayer;
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;
@@ -38,25 +42,28 @@ 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.listener.*;
import net.momirealms.customnameplates.paper.mechanic.bubble.provider.*;
import net.momirealms.customnameplates.paper.setting.CNConfig;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
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.bukkit.potion.PotionEffectType;
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 {
public class BubbleManagerImpl implements BubbleManager, Listener {
private AbstractChatListener chatListener;
private AbstractChatProvider chatProvider;
private AbstractChatProvider customProvider;
private ImageParser imageParser;
private final HashMap<String, Bubble> bubbleMap;
private final CustomNameplatesPluginImpl plugin;
@@ -73,6 +80,8 @@ public class BubbleManagerImpl implements BubbleManager {
private int lengthPerLine;
private int subStringIndex;
private String[] blacklistChannels;
private ChannelMode channelMode;
private Requirement[] requirements;
public BubbleManagerImpl(CustomNameplatesPluginImpl plugin) {
this.plugin = plugin;
@@ -88,18 +97,21 @@ public class BubbleManagerImpl implements BubbleManager {
if (!CNConfig.bubbleModule) return;
this.loadConfig();
this.loadBubbles();
this.registerListener();
this.registerChatProvider();
this.registerImageParser();
Bukkit.getPluginManager().registerEvents(this, plugin);
}
public void unload() {
this.imageParser = null;
this.bubbleMap.clear();
if (chatListener != null) chatListener.unregister();
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", "");
@@ -113,6 +125,23 @@ public class BubbleManagerImpl implements BubbleManager {
lengthPerLine = config.getInt("characters-per-line", 30);
startFormat = config.getString("default-format.start", "<gradient:#F5F5F5:#E1FFFF:#F5F5F5><u>");
endFormat = config.getString("default-format.end", "<!u></gradient>");
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() {
@@ -124,24 +153,47 @@ public class BubbleManagerImpl implements BubbleManager {
}
}
private void registerListener() {
if (CNConfig.trChatChannel) {
this.chatListener = new TrChatListener(this);
private void registerChatProvider() {
if (this.customProvider != null) {
this.chatProvider = customProvider;
} else if (CNConfig.trChatChannel) {
this.chatProvider = new TrChatProvider(this);
} else if (CNConfig.ventureChatChannel) {
this.chatListener = new VentureChatListener(this);
this.chatProvider = new VentureChatProvider(this);
} else if (CNConfig.huskChatChannel) {
this.chatListener = new HuskChatListener(this);
this.chatProvider = new HuskChatProvider(this);
} else if (CNConfig.carbonChatChannel) {
this.chatListener = new CarbonChatListener(this);
this.chatProvider = new CarbonChatProvider(this);
} else {
try {
Class.forName("io.papermc.paper.event.player.AsyncChatEvent");
this.chatListener = new PaperAsyncChatListener(this);
this.chatProvider = new PaperAsyncChatProvider(this);
} catch (ClassNotFoundException e) {
this.chatListener = new AsyncChatListener(this);
this.chatProvider = new AsyncChatProvider(this);
}
}
this.chatListener.register();
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() {
@@ -214,11 +266,16 @@ public class BubbleManagerImpl implements BubbleManager {
return this.bubbleMap.remove(key) != null;
}
@Override
public void onChat(Player player, String text) {
if ( player.getGameMode() == GameMode.SPECTATOR
|| player.hasPotionEffect(PotionEffectType.INVISIBILITY)
|| !player.hasPermission("bubbles.use")
) return;
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;
@@ -270,14 +327,14 @@ public class BubbleManagerImpl implements BubbleManager {
int finalIndex = i;
String finalBubble = bubble;
plugin.getScheduler().runTaskAsyncLater(
() -> sendBubble(player, split[finalIndex], bubbleConfig, finalBubble),
() -> sendBubble(player, split[finalIndex], bubbleConfig, finalBubble, channel),
(long) i * 250 + 100,
TimeUnit.MILLISECONDS
);
}
}
private void sendBubble(Player player, String text, Bubble bubbleConfig, String key) {
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 {
@@ -285,26 +342,40 @@ public class BubbleManagerImpl implements BubbleManager {
int width = FontUtils.getTextWidth(text);
text = bubbleConfig.getPrefixWithFont(width) + text + bubbleConfig.getSuffixWithFont(width);
}
EntityTagPlayer tagPlayer = plugin.getNameplateManager().getUnlimitedTagManager().createOrGetTagForPlayer(player);
if (tagPlayer == null)
EntityTagEntity tagEntity = plugin.getNameplateManager().getUnlimitedTagManager().createOrGetTagForEntity(player);
if (tagEntity == null)
return;
StaticTextEntity entity = tagPlayer.addTag(StaticTextTagSetting.builder()
StaticTextEntity entity = tagEntity.addTag(StaticTextTagSetting.builder()
.leaveRule((p, e) -> true)
.comeRule((p, e) -> true)
.comeRule((p, e) -> {
switch (channelMode) {
case ALL -> {
return true;
}
case JOINED -> {
return channel == null || chatProvider.hasJoinedChannel(p, channel);
}
case CAN_JOIN -> {
return channel == null || chatProvider.canJoinChannel(p, channel);
}
}
return false;
})
.verticalOffset(yOffset)
.defaultText(text)
.plugin("bubble")
.id("bubble")
.build());
for (StaticTextEntity bubble : tagPlayer.getStaticTags()) {
if (bubble.getPlugin().equals("bubble")) {
for (StaticTextEntity bubble : tagEntity.getStaticTags()) {
if (bubble.getID().equals("bubble")) {
bubble.setOffset(bubble.getOffset() + lineSpace);
}
}
plugin.getScheduler().runTaskAsyncLater(() -> {
tagPlayer.removeTag(entity);
tagEntity.removeTag(entity);
}, stayTime, TimeUnit.SECONDS);
}
@@ -15,19 +15,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
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.listener.AbstractChatListener;
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 AsyncChatListener extends AbstractChatListener {
public class AsyncChatProvider extends AbstractChatProvider {
public AsyncChatListener(BubbleManager chatBubblesManager) {
public AsyncChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
}
@@ -41,6 +42,17 @@ public class AsyncChatListener extends AbstractChatListener {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
return true;
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
return true;
}
// This event is not async sometimes
@EventHandler (ignoreCancelled = true)
public void onChat(AsyncPlayerChatEvent event) {
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(event.getPlayer(), event.getMessage()));
@@ -15,16 +15,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
package net.momirealms.customnameplates.paper.mechanic.bubble.provider;
import net.draycia.carbon.api.CarbonChat;
import net.draycia.carbon.api.CarbonChatProvider;
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.listener.AbstractChatListener;
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;
@@ -32,19 +34,21 @@ import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class CarbonChatListener extends AbstractChatListener {
public class CarbonChatProvider extends AbstractChatProvider {
private final CarbonChat api;
private CarbonEventSubscription<CarbonChatEvent> subscription;
private Method originalMessageMethod;
private Method channelKeyMethod;
private Method getChannelByKeyMethod;
public CarbonChatListener(BubbleManager chatBubblesManager) {
public CarbonChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
this.api = CarbonChatProvider.carbonChat();
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();
}
@@ -67,7 +71,7 @@ public class CarbonChatListener extends AbstractChatListener {
return;
Object component = getComponentFromEvent(event);
String message = ReflectionUtils.getMiniMessageTextFromNonShadedComponent(component);
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(player, message));
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(player, message, channel));
});
}
@@ -78,6 +82,51 @@ public class CarbonChatListener extends AbstractChatListener {
}
}
@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();
if (selectedChannel == null) {
return false;
}
Object key = getChannelKey(selectedChannel);
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);
}
private Object getChannelKey(ChatChannel channel) {
try {
return this.channelKeyMethod.invoke(channel);
@@ -15,20 +15,23 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
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.listener.AbstractChatListener;
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 HuskChatListener extends AbstractChatListener {
public class HuskChatProvider extends AbstractChatProvider {
public HuskChatListener(BubbleManager chatBubblesManager) {
public HuskChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
}
@@ -42,6 +45,30 @@ public class HuskChatListener extends AbstractChatListener {
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);
}
@EventHandler (ignoreCancelled = true)
public void onHuskChat(ChatMessageEvent event) {
String channel = event.getChannelId();
@@ -52,7 +79,7 @@ public class HuskChatListener extends AbstractChatListener {
if (player == null || !player.isOnline())
return;
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(player, event.getMessage());
chatBubblesManager.onChat(player, event.getMessage(), channel);
});
}
}
@@ -15,25 +15,26 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
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.listener.AbstractChatListener;
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 PaperAsyncChatListener extends AbstractChatListener {
public class PaperAsyncChatProvider extends AbstractChatProvider {
private Method messageMethod;
public PaperAsyncChatListener(BubbleManager chatBubblesManager) {
public PaperAsyncChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
try {
this.messageMethod = AsyncChatEvent.class.getMethod("message");
@@ -52,6 +53,16 @@ public class PaperAsyncChatListener extends AbstractChatListener {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
return true;
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
return true;
}
@EventHandler (ignoreCancelled = true)
public void onChat(AsyncChatEvent event) {
Object component = getComponentFromEvent(event);
@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
package net.momirealms.customnameplates.paper.mechanic.bubble.provider;
import me.arasple.mc.trchat.TrChat;
import me.arasple.mc.trchat.api.event.TrChatEvent;
@@ -24,16 +24,18 @@ 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.listener.AbstractChatListener;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
public class TrChatListener extends AbstractChatListener {
public class TrChatProvider extends AbstractChatProvider {
private final BukkitAdapter adapter;
public TrChatListener(BubbleManager chatBubblesManager) {
public TrChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
this.adapter = new BukkitAdapter();
}
@@ -48,6 +50,27 @@ public class TrChatListener extends AbstractChatListener {
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;
}
}
@EventHandler (ignoreCancelled = true)
public void onTrChat(TrChatEvent event) {
if (!event.getForward()) return;
@@ -58,7 +81,7 @@ public class TrChatListener extends AbstractChatListener {
}
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());
chatBubblesManager.onChat(event.getSession().getPlayer(), object.getFiltered(), channelName);
});
}
}
@@ -15,20 +15,24 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
package net.momirealms.customnameplates.paper.mechanic.bubble.provider;
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.listener.AbstractChatListener;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
public class VentureChatListener extends AbstractChatListener {
public class VentureChatProvider extends AbstractChatProvider {
public VentureChatListener(BubbleManager chatBubblesManager) {
public VentureChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
}
@@ -42,6 +46,25 @@ public class VentureChatListener extends AbstractChatListener {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
MineverseChatPlayer mcp = MineverseChatAPI.getOnlineMineverseChatPlayer(player);
return mcp.getCurrentChannel().getName().equals(channelID);
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
ChatChannel channel = ChatChannel.getChannel(channelID);
if (channel == null) {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
if (channel.hasPermission()) {
return player.hasPermission(channel.getPermission());
}
return true;
}
@EventHandler (ignoreCancelled = true)
public void onVentureChat(VentureChatEvent event) {
String channelName = event.getChannel().getName();
@@ -53,7 +76,7 @@ public class VentureChatListener extends AbstractChatListener {
return;
}
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(chatPlayer.getPlayer(), event.getChat());
chatBubblesManager.onChat(chatPlayer.getPlayer(), event.getChat(), channelName);
});
}
}
@@ -72,10 +72,12 @@ public class WidthManagerImpl implements WidthManager {
private FontData NONLATIN_EUROPEAN_DATA;
private FontData UNIFONT_DATA;
private FontData UNICODE_DATA;
private final HashSet<Integer> bitMapChars;
public WidthManagerImpl(CustomNameplatesPlugin plugin) {
this.plugin = plugin;
this.fontDataMap = new HashMap<>();
this.bitMapChars = new HashSet<>();
this.saveFontImages();
this.cacheImageWidthIfNotCached();
this.loadCachedFontData();
@@ -96,6 +98,7 @@ public class WidthManagerImpl implements WidthManager {
if (this.cacheSystem != null)
this.cacheSystem.destroy();
fontDataMap.clear();
bitMapChars.clear();
}
private void loadInternalConfigs() {
@@ -158,6 +161,10 @@ public class WidthManagerImpl implements WidthManager {
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() {
@@ -176,6 +183,7 @@ public class WidthManagerImpl implements WidthManager {
case "unifont" -> fontData.overrideWith(UNIFONT_DATA);
}
}
boolean defaultFont = entry.getKey().equals("minecraft:default");
ConfigurationSection customSection = innerSection.getConfigurationSection("values");
if (customSection != null)
for (Map.Entry<String, Object> innerEntry : customSection.getValues(false).entrySet()) {
@@ -187,8 +195,12 @@ public class WidthManagerImpl implements WidthManager {
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);
}
@@ -858,6 +870,10 @@ public class WidthManagerImpl implements WidthManager {
}
}
public boolean isNotReplacedUnifont(int codePoint) {
return !bitMapChars.contains(codePoint);
}
@Override
public boolean registerFontData(@NotNull Key key, @NotNull FontData fontData) {
if (fontDataMap.containsKey(key)) {
@@ -906,25 +922,47 @@ public class WidthManagerImpl implements WidthManager {
text = AdventureManagerImpl.getInstance().legacyToMiniMessage(text);
ElementNode node = (ElementNode) MiniMessage.miniMessage().deserializeToTree(text);
ArrayList<Tuple<String, Key, Boolean>> list = new ArrayList<>();
nodeToStringInfo(node, list, Key.of("minecraft", "default"), false);
Key defaultFont = Key.of("minecraft", "default");
nodeToStringInfo(node, list, defaultFont, false);
int totalLength = 0;
for (Tuple<String, Key, Boolean> element : list) {
for (int i = 0, size = list.size(); i < size; i++) {
Tuple<String, Key, Boolean> 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();
for (int i = 0; i < chars.length; i++) {
if (Character.isHighSurrogate(chars[i])) {
totalLength += data.getWidth(Character.toCodePoint(chars[i], chars[++i]));
} else {
totalLength += data.getWidth(chars[i]);
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 += element.getRight() ? element.getLeft().length() * 2 : element.getLeft().length();
}
totalLength /= 2;
return totalLength;
}
@@ -936,7 +974,11 @@ public class WidthManagerImpl implements WidthManager {
} else if (node instanceof TagNode tagNode) {
if (tagNode.tag() instanceof Inserting inserting) {
Component component = inserting.value();
isBold = component.hasDecoration(TextDecoration.BOLD);
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());
@@ -238,7 +238,7 @@ public class VersionManagerImpl implements VersionManager, Listener {
final Player player = event.getPlayer();
if (player.isOp()) {
if (!PlaceholderAPI.isRegistered("player")) {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, "You haven't installed Player Expansion yet. Click <b><gold><click:run_command:/papi ecloud download Player>HERE</click></gold><b> to download.");
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, "You haven't installed Player Expansion yet. Click <b><gold><u><click:run_command:/papi ecloud download Player>HERE</click></u></gold></b> to download. Otherwise you might encounter console spam on first installation.");
}
}
}
@@ -51,10 +51,13 @@ 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.*;
@@ -80,6 +83,8 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
private final EntityMoveListener entityMoveListener;
private final EntityLookListener entityLookListener;
private final EntityTeleportListener entityTeleportListener;
private final EntityAddEffectListener entityAddEffectListener;
private final EntityRemoveEffectListener entityRemoveEffectListener;
/**
* Configs
@@ -112,6 +117,8 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
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() {
@@ -134,6 +141,8 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
ProtocolLibrary.getProtocolManager().removePacketListener(entityLookListener);
ProtocolLibrary.getProtocolManager().removePacketListener(entityMoveListener);
ProtocolLibrary.getProtocolManager().removePacketListener(entityTeleportListener);
ProtocolLibrary.getProtocolManager().removePacketListener(entityAddEffectListener);
ProtocolLibrary.getProtocolManager().removePacketListener(entityRemoveEffectListener);
}
public void disable() {
@@ -150,6 +159,8 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
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();
@@ -285,8 +296,12 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
// unlimited part
var player = event.getPlayer();
handlePlayerJoin(player);
}
@Override
public void handlePlayerJoin(Player player) {
this.putEntityIDToMap(player.getEntityId(), player);
// nameplate module part
@@ -297,14 +312,18 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
plugin.getScheduler().runTaskAsyncLater(() -> {
if (player.isOnline())
this.createNameTag(player);
}, 200, TimeUnit.MILLISECONDS);
}, 50, TimeUnit.MILLISECONDS);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
// unlimited part
var player = event.getPlayer();
handlePlayerQuit(player);
}
@Override
public void handlePlayerQuit(Player player) {
this.removeEntityIDFromMap(player.getEntityId());
this.teamTagManager.handlePlayerQuit(player);
this.unlimitedTagManager.handlePlayerQuit(player);
@@ -331,6 +350,17 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
}
// 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))
@@ -590,7 +620,7 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
return tagMode;
}
@NotNull
@Nullable
@Override
public Nameplate getNameplate(@NotNull String key) {
return nameplateMap.get(key);
@@ -660,4 +690,12 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
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);
// }
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.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);
}
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.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));
// }
}
}
@@ -30,12 +30,13 @@ public class EntityDestroyListener extends PacketAdapter {
private final NameplateManagerImpl manager;
public EntityDestroyListener(NameplateManagerImpl manager) {
super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGH, PacketType.Play.Server.ENTITY_DESTROY);
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));
}
@@ -30,12 +30,13 @@ public class EntityLookListener extends PacketAdapter {
private final NameplateManagerImpl manager;
public EntityLookListener(NameplateManagerImpl manager) {
super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGHEST, PacketType.Play.Server.REL_ENTITY_MOVE_LOOK);
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),
@@ -30,12 +30,13 @@ public class EntityMoveListener extends PacketAdapter {
private final NameplateManagerImpl manager;
public EntityMoveListener(NameplateManagerImpl manager) {
super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGHEST, PacketType.Play.Server.REL_ENTITY_MOVE);
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),
@@ -0,0 +1,44 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.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));
// }
}
}
@@ -29,12 +29,13 @@ public class EntitySpawnListener extends PacketAdapter {
private final NameplateManagerImpl manager;
public EntitySpawnListener(NameplateManagerImpl manager) {
super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGH, CustomNameplatesPlugin.getInstance().getVersionManager().isVersionNewerThan1_20_R2() ? PacketType.Play.Server.SPAWN_ENTITY : PacketType.Play.Server.NAMED_ENTITY_SPAWN);
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));
}
}
@@ -30,12 +30,13 @@ public class EntityTeleportListener extends PacketAdapter {
private final NameplateManagerImpl manager;
public EntityTeleportListener(NameplateManagerImpl manager) {
super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGHEST, PacketType.Play.Server.ENTITY_TELEPORT);
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(),
@@ -22,12 +22,15 @@ 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;
@@ -36,18 +39,19 @@ public class MagicCosmeticsListener implements Listener {
this.unlimitedTagManager = unlimitedTagManager;
}
@EventHandler
@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
@EventHandler (ignoreCancelled = true)
public void onEnterBlackListWorld(PlayerChangeBlacklistEvent event) {
var player = event.getPlayer();
if (unlimitedTagManager.getUnlimitedObject(player.getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) {
@@ -59,40 +63,46 @@ public class MagicCosmeticsListener implements Listener {
final Cosmetic cosmetic = playerData.getHat();
if (cosmetic instanceof Hat hat) {
unlimitedPlayer.setHatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY());
unlimitedPlayer.teleport();
}
}
}
}
}
@EventHandler
@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
@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
@EventHandler (ignoreCancelled = true)
public void onDataLoaded(PlayerDataLoadEvent event) {
for (Cosmetic cosmetic : event.getEquippedCosmetics()) {
if (cosmetic instanceof Hat hat) {
if (unlimitedTagManager.getUnlimitedObject(event.getPlayerData().getUniqueId()) instanceof UnlimitedPlayer unlimitedPlayer) {
unlimitedPlayer.setHatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY());
}
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);
}
}
}
@@ -38,12 +38,13 @@ public class PlayerInfoListener extends PacketAdapter {
private final TeamTagManagerImpl manager;
public PlayerInfoListener(TeamTagManagerImpl manager) {
super(CustomNameplatesPlugin.getInstance(), ListenerPriority.HIGHEST, PacketType.Play.Server.PLAYER_INFO);
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<EnumWrappers.PlayerInfoAction> actions = packet.getPlayerInfoActions().read(0);
if (!actions.contains(EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME))
@@ -28,6 +28,7 @@ import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.listener.Pla
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;
@@ -84,11 +85,12 @@ public class TeamTagManagerImpl implements TeamTagManager {
ProtocolLibrary.getProtocolManager().removePacketListener(tabListener);
}
@NotNull
@Override
@SuppressWarnings("DuplicatedCode")
public TeamPlayer createTagForPlayer(Player player, String prefix, String suffix) {
if (this.teamPlayerMap.containsKey(player.getUniqueId())) {
return null;
return this.teamPlayerMap.get(player.getUniqueId());
}
var teamPlayer = new TeamPlayer(this, player, prefix, suffix);
@@ -26,6 +26,8 @@ import net.momirealms.customnameplates.api.mechanic.tag.unlimited.DynamicTextEnt
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;
@@ -47,6 +49,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
private final Requirement[] ownerRequirements;
private final Requirement[] viewerRequirements;
private final Vector<Player> viewers;
private Player[] viewerArray;
private final int refreshFrequency;
private final int checkFrequency;
private int checkTimer;
@@ -71,10 +74,11 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
this.viewerRequirements = viewerRequirements;
this.checkFrequency = checkFrequency;
this.refreshFrequency = refreshFrequency;
this.viewers = new Vector<>();
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
@@ -116,21 +120,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
checkTimer++;
if (checkTimer >= checkFrequency) {
checkTimer = 0;
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);
}
}
}
updateVisibility();
}
refreshTimer++;
@@ -141,12 +131,32 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
}
}
@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);
}
@@ -156,6 +166,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
return;
}
this.viewers.add(player);
viewersToArray();
spawn(player, owner.getPlayer().getPose());
}
@@ -168,7 +179,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
public void setOffset(double offset) {
if (yOffset == offset) return;
yOffset = offset;
for (Player all : viewers) {
for (Player all : viewerArray) {
PacketManager.getInstance().send(all, getTeleportPacket(0));
}
}
@@ -187,18 +198,19 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
@Override
public void spawn(Pose pose) {
for (Player all : viewers) {
for (Player all : viewerArray) {
spawn(all, pose);
}
}
@Override
public void destroy() {
for (Player all : viewers) {
for (Player all : viewerArray) {
PacketManager.getInstance().send(all, destroyPacket);
}
viewerText.clear();
viewers.clear();
viewersToArray();
}
@Override
@@ -210,15 +222,15 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
@Override
public void teleport(double x, double y, double z, boolean onGround) {
PacketContainer packet = getTeleportPacket(x, y, z, onGround);
for (Player all : viewers) {
for (Player all : viewerArray) {
PacketManager.getInstance().send(all, packet);
}
}
@Override
public void teleport() {
PacketContainer packet = getTeleportPacket(getCorrection(owner.getPlayer().getPose()));
for (Player viewer : viewers) {
PacketContainer packet = getTeleportPacket(getPlayerHeight());
for (Player viewer : viewerArray) {
PacketManager.getInstance().send(viewer, packet);
}
}
@@ -233,7 +245,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
@Override
public void setSneak(boolean isSneaking, boolean onGround) {
if (!onGround) {
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, viewerText.getLatestValue(viewer), isSneaking));
}
}
@@ -247,7 +259,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
@Override
public void move(short x, short y, short z, boolean onGround) {
PacketContainer packet = getMovePacket(x, y, z, onGround);
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
PacketManager.getInstance().send(viewer, packet);
}
}
@@ -268,14 +280,14 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
@Override
public void respawn(Pose pose) {
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
respawn(viewer, pose);
}
}
@Override
public void updateText() {
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
updateText(viewer);
}
}
@@ -296,7 +308,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
public void handlePose(Pose previous, Pose pose) {
// Add delay to prevent the tag from appearing earlier
CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> {
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
respawn(viewer, pose);
}
}, 20, TimeUnit.MILLISECONDS);
@@ -316,7 +328,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
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() + getCorrection(owner.getPlayer().getPose()) + yOffset);
packet.getDoubles().write(1, y + owner.getHatOffset() + getPlayerHeight() + yOffset);
packet.getDoubles().write(2, z);
packet.getBooleans().write(0, onGround);
return packet;
@@ -332,14 +344,14 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
return packet;
}
private Location getEntityLocation(double correction) {
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 += correction;
y += playerHeight;
return new Location(null, x, y, z);
}
@@ -348,7 +360,7 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
entityPacket.getModifier().write(0, entityId);
entityPacket.getModifier().write(1, uuid);
entityPacket.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND);
Location location = getEntityLocation(getCorrection(pose));
Location location = getEntityLocation(getPlayerHeight());
entityPacket.getDoubles().write(0, location.getX());
entityPacket.getDoubles().write(1, location.getY());
entityPacket.getDoubles().write(2, location.getZ());
@@ -356,13 +368,14 @@ public class DynamicTextEntityImpl implements DynamicTextEntity {
return new PacketContainer[] {entityPacket, metaPacket};
}
private double getCorrection(Pose pose) {
return switch (pose) {
case STANDING -> 1.8;
case SNEAKING -> 1.5;
case SLEEPING -> 0.2;
case SWIMMING, FALL_FLYING, SPIN_ATTACK -> 0.55;
default -> 0;
};
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]);
}
}
@@ -23,6 +23,8 @@ 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;
@@ -42,11 +44,12 @@ public class StaticTextEntityImpl implements StaticTextEntity {
private double yOffset;
private final int entityId;
private final Vector<Player> viewers;
private Player[] viewerArray;
private final ConcurrentHashMap<UUID, String> textCache;
private final NearbyRule comeRule;
private final NearbyRule leaveRule;
private String defaultText;
private final String plugin;
private final String id;
private final PacketContainer destroyPacket;
public StaticTextEntityImpl (
@@ -55,7 +58,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
NearbyRule comeRule,
NearbyRule leaveRule,
String defaultText,
String plugin
String id
) {
this.entityId = FakeEntityUtils.getAndIncrease();
this.owner = owner;
@@ -65,14 +68,15 @@ public class StaticTextEntityImpl implements StaticTextEntity {
this.comeRule = comeRule;
this.leaveRule = leaveRule;
this.defaultText = defaultText;
this.plugin = plugin;
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 getPlugin() {
return plugin;
public String getID() {
return id;
}
@Override
@@ -109,6 +113,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
return;
}
this.viewers.remove(player);
viewersToArray();
destroy(player);
}
@@ -118,6 +123,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
return;
}
this.viewers.add(player);
viewersToArray();
spawn(player, owner.getEntity().getPose());
}
@@ -130,7 +136,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
public void setOffset(double offset) {
if (yOffset == offset) return;
yOffset = offset;
for (Player all : viewers) {
for (Player all : viewerArray) {
PacketManager.getInstance().send(all, getTeleportPacket());
}
}
@@ -139,7 +145,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
public void setText(String text) {
if (text.equals(defaultText)) return;
this.defaultText = text;
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
PacketManager.getInstance().send(viewer, FakeEntityUtils.getMetaPacket(entityId, getText(viewer), owner.getEntity().isSneaking()));
}
}
@@ -174,11 +180,12 @@ public class StaticTextEntityImpl implements StaticTextEntity {
@Override
public void destroy() {
for (Player all : viewers) {
for (Player all : viewerArray) {
PacketManager.getInstance().send(all, destroyPacket);
}
viewers.clear();
textCache.clear();
viewersToArray();
}
@Override
@@ -190,7 +197,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
@Override
public void teleport(double x, double y, double z, boolean onGround) {
PacketContainer packet = getTeleportPacket(x, y, z, onGround);
for (Player all : viewers) {
for (Player all : viewerArray) {
PacketManager.getInstance().send(all, packet);
}
}
@@ -205,7 +212,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
@Override
public void teleport() {
PacketContainer packet = getTeleportPacket();
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
PacketManager.getInstance().send(viewer, packet);
}
}
@@ -218,7 +225,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
@Override
public void move(short x, short y, short z, boolean onGround) {
PacketContainer packet = getMovePacket(x, y, z, onGround);
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
PacketManager.getInstance().send(viewer, packet);
}
}
@@ -239,7 +246,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
@Override
public void respawn(Pose pose) {
for (Player viewer : viewers) {
for (Player viewer : viewerArray) {
respawn(viewer, pose);
}
}
@@ -253,12 +260,21 @@ public class StaticTextEntityImpl implements StaticTextEntity {
public void handlePose(Pose previous, Pose pose) {
// Add delay to prevent the tag from appearing earlier
CustomNameplatesPlugin.get().getScheduler().runTaskAsyncLater(() -> {
for (Player viewer : viewers) {
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);
@@ -273,7 +289,7 @@ public class StaticTextEntityImpl implements StaticTextEntity {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT);
packet.getIntegers().write(0, entityId);
packet.getDoubles().write(0, x);
packet.getDoubles().write(1, y + yOffset);
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;
@@ -295,6 +311,8 @@ public class StaticTextEntityImpl implements StaticTextEntity {
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);
}
@@ -305,9 +323,21 @@ public class StaticTextEntityImpl implements StaticTextEntity {
entityPacket.getEntityTypeModifier().write(0, EntityType.ARMOR_STAND);
Location location = getEntityLocation();
entityPacket.getDoubles().write(0, location.getX());
entityPacket.getDoubles().write(1, location.getY() + (pose == Pose.SNEAKING ? -0.3 : 0));
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]);
}
}
@@ -26,6 +26,8 @@ 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 {
@@ -33,21 +35,25 @@ public class UnlimitedEntity implements EntityTagEntity {
protected final UnlimitedTagManagerImpl manager;
protected final Entity entity;
protected final Vector<Player> nearbyPlayers;
protected Player[] nearbyPlayerArray;
protected final Vector<StaticTextEntity> 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 Vector<Player> getNearbyPlayers() {
return nearbyPlayers;
public Player[] getNearbyPlayers() {
return nearbyPlayerArray;
}
public void addNearbyPlayerNaturally(Player player) {
@@ -55,7 +61,8 @@ public class UnlimitedEntity implements EntityTagEntity {
return;
}
nearbyPlayers.add(player);
for (StaticTextEntity tag : staticTags) {
playerVectorToArray();
for (StaticTextEntity tag : staticTagArray) {
if (tag.getComeRule().isPassed(player, entity)) {
tag.addPlayerToViewers(player);
}
@@ -67,7 +74,8 @@ public class UnlimitedEntity implements EntityTagEntity {
return;
}
nearbyPlayers.remove(player);
for (StaticTextEntity tag : staticTags) {
playerVectorToArray();
for (StaticTextEntity tag : staticTagArray) {
if (tag.getLeaveRule().isPassed(player, entity)) {
tag.removePlayerFromViewers(player);
}
@@ -91,8 +99,11 @@ public class UnlimitedEntity implements EntityTagEntity {
return;
}
staticTags.add(tag);
staticTagVectorToArray();
for (Player all : nearbyPlayers) {
tag.addPlayerToViewers(all);
if (tag.getComeRule().isPassed(all, entity)) {
tag.addPlayerToViewers(all);
}
}
}
@@ -107,12 +118,13 @@ public class UnlimitedEntity implements EntityTagEntity {
public void removeTag(StaticTextEntity tag) {
if (staticTags.remove(tag)) {
tag.destroy();
staticTagVectorToArray();
}
}
@Override
public Vector<StaticTextEntity> getStaticTags() {
return staticTags;
public Collection<StaticTextEntity> getStaticTags() {
return new ArrayList<>(staticTags);
}
@Override
@@ -121,7 +133,8 @@ public class UnlimitedEntity implements EntityTagEntity {
return;
}
nearbyPlayers.add(player);
for (StaticTextEntity tag : staticTags) {
playerVectorToArray();
for (StaticTextEntity tag : staticTagArray) {
tag.addPlayerToViewers(player);
}
}
@@ -132,32 +145,32 @@ public class UnlimitedEntity implements EntityTagEntity {
return;
}
nearbyPlayers.remove(player);
for (StaticTextEntity tag : staticTags) {
playerVectorToArray();
for (StaticTextEntity tag : staticTagArray) {
tag.removePlayerFromViewers(player);
}
}
public void move(Player receiver, short x, short y, short z, boolean onGround) {
for (StaticTextEntity tag : staticTags) {
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 : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
tag.teleport(receiver, x, y, z, onGround);
}
}
public void teleport() {
for (StaticTextEntity tag : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
tag.teleport();
}
}
public void handlePose(Pose previous, Pose pose) {
for (StaticTextEntity tag : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
tag.handlePose(previous, pose);
}
}
@@ -165,16 +178,44 @@ public class UnlimitedEntity implements EntityTagEntity {
@Override
public void destroy() {
manager.removeUnlimitedEntityFromMap(entity.getUniqueId());
for (StaticTextEntity tag : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
tag.destroy();
}
nearbyPlayers.clear();
staticTags.clear();
staticTagArray = null;
nearbyPlayerArray = null;
}
public void respawn() {
for (StaticTextEntity tag : staticTags) {
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);
}
}
}
}
}
}
@@ -26,12 +26,15 @@ 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<DynamicTextEntity> dynamicTags;
private DynamicTextEntity[] dynamicTagArray;
private double hatOffset;
private boolean isPreviewing;
@@ -39,6 +42,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
super(manager, player);
this.owner = player;
this.dynamicTags = new Vector<>();
this.dynamicTagVectorToArray();
}
@Override
@@ -47,7 +51,8 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
return;
}
dynamicTags.add(tag);
for (Player all : nearbyPlayers) {
dynamicTagVectorToArray();
for (Player all : getNearbyPlayers()) {
if (tag.canShow() && tag.canSee(all)) {
tag.addPlayerToViewers(all);
}
@@ -60,8 +65,11 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
return;
}
staticTags.add(tag);
for (Player all : nearbyPlayers) {
tag.addPlayerToViewers(all);
staticTagVectorToArray();
for (Player all : getNearbyPlayers()) {
if (tag.getComeRule().isPassed(all, owner)) {
tag.addPlayerToViewers(all);
}
}
}
@@ -83,19 +91,13 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
public void removeTag(DynamicTextEntity tag) {
if (dynamicTags.remove(tag)) {
tag.destroy();
dynamicTagVectorToArray();
}
}
@Override
public void removeTag(StaticTextEntity tag) {
if (staticTags.remove(tag)) {
tag.destroy();
}
}
@Override
public Vector<DynamicTextEntity> getDynamicTags() {
return dynamicTags;
public Collection<DynamicTextEntity> getDynamicTags() {
return new ArrayList<>(dynamicTags);
}
@Override
@@ -128,7 +130,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void updateText() {
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.updateText();
}
}
@@ -144,13 +146,14 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
return;
}
nearbyPlayers.add(player);
playerVectorToArray();
setNameInvisibleFor(player);
for (StaticTextEntity tag : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
if (tag.getComeRule().isPassed(player, entity)) {
tag.addPlayerToViewers(player);
}
}
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
if (tag.canShow() && tag.canSee(player)) {
tag.addPlayerToViewers(player);
}
@@ -162,14 +165,15 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
if (!nearbyPlayers.contains(player)) {
return;
}
super.nearbyPlayers.remove(player);
nearbyPlayers.remove(player);
playerVectorToArray();
setNameVisibleFor(player);
for (StaticTextEntity tag : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
if (tag.getLeaveRule().isPassed(player, entity)) {
tag.removePlayerFromViewers(player);
}
}
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.removePlayerFromViewers(player);
}
}
@@ -177,22 +181,26 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void destroy() {
manager.removeUnlimitedEntityFromMap(entity.getUniqueId());
for (Player viewer : nearbyPlayers) {
for (Player viewer : getNearbyPlayers()) {
setNameVisibleFor(viewer);
}
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.destroy();
}
for (StaticTextEntity tag : staticTags) {
for (StaticTextEntity tag : staticTagArray) {
tag.destroy();
}
nearbyPlayers.clear();
dynamicTags.clear();
staticTags.clear();
staticTagArray = null;
}
public void sneak(boolean sneaking, boolean flying) {
for (DynamicTextEntity tag : dynamicTags) {
for (StaticTextEntity tag : staticTagArray) {
tag.setSneak(sneaking, !flying);
}
for (DynamicTextEntity tag : dynamicTagArray) {
tag.setSneak(sneaking, !flying);
}
}
@@ -200,7 +208,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void respawn() {
super.respawn();
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.respawn(owner.getPose());
}
}
@@ -208,7 +216,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void move(Player receiver, short x, short y, short z, boolean onGround) {
super.move(receiver, x, y, z, onGround);
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.move(receiver, x, y, z, onGround);
}
}
@@ -216,7 +224,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void teleport(Player receiver, double x, double y, double z, boolean onGround) {
super.teleport(receiver, x, y, z, onGround);
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.teleport(receiver, x, y, z, onGround);
}
}
@@ -224,7 +232,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void teleport() {
super.teleport();
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.teleport();
}
}
@@ -232,7 +240,7 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
@Override
public void handlePose(Pose previous, Pose pose) {
super.handlePose(previous, pose);
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.handlePose(previous, pose);
}
}
@@ -264,8 +272,18 @@ public class UnlimitedPlayer extends UnlimitedEntity implements EntityTagPlayer
}
public void timer() {
for (DynamicTextEntity tag : dynamicTags) {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.timer();
}
}
public void updateVisibility() {
for (DynamicTextEntity tag : dynamicTagArray) {
tag.updateVisibility();
}
}
private void dynamicTagVectorToArray() {
dynamicTagArray = dynamicTags.toArray(new DynamicTextEntity[0]);
}
}
@@ -24,8 +24,10 @@ 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;
@@ -44,6 +46,7 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
private final ConcurrentHashMap<UUID, UnlimitedEntity> unlimitedEntityMap;
private CancellableTask refreshTask;
private MagicCosmeticsListener magicCosmeticsListener;
private DisguiseListener disguiseListener;
private final VehicleChecker vehicleChecker;
public UnlimitedTagManagerImpl(NameplateManager nameplateManager) {
@@ -53,6 +56,9 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
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() {
@@ -82,6 +88,9 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
if (this.magicCosmeticsListener != null) {
Bukkit.getPluginManager().registerEvents(magicCosmeticsListener, CustomNameplatesPlugin.get());
}
if (this.disguiseListener != null) {
Bukkit.getPluginManager().registerEvents(disguiseListener, CustomNameplatesPlugin.get());
}
}
public void unload() {
@@ -95,6 +104,9 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
if (this.magicCosmeticsListener != null) {
HandlerList.unregisterAll(magicCosmeticsListener);
}
if (this.disguiseListener != null) {
HandlerList.unregisterAll(disguiseListener);
}
}
@NotNull
@@ -106,7 +118,7 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
setting.getComeRule(),
setting.getLeaveRule(),
setting.getDefaultText(),
setting.getPlugin()
setting.getID()
);
}
@@ -126,6 +138,10 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
@Override
public UnlimitedEntity createOrGetTagForEntity(Entity entity) {
if (entity instanceof Player player) {
return createOrGetTagForPlayer(player);
}
final UUID uuid = entity.getUniqueId();
if (this.unlimitedEntityMap.containsKey(uuid)) {
return this.unlimitedEntityMap.get(uuid);
@@ -156,7 +172,9 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
unlimitedPlayer
);
unlimitedPlayer.addNearByPlayerToMap(48);
if (player.getGameMode() != GameMode.SPECTATOR) {
unlimitedPlayer.addNearByPlayerToMap(48);
}
return unlimitedPlayer;
}
@@ -169,11 +187,21 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
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);
}
@@ -336,7 +336,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
entry.getKey(),
SwitchText.builder()
.toParse(Objects.requireNonNull(innerSection.getString("switch")))
.defaultValue(innerSection.getString("default"))
.defaultValue(innerSection.getString("default", ""))
.valueMap(valueMap)
.build()
);
@@ -27,11 +27,14 @@ 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 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;
@@ -123,6 +126,8 @@ public class RequirementManagerImpl implements RequirementManager {
this.registerInListRequirement();
this.registerGameModeRequirement();
this.registerGeyserRequirement();
this.registerDisguisedRequirement();
this.registerDisguisedTypeRequirement();
}
/**
@@ -304,23 +309,58 @@ public class RequirementManagerImpl implements RequirementManager {
});
}
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<String> biomes = new HashSet<>(ConfigUtils.stringListArgs(args));
return condition -> {
String currentBiome = BiomeAPI.getBiome(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation());
String currentBiome = BiomeAPI.getBiomeAt(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation());
return biomes.contains(currentBiome);
};
});
registerRequirement("!biome", (args) -> {
HashSet<String> biomes = new HashSet<>(ConfigUtils.stringListArgs(args));
return condition -> {
String currentBiome = BiomeAPI.getBiome(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation());
String currentBiome = BiomeAPI.getBiomeAt(Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getLocation());
return !biomes.contains(currentBiome);
};
});
}
private void registerDisguisedTypeRequirement() {
registerRequirement("disguised-type", (args) -> {
HashSet<String> 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<String> 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<String> worlds = new HashSet<>(ConfigUtils.stringListArgs(args));
@@ -338,7 +378,8 @@ public class RequirementManagerImpl implements RequirementManager {
return condition -> {
String currentWeather;
World world = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getWorld();
if (world.isThundering()) currentWeather = "thunder";
if (world.hasStorm()) currentWeather = "rainstorm";
else if (world.isThundering()) currentWeather = "thunder";
else if (world.isClearWeather()) currentWeather = "clear";
else currentWeather = "rain";
for (String weather : weathers)
@@ -42,7 +42,11 @@ public class FoliaSchedulerImpl implements SyncScheduler {
*/
@Override
public void runSyncTask(Runnable runnable, Location location) {
Bukkit.getRegionScheduler().execute(plugin, location, runnable);
if (location == null) {
Bukkit.getGlobalRegionScheduler().execute(plugin, runnable);
} else {
Bukkit.getRegionScheduler().execute(plugin, location, runnable);
}
}
/**
@@ -56,6 +60,9 @@ public class FoliaSchedulerImpl implements SyncScheduler {
*/
@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));
}
@@ -69,6 +76,15 @@ public class FoliaSchedulerImpl implements SyncScheduler {
*/
@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));
}
@@ -48,7 +48,7 @@ public class SchedulerImpl implements Scheduler {
}
/**
* Reloads the scheduler configuration based on CustomFishingPlugin settings.
* Reloads the scheduler configuration based on settings.
*/
public void reload() {
try {
@@ -28,6 +28,7 @@ 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;
@@ -85,6 +86,7 @@ public class CNConfig {
public static boolean enableShader;
public static boolean huskChatChannel;
public static boolean carbonChatChannel;
public static boolean hasLibsDisguise;
public static void load() {
try {
@@ -176,6 +178,8 @@ public class CNConfig {
legacyColorSupport = config.getBoolean("other-settings.legacy-color-code-support");
createRealTeam = config.getBoolean("other-settings.create-real-teams", false);
hasLibsDisguise = Bukkit.getPluginManager().getPlugin("LibsDisguises") != null;
}
public static boolean isOtherTeamPluginHooked() {
@@ -134,7 +134,7 @@ public class RedisManager extends AbstractStorage {
* @param message The message to send.
*/
public void sendRedisMessage(@NotNull String channel, @NotNull String message) {
try (Jedis jedis = jedisPool.getResource()) {
try (Jedis jedis = getJedis()) {
jedis.publish(channel, message);
plugin.debug("Sent Redis message: " + message);
}
@@ -149,10 +149,9 @@ public class RedisManager extends AbstractStorage {
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid) {
var future = new CompletableFuture<Optional<PlayerData>>();
plugin.getScheduler().runTaskAsync(() -> {
try (Jedis jedis = jedisPool.getResource()) {
try (Jedis jedis = getJedis()) {
byte[] key = getRedisKey("cn_data", uuid);
byte[] data = jedis.get(key);
jedis.del(key);
if (data != null) {
future.complete(Optional.of(plugin.getStorageManager().fromBytes(data)));
plugin.debug("Redis data retrieved for " + uuid + "; normal data");
@@ -172,7 +171,7 @@ public class RedisManager extends AbstractStorage {
public CompletableFuture<Boolean> updatePlayerData(UUID uuid, PlayerData playerData) {
var future = new CompletableFuture<Boolean>();
plugin.getScheduler().runTaskAsync(() -> {
try (Jedis jedis = jedisPool.getResource()) {
try (Jedis jedis = getJedis()) {
jedis.setex(
getRedisKey("cn_data", uuid),
600,
@@ -55,7 +55,6 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme
} else if (getStorageType() == StorageType.MySQL) {
try {
Class.forName("com.mysql.jdbc.Driver");
LogUtils.warn("It seems that you are not using MySQL 8.0+. It's recommended to update.");
} catch (ClassNotFoundException e2) {
LogUtils.warn("No MySQL driver is found");
}
@@ -76,7 +75,7 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase impleme
return;
}
super.tablePrefix = section.getString("table-prefix", "customfishing");
super.tablePrefix = section.getString("table-prefix", "nameplates");
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setUsername(section.getString("user", "root"));
hikariConfig.setPassword(section.getString("password", "pa55w0rd"));
@@ -214,7 +214,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage {
public Set<UUID> getUniqueUsers(boolean legacy) {
Set<UUID> uuids = new HashSet<>();
try (Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement(String.format(SqlConstants.SQL_SELECT_ALL_UUID, legacy ? getTableName("fishingbag") : getTableName("data")))) {
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"));
@@ -19,19 +19,23 @@ 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 org.h2.jdbcx.JdbcConnectionPool;
import java.io.File;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.EnumSet;
/**
* An implementation of AbstractSQLDatabase that uses the H2 embedded database for player data storage.
*/
public class H2Impl extends AbstractSQLDatabase {
private JdbcConnectionPool connectionPool;
private Object connectionPool;
private Method disposeMethod;
private Method getConnectionMethod;
public H2Impl(CustomNameplatesPlugin plugin) {
super(plugin);
@@ -44,10 +48,20 @@ public class H2Impl extends AbstractSQLDatabase {
public void initialize() {
YamlConfiguration config = plugin.getConfig("database.yml");
File databaseFile = new File(plugin.getDataFolder(), config.getString("H2.file", "data.db"));
super.tablePrefix = config.getString("H2.table-prefix", "customnameplates");
super.tablePrefix = config.getString("H2.table-prefix", "nameplates");
final String url = String.format("jdbc:h2:%s", databaseFile.getAbsolutePath());
this.connectionPool = JdbcConnectionPool.create(url, "sa", "");
ClassLoader classLoader = ((CustomNameplatesPluginImpl) 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);
this.connectionPool = createPoolMethod.invoke(null, url, "sa", "");
this.disposeMethod = connectionClass.getMethod("dispose");
this.getConnectionMethod = connectionClass.getMethod("getConnection");
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
super.createTableIfNotExist();
}
@@ -57,7 +71,11 @@ public class H2Impl extends AbstractSQLDatabase {
@Override
public void disable() {
if (connectionPool != null) {
connectionPool.dispose();
try {
disposeMethod.invoke(connectionPool);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
@@ -67,7 +85,11 @@ public class H2Impl extends AbstractSQLDatabase {
}
@Override
public Connection getConnection() throws SQLException {
return connectionPool.getConnection();
public Connection getConnection() {
try {
return (Connection) getConnectionMethod.invoke(connectionPool);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
@@ -51,7 +51,7 @@ public class SQLiteImpl extends AbstractSQLDatabase {
public void initialize() {
YamlConfiguration config = plugin.getConfig("database.yml");
this.databaseFile = new File(plugin.getDataFolder(), config.getString("SQLite.file", "data") + ".db");
super.tablePrefix = config.getString("SQLite.table-prefix", "customfishing");
super.tablePrefix = config.getString("SQLite.table-prefix", "nameplates");
super.createTableIfNotExist();
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.util;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.disguisetypes.Disguise;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
public class DisguiseUtils {
public static boolean isDisguised(Entity entity) {
return DisguiseAPI.isDisguised(entity);
}
public static double getDisguisedHeight(Entity entity) {
Disguise disguise = DisguiseAPI.getDisguise(entity);
return disguise.getHeight() + disguise.getWatcher().getYModifier() + disguise.getWatcher().getNameYModifier();
}
public static EntityType getDisguisedType(Entity entity) {
return DisguiseAPI.getDisguise(entity).getType().getEntityType();
}
}
@@ -36,7 +36,10 @@ public class ReflectionUtils {
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() {
try {
@@ -63,8 +66,10 @@ public class ReflectionUtils {
Method miniMessageInstanceGetMethod = miniMessageClass.getMethod("miniMessage");
miniMessageInstance = miniMessageInstanceGetMethod.invoke(null);
serializeComponentMethod = miniMessageClass.getMethod("serialize", componentClass);
Class<?> keyClass = Class.forName("net;kyori;adventure;key;Key".replace(";", "."));
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) {
}
@@ -90,6 +95,14 @@ public class ReflectionUtils {
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);
@@ -99,6 +112,15 @@ public class ReflectionUtils {
return "";
}
public static Object getKerFromString(String key) {
try {
return keyFromStringMethod.invoke(key);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static String getMiniMessageTextFromNonShadedComponent(Object component) {
try {
return (String) serializeComponentMethod.invoke(miniMessageInstance, component);
+3 -3
View File
@@ -91,19 +91,19 @@ other-settings:
create-real-teams: false
# It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below.
legacy-color-code-support: false
# 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
# Increase the size of corePoolSize when you are running a large server with many players fishing at the same time
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 is sent to players
# 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
@@ -1,8 +1,20 @@
# 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'
@@ -1,5 +1,8 @@
# namespace:font
minecraft:default:
# default width
default: 8
# available font data template: unifont/ascii/nonlatin_european/accented/unicode(legacy)
template-loading-sequence:
- unifont
- nonlatin_european
@@ -25,6 +28,16 @@ minecraft:asciillager:
- asciillager
customcrops:default:
default: 8
template-loading-sequence: []
values:
뀁: -2
뀂: 4
뀃: 6
뀄: 6
뀅: 4
minecraft:customcrops:
default: 8
template-loading-sequence: []
values:
@@ -33,6 +33,7 @@ unlimited:
# Conditions (Owner side)
owner-conditions:
potion-effect: "INVISIBILITY<0"
self-disguised: false # Requires LibsDisguises
# Conditions (Viewer side)
viewer-conditions: { }
tag_2:
@@ -42,6 +43,7 @@ unlimited:
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
+9
View File
@@ -11,5 +11,14 @@ tasks {
relocate ("net.kyori", "net.momirealms.customnameplates.libraries")
relocate ("org.bstats", "net.momirealms.customnameplates.libraries.bstats")
relocate ("net.momirealms.biomeapi", "net.momirealms.customnameplates.libraries.biomeapi")
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")
}
}