57 Commits
Author SHA1 Message Date
XiaoMoMi a5fd4f237e Fix shaders on 1.20.5+ 2024-08-13 16:49:25 +08:00
XiaoMoMi 5046372177 2.4.6-pre-1 2024-08-11 20:28:22 +08:00
XiaoMoMi df86e4bc33 Merge pull request #38 from DrZoddiak/feature-ignore
[Feature] Support Ignoring for Chat Bubbles
2024-08-10 01:01:07 +08:00
Jesse McKee b5e83ca984 Implements ignoring bubbles
Allows for plugins that allow for ignoring to also ignore chat bubbles.

Carbon was given special attention for this PR, but other plugins will maintain prior behavior.
2024-08-08 01:06:33 -05:00
XiaoMoMi 04bb10cb3d Merge pull request #37 from DrZoddiak/fix-channel-mode
Fix channel-mode
2024-08-08 13:46:41 +08:00
Jesse McKee e7371cc02a Fix JOINED channel-mode for CarbonChatProvider
#selectedChannel only gets the channel current selected by the player if it exists. If a user does NOT select a channel, and is instead put into a default channel, the result is still `null`.

This PR provides a fallback value for channels in this case.
2024-08-08 00:27:00 -05:00
Jesse McKee bee0b26a9f Fix CAN_JOIN channel-mode
the method in question requires 1 argument, and was being passed 0. This error was being swallowed and not shown in console, so it was not immediately obvious.

Reading the JD for #invoke
`If the underlying method is static, then the specified obj argument is ignored. It may be null.`

So in order to pass the arg required, and because the method is indeed static, the first parameter is now null.
2024-08-08 00:21:05 -05:00
XiaoMoMi 343c0e5cb0 Update build.gradle.kts 2024-08-04 16:48:29 +08:00
XiaoMoMi 81d3a888b8 2.4.4 2024-08-04 16:48:02 +08:00
XiaoMoMi 58e16e5376 Fixed nms on 1.18.2 2024-07-27 17:17:42 +08:00
XiaoMoMi cc8c3d6bcd 2.4.2 2024-07-25 22:41:15 +08:00
XiaoMoMi 3000b26bfa Fix async events 2024-07-03 15:56:47 +08:00
XiaoMoMi a800481574 Update NameplateManagerImpl.java 2024-07-03 15:23:46 +08:00
XiaoMoMi 82350ac0c4 Fix some issues 2024-07-03 15:09:39 +08:00
XiaoMoMi 35edd85434 Update en.yml 2024-06-28 22:51:15 +08:00
XiaoMoMi 99bfc28be8 1.21 support 2024-06-28 02:52:20 +08:00
XiaoMoMi 5ee27c126c fix shader on 1.20.5+ 2024-06-28 01:12:39 +08:00
XiaoMoMi 5282d5e8a7 use NMS to send bossbar 2024-06-28 00:21:19 +08:00
XiaoMoMi 0f7a72bf1e Merge pull request #33 from relpia/main
Create kr.yml
2024-06-26 19:45:58 +08:00
XiaoMoMi b36d0c23e1 some changes 2024-06-24 00:14:44 +08:00
Horibin f8219659c4 Add files via upload 2024-06-22 13:01:28 +09:00
XiaoMoMi eb730c8631 Merge pull request #32 from Vondri/patch-2
Create pl.yml
2024-05-25 17:03:59 +08:00
Vondri 12b8b74031 Create pl.yml
Translation for polish
2024-05-18 00:46:15 +02:00
XiaoMoMi 365aae10f5 2.3.3.9 2024-05-07 22:34:39 +08:00
XiaoMoMi 5e299bb0d4 Update build logics 2024-05-04 05:10:53 +08:00
XiaoMoMi 79a0892b09 [Update] Updated dependencies 2024-04-23 03:43:07 +08:00
XiaoMoMi a8bc4d8de5 update shaders 2024-04-22 01:14:48 +08:00
XiaoMoMi 6276d27d57 Merge branch 'main' of https://github.com/Xiao-MoMi/Custom-Nameplates 2024-04-16 23:39:06 +08:00
XiaoMoMi 096e6330d6 2.3.3.8 2024-04-16 23:39:01 +08:00
XiaoMoMi e41686aa31 Merge pull request #30 from Schroddinger/release
Tweaks to Spanish translation
2024-04-16 02:39:05 +08:00
Schroddinger 590b26ac2a Tweaks to Spanish translation 2024-04-14 11:19:13 -05:00
XiaoMoMi a7dc0db15d rename language files 2024-04-14 20:19:37 +08:00
XiaoMoMi c9250fea13 Merge pull request #28 from jhqwqmc/main
small changes
2024-04-07 21:33:26 +08:00
jhqwqmc fa914d52a2 del time-24 2024-04-07 21:12:10 +08:00
jhqwqmc efbf2d3b64 Update config file 2024-04-07 21:03:01 +08:00
jhqwqmc 40672aefb4 small changes
Add a 24-hour time variable
Fix weather detection issue
2024-04-07 21:00:50 +08:00
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
120 changed files with 3267 additions and 1525 deletions

No files matched your search

+2 -1
View File
@@ -1,11 +1,12 @@
# Custom-Nameplates
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Nameplates)
![Code Size](https://img.shields.io/github/languages/code-size/Xiao-MoMi/Custom-Nameplates)
![bStats Servers](https://img.shields.io/bstats/servers/16649)
![bStats Players](https://img.shields.io/bstats/players/16649)
![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Nameplates)
[![](https://jitpack.io/v/Xiao-MoMi/Custom-Nameplates.svg)](https://jitpack.io/#Xiao-MoMi/Custom-Nameplates)
<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>
+1 -1
View File
@@ -2,7 +2,7 @@ dependencies {
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.5")
implementation(project(":common"))
implementation("net.kyori:adventure-api:4.15.0")
implementation("net.kyori:adventure-api:4.16.0")
}
tasks {
@@ -26,5 +26,6 @@ public enum StorageType {
MySQL,
MariaDB,
MongoDB,
Redis
Redis,
None
}
@@ -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);
/**
@@ -28,13 +28,6 @@ public interface VersionManager {
*/
boolean isFolia();
/**
* Get bukkit version
*
* @return version
*/
String getServerVersion();
/**
* Check update
*
@@ -42,6 +35,8 @@ public interface VersionManager {
*/
CompletionStage<Boolean> checkUpdate();
boolean isVersionNewerThan1_20_5();
/**
* Get plugin version
*
@@ -66,6 +61,8 @@ public interface VersionManager {
boolean isVersionNewerThan1_19();
boolean isMojmap();
boolean isVersionNewerThan1_19_R2();
boolean isVersionNewerThan1_19_R3();
@@ -15,11 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.bungeecord.team;
package net.momirealms.customnameplates.api.mechanic.bubble;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public enum ChannelMode {
public interface TeamProvider {
String getTeam(ProxiedPlayer player, ProxiedPlayer viewer);
ALL,
JOINED,
CAN_JOIN
}
@@ -15,20 +15,27 @@
* 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);
public abstract boolean isIgnoring(Player sender, Player receiver);
}
@@ -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() {
+5 -4
View File
@@ -2,17 +2,17 @@ plugins {
id("java")
id("application")
id("maven-publish")
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.github.goooler.shadow") version "8.1.7"
}
allprojects {
version = "2.3.2.0"
version = "2.4.7"
apply<JavaPlugin>()
apply(plugin = "java")
apply(plugin = "application")
apply(plugin = "com.github.johnrengelman.shadow")
apply(plugin = "io.github.goooler.shadow")
apply(plugin = "org.gradle.maven-publish")
application {
@@ -20,8 +20,8 @@ allprojects {
}
repositories {
maven("https://maven.aliyun.com/repository/public/")
mavenCentral()
maven("https://maven.aliyun.com/repository/public/")
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://oss.sonatype.org/content/groups/public/")
maven("https://repo.dmulloy2.net/repository/public/")
@@ -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/")
}
}
+3 -3
View File
@@ -10,10 +10,10 @@ dependencies {
compileOnly("net.md-5:bungeecord-event:1.20-R0.2-SNAPSHOT")
compileOnly("net.md-5:bungeecord-proxy:1.20-R0.2-SNAPSHOT")
implementation("net.kyori:adventure-api:4.15.0")
implementation("net.kyori:adventure-api:4.16.0")
implementation("net.kyori:adventure-platform-bungeecord:4.3.2")
implementation("net.kyori:adventure-text-minimessage:4.15.0")
implementation("net.kyori:adventure-text-serializer-legacy:4.15.0")
implementation("net.kyori:adventure-text-minimessage:4.16.0")
implementation("net.kyori:adventure-text-serializer-legacy:4.16.0")
}
tasks {
@@ -1,47 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.bungeecord.team;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.tablist.SortingManager;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class TABProvider implements TeamProvider {
private final TabAPI api;
private final SortingManager sortingManager;
public TABProvider() {
this.api = TabAPI.getInstance();
this.sortingManager = api.getSortingManager();
}
@Override
public String getTeam(ProxiedPlayer player, ProxiedPlayer ignore) {
TabPlayer tabPlayer = api.getPlayer(player.getUniqueId());
if (tabPlayer == null) {
return null;
}
String forced = sortingManager.getForcedTeamName(tabPlayer);
if (forced != null) {
return forced;
}
return sortingManager.getOriginalTeamName(tabPlayer);
}
}
@@ -1,115 +0,0 @@
package net.momirealms.customnameplates.bungeecord.team;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.protocol.packet.Team;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class UnknownProvider extends PacketHandler implements Listener, TeamProvider {
private final ConcurrentHashMap<UUID, PlayerTeamView> teamViewMap;
public UnknownProvider() {
this.teamViewMap = new ConcurrentHashMap<>();
}
@Override
public void handle(Team team) {
switch (team.getMode()) {
case 0, 3 -> {
}
case 1 -> {
}
}
}
@Override
public String toString() {
return null;
}
@EventHandler
public void onPostLogin(PostLoginEvent event) {
teamViewMap.put(event.getPlayer().getUniqueId(), new PlayerTeamView());
}
@EventHandler
public void onDisConnect(PlayerDisconnectEvent event) {
teamViewMap.remove(event.getPlayer().getUniqueId());
}
@Override
public String getTeam(ProxiedPlayer player, ProxiedPlayer viewer) {
return null;
}
public static class PlayerTeamView {
private final HashMap<String, String> playerToTeamMap;
private final HashMap<String, String[]> teamToPlayerMap;
public PlayerTeamView() {
this.playerToTeamMap = new HashMap<>();
this.teamToPlayerMap = new HashMap<>();
}
public String getTeam(ProxiedPlayer player) {
return playerToTeamMap.get(player.getName());
}
public void removeTeam(String team) {
String[] removedMembers = teamToPlayerMap.remove(team);
if (removedMembers != null) {
for (String member : removedMembers) {
playerToTeamMap.remove(member);
}
}
}
public void addMembers(String team, Collection<String> members) {
String[] oldMembers = teamToPlayerMap.remove(team);
if (oldMembers != null) {
String[] newMembers = new String[members.size() + oldMembers.length];
int i = 0;
for (String m : oldMembers) {
newMembers[i] = m;
i++;
}
for (String m : members) {
newMembers[i] = m;
i++;
}
teamToPlayerMap.put(team, newMembers);
for (String member : newMembers) {
playerToTeamMap.put(member, team);
}
} else {
teamToPlayerMap.put(team, members.toArray(new String[0]));
for (String member : members) {
playerToTeamMap.put(member, team);
}
}
}
public void removeMembers(String team, Collection<String> members) {
String[] oldMembers = teamToPlayerMap.remove(team);
if (oldMembers != null) {
ArrayList<String> newMembers = new ArrayList<>(Arrays.asList(oldMembers));
for (String member : members) {
playerToTeamMap.remove(member);
newMembers.remove(member);
}
teamToPlayerMap.put(team, newMembers.toArray(new String[0]));
}
}
}
}
+1 -2
View File
@@ -1,5 +1,4 @@
name: CustomNameplates
main: net.momirealms.customnameplates.bungeecord.CustomNameplatesBungeeCord
version: '${version}'
author: XiaoMoMi
softDepends: [TAB, BungeeTabListPlus]
author: XiaoMoMi
-8
View File
@@ -1,8 +0,0 @@
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
+24 -17
View File
@@ -3,16 +3,19 @@ dependencies {
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
// command
compileOnly("dev.jorel:commandapi-bukkit-core:9.3.0")
compileOnly("dev.jorel:commandapi-bukkit-core:9.5.3")
// packet
compileOnly("com.comphenix.protocol:ProtocolLib:5.2.0-SNAPSHOT")
// papi
compileOnly("me.clip:placeholderapi:2.11.5")
compileOnly("me.clip:placeholderapi:2.11.6")
// config
compileOnly("dev.dejvokep:boosted-yaml:1.3.1")
compileOnly("dev.dejvokep:boosted-yaml:1.3.6")
// bStats
compileOnly("org.bstats:bstats-bukkit:3.0.2")
// team
compileOnly("me.neznamy:tab-api:4.0.2")
@@ -22,11 +25,11 @@ dependencies {
compileOnly("com.google.code.gson:gson:2.10.1")
// database
compileOnly("org.xerial:sqlite-jdbc:3.43.0.0")
compileOnly("org.xerial:sqlite-jdbc:3.45.3.0")
compileOnly("com.h2database:h2:2.2.224")
compileOnly("org.mongodb:mongodb-driver-sync:4.11.1")
compileOnly("org.mongodb:mongodb-driver-sync:5.0.1")
compileOnly("com.zaxxer:HikariCP:5.0.1")
compileOnly("redis.clients:jedis:5.1.0")
compileOnly("redis.clients:jedis:5.1.3")
// others
compileOnly("com.github.LoneDev6:api-itemsadder:3.5.0c-r5")
@@ -34,34 +37,38 @@ 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")
// chat channels
compileOnly(files("libs/VentureChat-3.7.1.jar"))
compileOnly(files("libs/TrChat-2.0.11.jar"))
compileOnly(files("libs/carbonchat-paper-3.0.0-beta.26.jar"))
compileOnly(files("libs/AdvancedChat-1.3.7.jar"))
compileOnly("net.william278:huskchat:2.7.1")
// api module
implementation(project(":api"))
implementation(project(":common"))
implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.35")
// adventure
implementation("net.kyori:adventure-api:4.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"))
implementation("net.kyori:adventure-api:4.17.0")
implementation("net.kyori:adventure-platform-bukkit:4.3.3")
}
tasks {
shadowJar {
relocate ("net.kyori", "net.momirealms.customnameplates.libraries")
relocate ("org.bstats", "net.momirealms.customnameplates.libraries.bstats")
relocate ("net.momirealms.biomeapi", "net.momirealms.customnameplates.libraries.biomeapi")
relocate ("net.momirealms.sparrow.heart", "net.momirealms.customnameplates.libraries.sparrow")
relocate ("org.apache.commons.pool2", "net.momirealms.customnameplates.libraries.commonspool2")
relocate ("com.mysql", "net.momirealms.customnameplates.libraries.mysql")
relocate ("org.mariadb", "net.momirealms.customnameplates.libraries.mariadb")
relocate ("com.zaxxer.hikari", "net.momirealms.customnameplates.libraries.hikari")
relocate ("redis.clients.jedis", "net.momirealms.customnameplates.libraries.jedis")
relocate ("com.mongodb", "net.momirealms.customnameplates.libraries.mongodb")
relocate ("org.bson", "net.momirealms.customnameplates.libraries.bson")
relocate ("dev.jorel.commandapi", "net.momirealms.customnameplates.libraries.commandapi")
relocate ("dev.dejvokep.boostedyaml", "net.momirealms.customnameplates.libraries.boostedyaml")
}
}
Binary file not shown.
@@ -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,40 @@ 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.versionManager = new VersionManagerImpl(this);
this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader()));
this.dependencyManager.loadDependencies(new ArrayList<>(
List.of(
Dependency.GSON,
Dependency.SLF4J_API,
Dependency.SLF4J_SIMPLE,
versionManager.isMojmap() ? Dependency.COMMAND_API_MOJMAP : Dependency.COMMAND_API,
Dependency.BOOSTED_YAML,
Dependency.MYSQL_DRIVER,
Dependency.MARIADB_DRIVER,
Dependency.MONGODB_DRIVER_SYNC,
Dependency.MONGODB_DRIVER_CORE,
Dependency.MONGODB_DRIVER_BSON,
Dependency.JEDIS,
Dependency.COMMONS_POOL_2,
Dependency.H2_DRIVER,
Dependency.SQLITE_DRIVER,
Dependency.BSTATS_BASE,
Dependency.HIKARI,
Dependency.BSTATS_BUKKIT
)
));
ReflectionUtils.load();
}
@@ -71,7 +98,6 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
return;
}
this.adventureManager = new AdventureManagerImpl(this);
this.versionManager = new VersionManagerImpl(this);
this.scheduler = new SchedulerImpl(this);
this.storageManager = new StorageManagerImpl(this);
this.requirementManager = new RequirementManagerImpl(this);
@@ -155,27 +181,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 +188,8 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
public PacketManager getPacketManager() {
return packetManager;
}
public DependencyManager getDependencyManager() {
return dependencyManager;
}
}
@@ -19,7 +19,6 @@ package net.momirealms.customnameplates.paper.adventure;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@@ -75,8 +74,8 @@ public class AdventureManagerImpl implements AdventureManager {
this.cacheSystem = new CacheSystem(CNConfig.cacheSize);
}
public WrappedChatComponent getWrappedChatComponentFromMiniMessage(String text) {
return cacheSystem.getWrappedChatComponentFromCache(text);
public String getJsonComponentFromMiniMessage(String text) {
return cacheSystem.getJsonComponentFromCache(text);
}
public Object getIChatComponentFromMiniMessage(String text) {
@@ -195,12 +194,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])
@@ -286,11 +285,15 @@ public class AdventureManagerImpl implements AdventureManager {
}
}
public Audience audience(Player player) {
return adventure.player(player);
}
public class CacheSystem {
private final LoadingCache<String, Object> miniMessageToIChatComponentCache;
private final LoadingCache<String, Component> miniMessageToComponentCache;
private final LoadingCache<String, WrappedChatComponent> miniMessageToWrappedChatComponentCache;
private final LoadingCache<String, String> miniMessageToJsonChatComponentCache;
public CacheSystem(int size) {
miniMessageToIChatComponentCache = CacheBuilder.newBuilder()
@@ -315,15 +318,15 @@ public class AdventureManagerImpl implements AdventureManager {
return fetchComponent(text);
}
});
miniMessageToWrappedChatComponentCache = CacheBuilder.newBuilder()
miniMessageToJsonChatComponentCache = CacheBuilder.newBuilder()
.maximumSize(size)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<>() {
@NotNull
@Override
public WrappedChatComponent load(@NotNull String text) {
return fetchWrappedChatComponent(text);
public String load(@NotNull String text) {
return fetchJsonChatComponent(text);
}
});
}
@@ -349,11 +352,11 @@ public class AdventureManagerImpl implements AdventureManager {
}
@NotNull
private WrappedChatComponent fetchWrappedChatComponent(String text) {
private String fetchJsonChatComponent(String text) {
if (CNConfig.legacyColorSupport) {
return WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text))));
return GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text)));
} else {
return WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(text)));
return GsonComponentSerializer.gson().serialize(MiniMessage.miniMessage().deserialize(text));
}
}
@@ -375,12 +378,12 @@ public class AdventureManagerImpl implements AdventureManager {
}
}
public WrappedChatComponent getWrappedChatComponentFromCache(String text) {
public String getJsonComponentFromCache(String text) {
try {
return miniMessageToWrappedChatComponentCache.get(text);
return miniMessageToJsonChatComponentCache.get(text);
} catch (ExecutionException e) {
e.printStackTrace();
return WrappedChatComponent.fromText("");
return "{\"text\":\"\"}";
}
}
}
@@ -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,272 @@
/*
* 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 CustomNameplates.
*/
public enum Dependency {
ASM(
"org.ow2.asm",
"asm",
"9.7",
null,
"asm"
),
ASM_COMMONS(
"org.ow2.asm",
"asm-commons",
"9.7",
null,
"asm-commons"
),
JAR_RELOCATOR(
"me.lucko",
"jar-relocator",
"1.7",
null,
"jar-relocator"
),
COMMAND_API(
"dev{}jorel",
"commandapi-bukkit-shade",
"9.5.3",
null,
"commandapi-bukkit",
Relocation.of("commandapi", "dev{}jorel{}commandapi")
),
COMMAND_API_MOJMAP(
"dev{}jorel",
"commandapi-bukkit-shade-mojang-mapped",
"9.5.3",
null,
"commandapi-bukkit-mojang-mapped",
Relocation.of("commandapi", "dev{}jorel{}commandapi")
),
MARIADB_DRIVER(
"org{}mariadb{}jdbc",
"mariadb-java-client",
"3.3.3",
null,
"mariadb-java-client",
Relocation.of("mariadb", "org{}mariadb")
),
BOOSTED_YAML(
"dev{}dejvokep",
"boosted-yaml",
"1.3.6",
null,
"boosted-yaml",
Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml")
),
MYSQL_DRIVER(
"com{}mysql",
"mysql-connector-j",
"9.0.0",
null,
"mysql-connector-j",
Relocation.of("mysql", "com{}mysql")
),
H2_DRIVER(
"com.h2database",
"h2",
"2.2.224",
null,
"h2database"
),
SQLITE_DRIVER(
"org.xerial",
"sqlite-jdbc",
"3.46.0.1",
null,
"sqlite-jdbc"
),
HIKARI(
"com{}zaxxer",
"HikariCP",
"5.1.0",
null,
"HikariCP",
Relocation.of("hikari", "com{}zaxxer{}hikari")
),
SLF4J_SIMPLE(
"org.slf4j",
"slf4j-simple",
"2.0.12",
null,
"slf4j-simple"
),
SLF4J_API(
"org.slf4j",
"slf4j-api",
"2.0.12",
null,
"slf4j-api"
),
MONGODB_DRIVER_CORE(
"org{}mongodb",
"mongodb-driver-core",
"5.1.0",
null,
"mongodb-driver-core",
Relocation.of("mongodb", "com{}mongodb"),
Relocation.of("bson", "org{}bson")
),
MONGODB_DRIVER_SYNC(
"org{}mongodb",
"mongodb-driver-sync",
"5.1.0",
null,
"mongodb-driver-sync",
Relocation.of("mongodb", "com{}mongodb"),
Relocation.of("bson", "org{}bson")
),
MONGODB_DRIVER_BSON(
"org{}mongodb",
"bson",
"5.1.0",
null,
"mongodb-bson",
Relocation.of("mongodb", "com{}mongodb"),
Relocation.of("bson", "org{}bson")
),
JEDIS(
"redis{}clients",
"jedis",
"5.1.2",
null,
"jedis",
Relocation.of("jedis", "redis{}clients{}jedis"),
Relocation.of("commonspool2", "org{}apache{}commons{}pool2")
),
BSTATS_BASE(
"org{}bstats",
"bstats-base",
"3.0.2",
null,
"bstats-base",
Relocation.of("bstats", "org{}bstats")
),
BSTATS_BUKKIT(
"org{}bstats",
"bstats-bukkit",
"3.0.2",
null,
"bstats-bukkit",
Relocation.of("bstats", "org{}bstats")
),
COMMONS_POOL_2(
"org{}apache{}commons",
"commons-pool2",
"2.12.0",
null,
"commons-pool2",
Relocation.of("commonspool2", "org{}apache{}commons{}pool2")
),
GSON(
"com.google.code.gson",
"gson",
"2.10.1",
null,
"gson"
);
private final String mavenRepoPath;
private final String version;
private final List<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,233 @@
/*
* 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.api.util.LogUtils;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import net.momirealms.customnameplates.paper.libraries.classpath.ClassPathAppender;
import net.momirealms.customnameplates.paper.libraries.dependencies.classloader.IsolatedClassLoader;
import net.momirealms.customnameplates.paper.libraries.dependencies.relocation.Relocation;
import net.momirealms.customnameplates.paper.libraries.dependencies.relocation.RelocationHandler;
import 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;
this.relocationHandler = new RelocationHandler(this);
}
@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 fileName = dependency.getFileName(null);
String forceRepo = dependency.getRepo();
if (forceRepo == null) {
// attempt to download the dependency from each repo in order.
for (DependencyRepository repo : DependencyRepository.values()) {
if (repo.getId().equals("maven") && TimeZone.getDefault().getID().startsWith("Asia")) {
continue;
}
try {
LogUtils.info("Downloading dependency(" + fileName + ") from " + repo.getUrl() + dependency.getMavenRepoPath());
repo.download(dependency, file);
LogUtils.info("Successfully downloaded " + fileName);
return file;
} catch (DependencyDownloadException e) {
lastError = e;
}
}
} else {
DependencyRepository repository = DependencyRepository.getByID(forceRepo);
if (repository != null) {
try {
LogUtils.info("Downloading dependency(" + fileName + ") from " + repository.getUrl() + dependency.getMavenRepoPath());
repository.download(dependency, file);
LogUtils.info("Successfully downloaded " + fileName);
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;
}
LogUtils.info("Remapping " + dependency.getFileName(null));
relocationHandler.remap(normalFile, remappedFile, rules);
LogUtils.info("Successfully remapped " + dependency.getFileName(null));
return remappedFile;
}
private static Path setupCacheDirectory(CustomNameplatesPluginImpl plugin) {
File folder = new File(plugin.getDataFolder(), "libs");
folder.mkdirs();
return folder.toPath();
}
@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 CustomNameplates 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,149 @@
/*
* 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 String getUrl() {
return url;
}
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 CustomNameplates 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,17 +24,18 @@ import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.ActionBarManager;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.ActionBarListener;
import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.ChatMessageListener;
import net.momirealms.customnameplates.paper.mechanic.actionbar.listener.SystemChatListener;
import net.momirealms.customnameplates.paper.mechanic.misc.DisplayController;
import net.momirealms.customnameplates.paper.setting.CNConfig;
import net.momirealms.customnameplates.paper.util.ConfigUtils;
import net.momirealms.customnameplates.paper.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -60,11 +61,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 +181,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 +219,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 +227,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 +238,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());
}
}
}
}
@@ -17,15 +17,12 @@
package net.momirealms.customnameplates.paper.mechanic.bossbar;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.events.PacketContainer;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager;
import net.momirealms.customnameplates.paper.util.ReflectionUtils;
import net.momirealms.sparrow.heart.SparrowHeart;
import net.momirealms.sparrow.heart.feature.bossbar.BossBarColor;
import net.momirealms.sparrow.heart.feature.bossbar.BossBarOverlay;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.util.UUID;
public class BossBar {
@@ -46,17 +43,17 @@ public class BossBar {
}
public void show() {
PacketManager.getInstance().send(owner, getCreatePacket());
SparrowHeart.getInstance().createBossBar(owner, uuid, AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(latestMiniMessage), BossBarColor.valueOf(barColor.name()), BossBarOverlay.valueOf(overlay.name()), 0f, false, false, false);
this.visible = true;
}
public void hide() {
PacketManager.getInstance().send(owner, getRemovePacket());
SparrowHeart.getInstance().removeBossBar(owner, uuid);
this.visible = false;
}
public void update() {
PacketManager.getInstance().send(owner, getUpdatePacket());
SparrowHeart.getInstance().updateBossBarName(owner, uuid, AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(latestMiniMessage));
}
public boolean isVisible() {
@@ -66,38 +63,4 @@ public class BossBar {
public void setMiniMessageText(String text) {
latestMiniMessage = text;
}
private PacketContainer getCreatePacket() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
packet.getModifier().write(0, uuid);
InternalStructure internalStructure = packet.getStructures().read(1);
internalStructure.getChatComponents().write(0, AdventureManagerImpl.getInstance().getWrappedChatComponentFromMiniMessage(latestMiniMessage));
internalStructure.getFloat().write(0,1F);
internalStructure.getEnumModifier(BarColor.class, 2).write(0, barColor);
internalStructure.getEnumModifier(Overlay.class, 3).write(0, overlay);
internalStructure.getModifier().write(4, false);
internalStructure.getModifier().write(5, false);
internalStructure.getModifier().write(6, false);
return packet;
}
private PacketContainer getUpdatePacket() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
packet.getModifier().write(0, uuid);
try {
Object chatComponent = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(latestMiniMessage);
Object updatePacket = ReflectionUtils.getUpdateConstructor().newInstance(chatComponent);
packet.getModifier().write(1, updatePacket);
} catch (InvocationTargetException | IllegalAccessException | InstantiationException e) {
throw new RuntimeException(e);
}
return packet;
}
private PacketContainer getRemovePacket() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.BOSS);
packet.getModifier().write(0, uuid);
packet.getModifier().write(1, ReflectionUtils.getRemoveBossBarPacket());
return packet;
}
}
@@ -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,49 @@ 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 if (CNConfig.advancedChatChannel) {
this.chatProvider = new AdvancedChatProvider(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 +268,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 +329,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 +344,43 @@ 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()
.leaveRule((p, e) -> true)
.comeRule((p, e) -> true)
StaticTextEntity entity = tagEntity.addTag(StaticTextTagSetting.builder()
.leaveRule((receiver, owner) -> true)
.comeRule((receiver, owner) -> {
if ((owner instanceof Player sender) && chatProvider.isIgnoring(sender, receiver)) {
return false;
}
switch (channelMode) {
case ALL -> {
return true;
}
case JOINED -> {
return channel == null || chatProvider.hasJoinedChannel(receiver, channel);
}
case CAN_JOIN -> {
return channel == null || chatProvider.canJoinChannel(receiver, channel);
}
}
return false;
})
.verticalOffset(yOffset)
.defaultText(text)
.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);
}
@@ -0,0 +1,88 @@
/*
* 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.bubble.provider;
import net.advancedplugins.chat.api.AdvancedChannelChatEvent;
import net.advancedplugins.chat.api.AdvancedChatAPI;
import net.advancedplugins.chat.channel.ChatChannel;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import java.util.List;
public class AdvancedChatProvider extends AbstractChatProvider {
public AdvancedChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
}
@Override
public void register() {
Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get());
}
@Override
public void unregister() {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
ChatChannel chatChannel = AdvancedChatAPI.getApi().getPlayerChatChannel(player.getUniqueId());
if (chatChannel == null) {
return false;
}
return chatChannel.getSectionName().equals(channelID);
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
List<ChatChannel> channelList = AdvancedChatAPI.getApi().getAllowedChatChannels(player);
for (ChatChannel chatChannel : channelList) {
if (chatChannel.getSectionName().equals(channelID)) {
return true;
}
}
return false;
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return AdvancedChatAPI.getApi().getIgnoredPlayers(receiver).contains(sender.getName());
}
@EventHandler (ignoreCancelled = true)
public void onAdvancedChat(AdvancedChannelChatEvent event) {
String channel = event.getChannel().getSectionName();
for (String black : chatBubblesManager.getBlacklistChannels()) {
if (channel.equals(black)) return;
}
if (event.getSender() instanceof Player player) {
if (!player.isOnline())
return;
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(player, event.getMessage(), channel);
});
}
}
}
@@ -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,22 @@ 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;
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
// This event is not async sometimes
@EventHandler (ignoreCancelled = true)
public void onChat(AsyncPlayerChatEvent event) {
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(event.getPlayer(), event.getMessage()));
@@ -15,36 +15,42 @@
* 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;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
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 +73,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 +84,71 @@ 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();
ChatChannel currentChannel = selectedChannel != null ? selectedChannel : api.channelRegistry().defaultChannel();
Object key = getChannelKey(currentChannel);
String str = ReflectionUtils.getKeyAsString(key);
return str.equals(channelID);
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
ChannelRegistry registry = api.channelRegistry();
Object key = ReflectionUtils.getKerFromString(channelID);
if (key == null) {
return false;
}
ChatChannel channel = null;
try {
channel = (ChatChannel) getChannelByKeyMethod.invoke(registry, key);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
if (channel == null) {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
String perm = channel.permission();
if (perm == null) {
return true;
}
return player.hasPermission(perm);
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
CarbonPlayer sPlayer = carbonPlayer(sender.getUniqueId());
CarbonPlayer rPlayer = carbonPlayer(receiver.getUniqueId());
if (sPlayer == null || rPlayer == null) {
return false;
}
return rPlayer.ignoring(sPlayer.uuid());
}
@Nullable
private CarbonPlayer carbonPlayer(UUID uuid) {
CarbonPlayer carbonPlayer = null;
for (CarbonPlayer cPlayer : api.server().players()) {
if (cPlayer.uuid().equals(uuid)) {
carbonPlayer = cPlayer;
break;
}
}
return carbonPlayer;
}
private Object getChannelKey(ChatChannel channel) {
try {
return this.channelKeyMethod.invoke(channel);
@@ -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,35 @@ 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);
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@EventHandler (ignoreCancelled = true)
public void onHuskChat(ChatMessageEvent event) {
String channel = event.getChannelId();
@@ -52,7 +84,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,21 @@ 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;
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@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,32 @@ 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;
}
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@EventHandler (ignoreCancelled = true)
public void onTrChat(TrChatEvent event) {
if (!event.getForward()) return;
@@ -58,7 +86,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,30 @@ 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;
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@EventHandler (ignoreCancelled = true)
public void onVentureChat(VentureChatEvent event) {
String channelName = event.getChannel().getName();
@@ -53,7 +81,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());
@@ -22,7 +22,6 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.entity.Player;
import java.util.ArrayList;
@@ -47,7 +46,7 @@ public class PacketManager {
public void send(Player player, PacketContainer packet) {
if (!player.isOnline()) {
LogUtils.warn(player.getName() + " not online");
return;
}
this.plugin.debug("Packet sent: " + packet.getType() + " to " + player.getName());
this.protocolManager.sendServerPacket(player, packet);
@@ -22,6 +22,7 @@ import net.momirealms.customnameplates.api.manager.VersionManager;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -40,14 +41,11 @@ import java.util.concurrent.CompletableFuture;
*/
public class VersionManagerImpl implements VersionManager, Listener {
private final boolean isNewerThan1_19;
private final boolean isNewerThan1_19_R2;
private final boolean isNewerThan1_19_R3;
private final boolean isNewerThan1_20;
private final boolean isNewerThan1_20_R2;
private final String serverVersion;
private final float mcVersion;
private final CustomNameplatesPluginImpl plugin;
private boolean isFolia;
private boolean isMojmap;
private final String pluginVersion;
private boolean isLatest = true;
@@ -55,47 +53,52 @@ public class VersionManagerImpl implements VersionManager, Listener {
public VersionManagerImpl(CustomNameplatesPluginImpl plugin) {
this.plugin = plugin;
// Get the server version
serverVersion = plugin.getServer().getClass().getPackage().getName().split("\\.")[3];
String[] split = serverVersion.split("_");
int main_ver = Integer.parseInt(split[1]);
// Determine if the server version is newer than 1_19_R2 and 1_20_R1
if (main_ver >= 20) {
isNewerThan1_20_R2 = Integer.parseInt(split[2].substring(1)) >= 2;
isNewerThan1_19_R2 = isNewerThan1_19_R3 = true;
isNewerThan1_20 = true;
isNewerThan1_19 = true;
} else if (main_ver == 19) {
isNewerThan1_19_R2 = Integer.parseInt(split[2].substring(1)) >= 2;
isNewerThan1_19_R3 = Integer.parseInt(split[2].substring(1)) >= 3;
isNewerThan1_20 = isNewerThan1_20_R2 = false;
isNewerThan1_19 = true;
} else {
isNewerThan1_19 = isNewerThan1_19_R2= isNewerThan1_19_R3 = false;
isNewerThan1_20 = isNewerThan1_20_R2 = false;
}
// Check if the server is Folia
try {
Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler");
this.isFolia = true;
} catch (ClassNotFoundException ignored) {
}
String[] split = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\.");
this.mcVersion = Float.parseFloat(split[1] + "." + (split.length >= 3 ? split[2] : "0"));
// Get the plugin version
this.pluginVersion = plugin.getDescription().getVersion();
// Check if the server is Folia
try {
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
this.isFolia = true;
} catch (ClassNotFoundException ignored) {
this.isFolia = false;
}
// Check if the server is Mojmap
try {
Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket");
this.isMojmap = true;
} catch (ClassNotFoundException ignored) {
}
}
@Override
public boolean isMojmap() {
return isMojmap;
}
@Override
public boolean isVersionNewerThan1_19_R2() {
return isNewerThan1_19_R2;
return mcVersion >= 19.3;
}
@Override
public boolean isVersionNewerThan1_20() {
return isNewerThan1_20;
return mcVersion >= 20;
}
@Override
public boolean isVersionNewerThan1_20_R2() {
return isNewerThan1_20_R2;
return mcVersion >= 20.2;
}
@Override
public boolean isVersionNewerThan1_20_5() {
return mcVersion >= 20.5;
}
@NotNull
@@ -111,36 +114,42 @@ public class VersionManagerImpl implements VersionManager, Listener {
@Override
public boolean isVersionNewerThan1_19() {
return isNewerThan1_19;
return mcVersion >= 19;
}
@Override
public boolean isVersionNewerThan1_19_R3() {
return isNewerThan1_19_R3;
return mcVersion >= 19.4;
}
@Override
public int getPackFormat() {
switch (serverVersion) {
case "v1_20_R3" -> {
switch (Bukkit.getServer().getBukkitVersion().split("-")[0]) {
case "1.21" -> {
return 34;
}
case "1.20.5", "1.20.6" -> {
return 32;
}
case "1.20.3", "1.20.4" -> {
return 22;
}
case "v1_20_R2" -> {
case "1.20.2" -> {
return 18;
}
case "v1_20_R1" -> {
case "1.20", "1.20.1" -> {
return 15;
}
case "v1_19_R3" -> {
case "1.19.4" -> {
return 13;
}
case "v1_19_R2" -> {
case "1.19.3" -> {
return 12;
}
case "v1_19_R1" -> {
case "1.19", "1.19.1", "1.19.2" -> {
return 9;
}
case "v1_18_R1", "v1_18_R2" -> {
case "1.18", "1.18.1", "1.18.2" -> {
return 8;
}
default -> {
@@ -154,11 +163,6 @@ public class VersionManagerImpl implements VersionManager, Listener {
return isFolia;
}
@Override
public String getServerVersion() {
return serverVersion;
}
// Method to asynchronously check for plugin updates
public CompletableFuture<Boolean> checkUpdate() {
CompletableFuture<Boolean> updateFuture = new CompletableFuture<>();
@@ -238,7 +242,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,17 +312,22 @@ 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);
this.removeNameplatePlayerFromMap(player.getUniqueId());
// nameplate module part
if (CNConfig.nameplateModule) {
@@ -331,6 +351,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 +621,7 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
return tagMode;
}
@NotNull
@Nullable
@Override
public Nameplate getNameplate(@NotNull String key) {
return nameplateMap.get(key);
@@ -660,4 +691,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);
}
@@ -185,7 +213,7 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager {
}
public void handlePlayerQuit(Player quit) {
UnlimitedEntity unlimitedEntity = getUnlimitedObject(quit.getUniqueId());
UnlimitedEntity unlimitedEntity = removeUnlimitedEntityFromMap(quit.getUniqueId());
if (unlimitedEntity != null) {
unlimitedEntity.destroy();
}
@@ -90,8 +90,21 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
// save unicodes
this.saveLegacyUnicodes();
// generate shaders
this.generateShaders();
if (!plugin.getVersionManager().isVersionNewerThan1_20_5()) {
this.generateShaders("ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, false);
this.generateShaders("ResourcePack" + File.separator + "overlay_1_20_5" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
} else {
this.generateShaders("ResourcePack" + File.separator + "overlay_1_20_5" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
try {
FileUtils.copyDirectory(
new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_20_5"),
new File(plugin.getDataFolder(), "ResourcePack")
);
FileUtils.deleteDirectory(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_20_5"));
} catch (IOException e) {
e.printStackTrace();
}
}
// add offset characters
this.getOffsets(texturesFolder).forEach(providers::add);
// add nameplate characters
@@ -110,9 +123,8 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
this.copyResourcePackToHookedPlugins(resourcePackFolder);
}
private void generateShaders() {
private void generateShaders(String path, boolean v1_20_5) {
if (!CNConfig.enableShader) return;
String path = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator;
plugin.saveResource(path + "rendertype_text.fsh", true);
plugin.saveResource(path + "rendertype_text.json", true);
plugin.saveResource(path + "rendertype_text.vsh", true);
@@ -126,11 +138,13 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
} catch (IOException e) {
e.printStackTrace();
}
String mainShader = v1_20_5 ? ShaderConstants.Nameplates_Shader_1_20_5 : ShaderConstants.Nameplates_Shader_1_20_4;
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(shader1), StandardCharsets.UTF_8))) {
writer.write(sb1.toString()
.replace("%SHADER_0%", !CNConfig.animatedImage ? "" : ShaderConstants.Animated_Text_Out)
.replace("%SHADER_1%", !CNConfig.textEffects ? ShaderConstants.Nameplates_Shader : ShaderConstants.ItemsAdder_Text_Effects + ShaderConstants.Nameplates_Shader)
.replace("%SHADER_1%", !CNConfig.textEffects ? mainShader : ShaderConstants.ItemsAdder_Text_Effects + mainShader)
.replace("%SHADER_2%", !CNConfig.animatedImage ? "" : ShaderConstants.Animated_Text_VSH)
.replace("%SHADER_3%", !CNConfig.hideScoreboardNumber ? "" : ShaderConstants.Hide_ScoreBoard_Numbers)
);
@@ -495,29 +509,57 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
}
private void setPackFormat() {
plugin.saveResource("ResourcePack" + File.separator + "pack.mcmeta", false);
File format_file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta");
String line;
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(format_file), StandardCharsets.UTF_8))) {
while ((line = reader.readLine()) != null) {
sb.append(line).append(System.lineSeparator());
}
} catch (IOException e) {
e.printStackTrace();
}
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(new File(plugin.getDataFolder(),
"ResourcePack" + File.separator + "pack.mcmeta")), StandardCharsets.UTF_8))) {
writer.write(sb.toString().replace("%version%", String.valueOf(plugin.getVersionManager().getPackFormat())));
} catch (IOException e) {
e.printStackTrace();
if (plugin.getVersionManager().isVersionNewerThan1_20_5()) {
plugin.saveResource("ResourcePack" + File.separator + "pack_1_20_5.mcmeta", false);
File file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack_1_20_5.mcmeta");
file.renameTo(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta"));
} else {
plugin.saveResource("ResourcePack" + File.separator + "pack.mcmeta", false);
}
// File format_file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta");
// String line;
// StringBuilder sb = new StringBuilder();
// try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(format_file), StandardCharsets.UTF_8))) {
// while ((line = reader.readLine()) != null) {
// sb.append(line).append(System.lineSeparator());
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
// try (BufferedWriter writer = new BufferedWriter(
// new OutputStreamWriter(new FileOutputStream(new File(plugin.getDataFolder(),
// "ResourcePack" + File.separator + "pack.mcmeta")), StandardCharsets.UTF_8))) {
// writer.write(sb.toString().replace("%version%", String.valueOf(plugin.getVersionManager().getPackFormat())));
// } catch (IOException e) {
// e.printStackTrace();
// }
}
public static class ShaderConstants {
public static final String Nameplates_Shader =
public static final String Nameplates_Shader_1_20_5 =
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.y += 1;\n" +
" vertex.x += 1;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(254., 254., 254.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.z *= 1.001;\n" +
" vertex.x *= 1.001;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else if (Color.xyz == vec3(253., 254., 254.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.z *= 1.001001;\n" +
" vertex.x *= 1.001001;\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" } else {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
" }";
public static final String Nameplates_Shader_1_20_4 =
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.y += 1;\n" +
@@ -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()
);
@@ -17,6 +17,7 @@
package net.momirealms.customnameplates.paper.mechanic.placeholder;
import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.PlaceholderManager;
@@ -124,6 +125,11 @@ public class PluginPlaceholders extends PlaceholderExpansion {
if (switchText == null) return "Switch text not exists: " + mainArg;
return switchText.getValue(offlinePlayer);
}
// fix MagicAvatar's ugly colors
case "magicavatar" -> {
String parsed = PlaceholderAPI.setPlaceholders(offlinePlayer, "%magicavatar_player%");
return parsed.replace("§r", "");
}
}
Player onlinePlayer = offlinePlayer.getPlayer();
@@ -18,7 +18,6 @@
package net.momirealms.customnameplates.paper.mechanic.requirement;
import me.clip.placeholderapi.PlaceholderAPI;
import net.momirealms.biomeapi.BiomeAPI;
import net.momirealms.customnameplates.api.manager.RequirementManager;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.requirement.RequirementExpansion;
@@ -27,11 +26,15 @@ import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.common.Pair;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import net.momirealms.customnameplates.paper.mechanic.requirement.papi.PapiCondition;
import net.momirealms.customnameplates.paper.setting.CNConfig;
import net.momirealms.customnameplates.paper.util.ClassUtils;
import net.momirealms.customnameplates.paper.util.ConfigUtils;
import net.momirealms.customnameplates.paper.util.DisguiseUtils;
import net.momirealms.customnameplates.paper.util.GeyserUtils;
import net.momirealms.sparrow.heart.SparrowHeart;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
@@ -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 = SparrowHeart.getInstance().getBiomeResourceLocation(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 = SparrowHeart.getInstance().getBiomeResourceLocation(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,8 +378,8 @@ public class RequirementManagerImpl implements RequirementManager {
return condition -> {
String currentWeather;
World world = Objects.requireNonNull(condition.getOfflinePlayer().getPlayer()).getWorld();
if (world.isThundering()) currentWeather = "thunder";
else if (world.isClearWeather()) currentWeather = "clear";
if (world.isClearWeather()) currentWeather = "clear";
else if (world.isThundering()) currentWeather = "thunder";
else currentWeather = "rain";
for (String weather : weathers)
if (weather.equalsIgnoreCase(currentWeather))
@@ -19,7 +19,6 @@ package net.momirealms.customnameplates.paper.mechanic.team;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
@@ -27,13 +26,14 @@ import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.TeamManager;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.common.message.MessageType;
import net.momirealms.customnameplates.common.team.TeamCollisionRule;
import net.momirealms.customnameplates.common.team.TeamColor;
import net.momirealms.customnameplates.common.team.TeamTagVisibility;
import net.momirealms.customnameplates.paper.mechanic.misc.PacketManager;
import net.momirealms.customnameplates.paper.mechanic.team.packet.*;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
import net.momirealms.customnameplates.paper.mechanic.team.provider.*;
import net.momirealms.customnameplates.paper.setting.CNConfig;
import net.momirealms.sparrow.heart.SparrowHeart;
import net.momirealms.sparrow.heart.feature.team.TeamCollisionRule;
import net.momirealms.sparrow.heart.feature.team.TeamColor;
import net.momirealms.sparrow.heart.feature.team.TeamVisibility;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
@@ -50,13 +50,11 @@ import java.util.Optional;
public class TeamManagerImpl implements TeamManager, PluginMessageListener {
private final CustomNameplatesPlugin plugin;
private final TeamPacketAdaptor teamPacketAdaptor;
private TeamProvider teamProvider;
private static final String CHANNEL = "customnameplates:cnp";
public TeamManagerImpl(CustomNameplatesPlugin plugin) {
this.plugin = plugin;
this.teamPacketAdaptor = new TeamPacket_1_17();
}
/**
@@ -78,35 +76,33 @@ public class TeamManagerImpl implements TeamManager, PluginMessageListener {
}
playerTeam.addPlayer(player);
} else {
PacketContainer createOwner = teamPacketAdaptor.getTeamCreatePacket(
TeamCreate.builder()
.teamName(team)
.color(TeamColor.WHITE)
.display("")
.prefix("")
.suffix("")
.members(Collections.singletonList(player.getName()))
.collisionRule(TeamCollisionRule.ALWAYS)
.tagVisibility(TeamTagVisibility.ALWAYS)
.build()
);
for (Player online : Bukkit.getOnlinePlayers()) {
PacketManager.getInstance().send(online, createOwner);
SparrowHeart.getInstance().addClientSideTeam(online, team,
Collections.singletonList(player.getName()),
"{\"text\":\"\"}",
"{\"text\":\"\"}",
"{\"text\":\"\"}",
TeamVisibility.ALWAYS,
TeamVisibility.ALWAYS,
TeamCollisionRule.ALWAYS,
TeamColor.WHITE,
false,
false
);
if (online == player) continue;
String onlineTeam = teamProvider.getTeam(online, null);
PacketContainer createOther = teamPacketAdaptor.getTeamCreatePacket(
TeamCreate.builder()
.teamName(onlineTeam)
.color(TeamColor.WHITE)
.display("")
.prefix("")
.suffix("")
.members(Collections.singletonList(online.getName()))
.collisionRule(TeamCollisionRule.ALWAYS)
.tagVisibility(TeamTagVisibility.ALWAYS)
.build()
SparrowHeart.getInstance().addClientSideTeam(player, onlineTeam,
Collections.singletonList(online.getName()),
"{\"text\":\"\"}",
"{\"text\":\"\"}",
"{\"text\":\"\"}",
TeamVisibility.ALWAYS,
TeamVisibility.ALWAYS,
TeamCollisionRule.ALWAYS,
TeamColor.WHITE,
false,
false
);
PacketManager.getInstance().send(player, createOther);
}
}
}
@@ -127,28 +123,23 @@ public class TeamManagerImpl implements TeamManager, PluginMessageListener {
Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
Optional.ofNullable(scoreboard.getTeam(team)).ifPresent(Team::unregister);
} else {
PacketContainer packet = teamPacketAdaptor.getTeamRemovePacket(
TeamRemove.builder()
.teamName(team)
.build()
);
for (Player online : Bukkit.getOnlinePlayers()) {
if (player == online) continue;
PacketManager.getInstance().send(online, packet);
SparrowHeart.getInstance().removeClientSideTeam(online, team);
}
}
}
@Override
public void updateTeam(Player owner, Player viewer, String prefix, String suffix, TeamColor color, TeamTagVisibility visibility) {
public void updateTeam(Player owner, Player viewer, String prefix, String suffix, net.momirealms.customnameplates.common.team.TeamColor color, TeamTagVisibility visibility) {
if (CNConfig.disableTeamManage) return;
String team = teamProvider.getTeam(owner, viewer);
if (team == null) {
LogUtils.warn("Failed to get player " + owner.getName() + "'s team for viewer " + viewer.getName());
return;
}
if (color == TeamColor.NONE || color == TeamColor.CUSTOM)
color = TeamColor.WHITE;
if (color == net.momirealms.customnameplates.common.team.TeamColor.NONE || color == net.momirealms.customnameplates.common.team.TeamColor.CUSTOM)
color = net.momirealms.customnameplates.common.team.TeamColor.WHITE;
if (plugin.getNameplateManager().isProxyMode()) {
this.sendPluginMessage(
MessageType.UPDATE,
@@ -160,18 +151,18 @@ public class TeamManagerImpl implements TeamManager, PluginMessageListener {
visibility.name()
);
} else {
PacketContainer packet = teamPacketAdaptor.getTeamUpdatePacket(
TeamUpdate.builder()
.teamName(team)
.color(color)
.display("")
.prefix(prefix)
.suffix(suffix)
.tagVisibility(visibility)
.collisionRule(TeamCollisionRule.ALWAYS)
.build()
SparrowHeart.getInstance().updateClientSideTeam(
viewer, team,
"{\"text\":\"\"}",
AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(prefix),
AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(suffix),
TeamVisibility.valueOf(visibility.name()),
TeamVisibility.ALWAYS,
TeamCollisionRule.ALWAYS,
TeamColor.valueOf(color.name()),
false,
false
);
PacketManager.getInstance().send(viewer, packet);
}
}
@@ -1,166 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.team.packet;
import net.kyori.adventure.text.Component;
import net.momirealms.customnameplates.common.team.TeamCollisionRule;
import net.momirealms.customnameplates.common.team.TeamColor;
import net.momirealms.customnameplates.common.team.TeamTagVisibility;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
import java.util.Collection;
public class TeamCreate {
// String i
private String teamName;
// Collection<String> j
private Collection<String> members;
/*
Optional<b> k
*/
// IChatBaseComponent a
private Object teamDisplay;
// IChatBaseComponent b
private Object teamPrefix;
// IChatBaseComponent c
private Object teamSuffix;
// String d
private TeamTagVisibility tagVisibility;
// String e
private TeamCollisionRule collisionRule;
// Enum f
private TeamColor teamColor;
private TeamCreate() {
}
public TeamCreate(
String teamName,
Collection<String> members,
Component teamDisplay,
Component teamPrefix,
Component teamSuffix,
TeamTagVisibility tagVisibility,
TeamCollisionRule collisionRule,
TeamColor teamColor
) {
this.teamName = teamName;
this.members = members;
this.teamDisplay = teamDisplay;
this.teamPrefix = teamPrefix;
this.teamSuffix = teamSuffix;
this.tagVisibility = tagVisibility;
this.collisionRule = collisionRule;
this.teamColor = teamColor;
}
public String getTeamName() {
return teamName;
}
public Collection<String> getMembers() {
return members;
}
public Object getTeamDisplay() {
return teamDisplay;
}
public Object getTeamPrefix() {
return teamPrefix;
}
public Object getTeamSuffix() {
return teamSuffix;
}
public TeamTagVisibility getTagVisibility() {
return tagVisibility;
}
public TeamCollisionRule getCollisionRule() {
return collisionRule;
}
public TeamColor getTeamColor() {
return teamColor;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final TeamCreate packet;
public Builder() {
this.packet = new TeamCreate();
}
public static Builder of() {
return new Builder();
}
public Builder teamName(String name) {
packet.teamName = name;
return this;
}
public Builder members(Collection<String> members) {
packet.members = members;
return this;
}
public Builder display(String display) {
packet.teamDisplay = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(display);
return this;
}
public Builder prefix(String prefix) {
packet.teamPrefix = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(prefix);
return this;
}
public Builder suffix(String suffix) {
packet.teamSuffix = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(suffix);
return this;
}
public Builder color(TeamColor color) {
packet.teamColor = color;
return this;
}
public Builder tagVisibility(TeamTagVisibility visibility) {
packet.tagVisibility = visibility;
return this;
}
public Builder collisionRule(TeamCollisionRule rule) {
packet.collisionRule = rule;
return this;
}
public TeamCreate build() {
return packet;
}
}
}
@@ -1,29 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.team.packet;
import com.comphenix.protocol.events.PacketContainer;
public interface TeamPacketAdaptor {
PacketContainer getTeamCreatePacket(TeamCreate teamCreate);
PacketContainer getTeamUpdatePacket(TeamUpdate teamUpdate);
PacketContainer getTeamRemovePacket(TeamRemove teamRemove);
}
@@ -1,81 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.team.packet;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.momirealms.customnameplates.common.team.TeamColor;
import java.util.Optional;
@SuppressWarnings("DuplicatedCode")
public class TeamPacket_1_17 implements TeamPacketAdaptor {
@Override
public PacketContainer getTeamCreatePacket(TeamCreate teamCreate) {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM);
// 0 = create team
packet.getModifier().write(0,0);
packet.getModifier().write(1, teamCreate.getTeamName());
packet.getModifier().write(2, teamCreate.getMembers());
Optional<InternalStructure> optionalInternalStructure = packet.getOptionalStructures().read(0);
if (optionalInternalStructure.isPresent()) {
InternalStructure is = optionalInternalStructure.get();
// Team
is.getModifier().write(0, teamCreate.getTeamDisplay());
is.getModifier().write(1, teamCreate.getTeamPrefix());
is.getModifier().write(2, teamCreate.getTeamSuffix());
is.getModifier().write(3, teamCreate.getTagVisibility().getId());
is.getModifier().write(4, teamCreate.getCollisionRule().getId());
is.getEnumModifier(TeamColor.class, MinecraftReflection.getMinecraftClass("EnumChatFormat")).write(0, teamCreate.getTeamColor());
is.getModifier().write(6, teamCreate.getMembers().size());
}
return packet;
}
@Override
public PacketContainer getTeamRemovePacket(TeamRemove teamRemove) {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM);
// 1 = remove team
packet.getModifier().write(0,1);
packet.getStrings().write(0, teamRemove.getTeamName());
return packet;
}
@Override
public PacketContainer getTeamUpdatePacket(TeamUpdate teamUpdate) {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM);
// 2 = update team
packet.getModifier().write(0,2);
packet.getModifier().write(1, teamUpdate.getTeamName());
Optional<InternalStructure> optionalInternalStructure = packet.getOptionalStructures().read(0);
if (optionalInternalStructure.isPresent()) {
InternalStructure is = optionalInternalStructure.get();
// Team
is.getModifier().write(0, teamUpdate.getTeamDisplay());
is.getModifier().write(1, teamUpdate.getTeamPrefix());
is.getModifier().write(2, teamUpdate.getTeamSuffix());
is.getModifier().write(3, teamUpdate.getTagVisibility().getId());
is.getModifier().write(4, teamUpdate.getCollisionRule().getId());
is.getEnumModifier(TeamColor.class, MinecraftReflection.getMinecraftClass("EnumChatFormat")).write(0, teamUpdate.getTeamColor());
}
return packet;
}
}
@@ -1,56 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.team.packet;
public class TeamRemove {
private String teamName;
private TeamRemove() {
}
public TeamRemove(String teamName) {
this.teamName = teamName;
}
public String getTeamName() {
return teamName;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final TeamRemove packet;
public Builder() {
this.packet = new TeamRemove();
}
public Builder teamName(String teamName) {
packet.teamName = teamName;
return this;
}
public TeamRemove build() {
return packet;
}
}
}
@@ -1,149 +0,0 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.team.packet;
import net.momirealms.customnameplates.common.team.TeamCollisionRule;
import net.momirealms.customnameplates.common.team.TeamColor;
import net.momirealms.customnameplates.common.team.TeamTagVisibility;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
public class TeamUpdate {
private String teamName;
/*
Optional<b> k
*/
// IChatBaseComponent a
private Object teamDisplay;
// IChatBaseComponent b
private Object teamPrefix;
// IChatBaseComponent c
private Object teamSuffix;
// String d
private TeamTagVisibility tagVisibility;
// String e
private TeamCollisionRule collisionRule;
// Enum f
private TeamColor teamColor;
private TeamUpdate() {
}
public TeamUpdate(
String teamName,
Object teamDisplay,
Object teamPrefix,
Object teamSuffix,
TeamTagVisibility tagVisibility,
TeamCollisionRule collisionRule,
TeamColor teamColor
) {
this.teamName = teamName;
this.teamDisplay = teamDisplay;
this.teamPrefix = teamPrefix;
this.teamSuffix = teamSuffix;
this.tagVisibility = tagVisibility;
this.collisionRule = collisionRule;
this.teamColor = teamColor;
}
public String getTeamName() {
return teamName;
}
public Object getTeamDisplay() {
return teamDisplay;
}
public Object getTeamPrefix() {
return teamPrefix;
}
public Object getTeamSuffix() {
return teamSuffix;
}
public TeamTagVisibility getTagVisibility() {
return tagVisibility;
}
public TeamCollisionRule getCollisionRule() {
return collisionRule;
}
public TeamColor getTeamColor() {
return teamColor;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final TeamUpdate packet;
public Builder() {
this.packet = new TeamUpdate();
}
public static Builder of() {
return new Builder();
}
public Builder teamName(String name) {
packet.teamName = name;
return this;
}
public Builder display(String display) {
packet.teamDisplay = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(display);
return this;
}
public Builder prefix(String prefix) {
packet.teamPrefix = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(prefix);
return this;
}
public Builder suffix(String suffix) {
packet.teamSuffix = AdventureManagerImpl.getInstance().getIChatComponentFromMiniMessage(suffix);;
return this;
}
public Builder color(TeamColor color) {
packet.teamColor = color;
return this;
}
public Builder tagVisibility(TeamTagVisibility visibility) {
packet.tagVisibility = visibility;
return this;
}
public Builder collisionRule(TeamCollisionRule rule) {
packet.collisionRule = rule;
return this;
}
public TeamUpdate build() {
return packet;
}
}
}
@@ -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;
@@ -38,7 +39,7 @@ import java.util.Objects;
public class CNConfig {
public static String configVersion = "26";
public static String configVersion = "27";
public static int cacheSize;
public static int corePoolSize;
public static long keepAliveTime;
@@ -85,6 +86,8 @@ public class CNConfig {
public static boolean enableShader;
public static boolean huskChatChannel;
public static boolean carbonChatChannel;
public static boolean advancedChatChannel;
public static boolean hasLibsDisguise;
public static void load() {
try {
@@ -134,6 +137,7 @@ public class CNConfig {
ventureChatChannel = integrationSection.getBoolean("chat.VentureChat", false);
huskChatChannel = integrationSection.getBoolean("chat.HuskChat", false);
carbonChatChannel = integrationSection.getBoolean("chat.CarbonChat", false);
advancedChatChannel = integrationSection.getBoolean("chat.AdvancedChat", false);
tabTeam = integrationSection.getBoolean("team.TAB", false);
cmiTeam = integrationSection.getBoolean("team.CMI", false);
velocitab = integrationSection.getBoolean("team.Velocitab", false);
@@ -176,6 +180,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() {
@@ -30,7 +30,7 @@ import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.io.InputStream;
public class CNLocale {
@@ -60,23 +60,27 @@ public class CNLocale {
public static String MSG_HAVE_NO_BUBBLE;
public static void load() {
try {
YamlDocument.create(
new File(CustomNameplatesPlugin.getInstance().getDataFolder(), "messages/" + CNConfig.language + ".yml"),
Objects.requireNonNull(CustomNameplatesPlugin.getInstance().getResource("messages/" + CNConfig.language + ".yml")),
GeneralSettings.DEFAULT,
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.DEFAULT,
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.build()
);
} catch (IOException e) {
LogUtils.warn(e.getMessage());
InputStream inputStream = CustomNameplatesPlugin.getInstance().getResource("messages/" + CNConfig.language + ".yml");
if (inputStream != null) {
try {
YamlDocument.create(
new File(CustomNameplatesPlugin.getInstance().getDataFolder(), "messages/" + CNConfig.language + ".yml"),
inputStream,
GeneralSettings.DEFAULT,
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.DEFAULT,
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.build()
);
inputStream.close();
} catch (IOException e) {
LogUtils.warn(e.getMessage());
}
}
loadSettings(CustomNameplatesPlugin.get().getConfig("messages/" + CNConfig.language + ".yml"));
}
@@ -28,6 +28,7 @@ import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent;
import net.momirealms.customnameplates.api.manager.StorageManager;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import net.momirealms.customnameplates.paper.storage.method.NoneStorage;
import net.momirealms.customnameplates.paper.storage.method.database.nosql.MongoDBImpl;
import net.momirealms.customnameplates.paper.storage.method.database.nosql.RedisManager;
import net.momirealms.customnameplates.paper.storage.method.database.sql.H2Impl;
@@ -84,8 +85,10 @@ public class StorageManagerImpl implements Listener, StorageManager {
if (player == null || !player.isOnline()) return;
var onlineUser = new OnlineUser(player, playerData);
this.putOnlineUserInMap(onlineUser);
NameplateDataLoadEvent syncEvent = new NameplateDataLoadEvent(uuid, onlineUser);
plugin.getServer().getPluginManager().callEvent(syncEvent);
plugin.getScheduler().runTaskAsync(() -> {
NameplateDataLoadEvent syncEvent = new NameplateDataLoadEvent(uuid, onlineUser);
plugin.getServer().getPluginManager().callEvent(syncEvent);
});
}
});
}
@@ -178,6 +181,7 @@ public class StorageManagerImpl implements Listener, StorageManager {
case MySQL -> this.dataSource = new MySQLImpl(plugin);
case MariaDB -> this.dataSource = new MariaDBImpl(plugin);
case MongoDB -> this.dataSource = new MongoDBImpl(plugin);
case None -> this.dataSource = new NoneStorage(plugin);
}
if (this.dataSource != null) this.dataSource.initialize();
else LogUtils.severe("No storage type is set.");
@@ -0,0 +1,37 @@
package net.momirealms.customnameplates.paper.storage.method;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.data.PlayerData;
import net.momirealms.customnameplates.api.data.StorageType;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class NoneStorage extends AbstractStorage {
public NoneStorage(CustomNameplatesPlugin plugin) {
super(plugin);
}
@Override
public StorageType getStorageType() {
return StorageType.None;
}
@Override
public CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid) {
return CompletableFuture.completedFuture(Optional.of(PlayerData.empty()));
}
@Override
public CompletableFuture<Boolean> updatePlayerData(UUID uuid, PlayerData playerData) {
return CompletableFuture.completedFuture(true);
}
@Override
public Set<UUID> getUniqueUsers(boolean legacy) {
return Set.of();
}
}
@@ -67,9 +67,10 @@ public class MongoDBImpl extends AbstractStorage {
collectionPrefix = section.getString("collection-prefix", "nameplates");
var settings = MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD);
if (!section.getString("connection-uri", "").equals("")) {
if (!section.getString("connection-uri", "").isEmpty()) {
settings.applyConnectionString(new ConnectionString(section.getString("connection-uri", "")));
mongoClient = MongoClients.create(settings.build());
this.mongoClient = MongoClients.create(settings.build());
this.database = mongoClient.getDatabase(section.getString("database", "minecraft"));
return;
}
@@ -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);
}
}
}
@@ -21,14 +21,20 @@ import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.data.PlayerData;
import net.momirealms.customnameplates.api.data.StorageType;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import net.momirealms.customnameplates.paper.libraries.dependencies.Dependency;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.sqlite.SQLiteConfig;
import java.io.File;
import java.io.IOException;
import java.sql.*;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.EnumSet;
import java.util.Optional;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -39,6 +45,7 @@ public class SQLiteImpl extends AbstractSQLDatabase {
private Connection connection;
private File databaseFile;
private Constructor<?> connectionConstructor;
public SQLiteImpl(CustomNameplatesPlugin plugin) {
super(plugin);
@@ -49,9 +56,17 @@ public class SQLiteImpl extends AbstractSQLDatabase {
*/
@Override
public void initialize() {
ClassLoader classLoader = ((CustomNameplatesPluginImpl) plugin).getDependencyManager().obtainClassLoaderWith(EnumSet.of(Dependency.SQLITE_DRIVER, Dependency.SLF4J_SIMPLE, Dependency.SLF4J_API));
try {
Class<?> connectionClass = classLoader.loadClass("org.sqlite.jdbc4.JDBC4Connection");
connectionConstructor = connectionClass.getConstructor(String.class, String.class, Properties.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
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();
}
@@ -143,34 +158,21 @@ public class SQLiteImpl extends AbstractSQLDatabase {
*/
@Override
public Connection getConnection() throws SQLException {
if (connection == null || connection.isClosed()) {
setConnection();
if (connection != null && !connection.isClosed()) {
return connection;
}
return connection;
}
/**
* Set up the connection to the SQLite database.
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
private void setConnection() {
try {
if (!databaseFile.exists()) databaseFile.createNewFile();
Class.forName("org.sqlite.JDBC");
SQLiteConfig config = new SQLiteConfig();
config.enforceForeignKeys(true);
config.setEncoding(SQLiteConfig.Encoding.UTF8);
config.setSynchronous(SQLiteConfig.SynchronousMode.FULL);
connection = DriverManager.getConnection(
String.format("jdbc:sqlite:%s", databaseFile.getAbsolutePath()),
config.toProperties()
);
} catch (IOException e) {
LogUtils.warn("Failed to create the SQLite database.");
} catch (SQLException e) {
LogUtils.warn("Failed to initialize SQLite database.");
} catch (ClassNotFoundException e) {
LogUtils.warn("Failed to find SQLite driver.");
var properties = new Properties();
properties.setProperty("foreign_keys", Boolean.toString(true));
properties.setProperty("encoding", "'UTF-8'");
properties.setProperty("synchronous", "FULL");
connection = (Connection) this.connectionConstructor.newInstance("jdbc:sqlite:" + databaseFile.toString(), databaseFile.toString(), properties);
return connection;
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof SQLException) {
throw (SQLException) e.getCause();
}
throw new RuntimeException(e);
}
}
}
@@ -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();
}
}
@@ -19,6 +19,7 @@ package net.momirealms.customnameplates.paper.util;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataValue;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.google.common.collect.Lists;
@@ -55,7 +56,7 @@ public class FakeEntityUtils {
WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher();
WrappedDataWatcher.Serializer serializer1 = WrappedDataWatcher.Registry.get(Boolean.class);
WrappedDataWatcher.Serializer serializer2 = WrappedDataWatcher.Registry.get(Byte.class);
wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(2, WrappedDataWatcher.Registry.getChatComponentSerializer(true)), Optional.of(AdventureManagerImpl.getInstance().getWrappedChatComponentFromMiniMessage(miniMessage).getHandle()));
wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(2, WrappedDataWatcher.Registry.getChatComponentSerializer(true)), Optional.of(WrappedChatComponent.fromJson(AdventureManagerImpl.getInstance().getJsonComponentFromMiniMessage(miniMessage)).getHandle()));
wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, serializer1), true);
byte flag = 0x20;
if (sneak) flag += (byte) 0x02;
@@ -18,6 +18,7 @@
package net.momirealms.customnameplates.paper.util;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.util.LogUtils;
import java.lang.reflect.Constructor;
@@ -36,26 +37,49 @@ 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 {
Class<?> bar = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss");
Field remove = bar.getDeclaredField("f");
remove.setAccessible(true);
removeBossBarPacket = remove.get(null);
Class<?> packetBossClassF = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$f");
progressConstructor = packetBossClassF.getDeclaredConstructor(float.class);
progressConstructor.setAccessible(true);
Class<?> packetBossClassE = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$e");
updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass());
updateConstructor.setAccessible(true);
iChatComponentMethod = MinecraftReflection.getChatSerializerClass().getMethod("a", String.class);
iChatComponentMethod.setAccessible(true);
emptyComponent = iChatComponentMethod.invoke(null, "{\"text\":\"\"}");
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) {
LogUtils.severe("Error occurred when loading reflections", exception);
exception.printStackTrace();
if (CustomNameplatesPlugin.get().getVersionManager().isMojmap()) {
try {
Class<?> bar = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket");
Field remove = bar.getDeclaredField("REMOVE_OPERATION");
remove.setAccessible(true);
removeBossBarPacket = remove.get(null);
Class<?> packetBossClassF = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket$UpdateProgressOperation");
progressConstructor = packetBossClassF.getDeclaredConstructor(float.class);
progressConstructor.setAccessible(true);
Class<?> packetBossClassE = Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket$UpdateNameOperation");
updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass());
updateConstructor.setAccessible(true);
Class<?> craftChatMessageClass = Class.forName("org.bukkit.craftbukkit.util.CraftChatMessage");
iChatComponentMethod = craftChatMessageClass.getDeclaredMethod("fromJSON", String.class);
iChatComponentMethod.setAccessible(true);
emptyComponent = iChatComponentMethod.invoke(null, "{\"text\":\"\"}");
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) {
LogUtils.severe("Error occurred while loading reflections", exception);
}
} else {
try {
Class<?> bar = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss");
Field remove = bar.getDeclaredField("f");
remove.setAccessible(true);
removeBossBarPacket = remove.get(null);
Class<?> packetBossClassF = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$f");
progressConstructor = packetBossClassF.getDeclaredConstructor(float.class);
progressConstructor.setAccessible(true);
Class<?> packetBossClassE = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutBoss$e");
updateConstructor = packetBossClassE.getDeclaredConstructor(MinecraftReflection.getIChatBaseComponentClass());
updateConstructor.setAccessible(true);
iChatComponentMethod = MinecraftReflection.getChatSerializerClass().getMethod("a", String.class);
iChatComponentMethod.setAccessible(true);
emptyComponent = iChatComponentMethod.invoke(null, "{\"text\":\"\"}");
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) {
LogUtils.severe("Error occurred while loading reflections", exception);
}
}
try {
Class<?> componentClass = Class.forName("net;kyori;adventure;text;Component".replace(";", "."));
@@ -63,8 +87,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 +116,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 +133,15 @@ public class ReflectionUtils {
return "";
}
public static Object getKerFromString(String key) {
try {
return keyFromStringMethod.invoke(null, key);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static String getMiniMessageTextFromNonShadedComponent(Object component) {
try {
return (String) serializeComponentMethod.invoke(miniMessageInstance, component);
@@ -0,0 +1,31 @@
#version 150
#moj_import<fog.glsl>
uniform sampler2D Sampler0;
uniform vec4 ColorModulator;
uniform float FogStart,FogEnd;
uniform vec4 FogColor;
uniform float GameTime;
in float vertexDistance;
in vec4 vertexColor;
in vec2 texCoord0;
in float depthLevel;
%SHADER_0%
out vec4 fragColor;
void main() {
vec4 texColor = texture(Sampler0, texCoord0);
vec4 color = texColor * vertexColor * ColorModulator;%SHADER_1%
if (color.a < 0.1) {
discard;
}
if (texColor.a == 254.0/255.0) {
if (depthLevel == 1000.00) {
discard;
} else {
color = vec4(texColor.rgb, 1.0) * vertexColor * ColorModulator;
}
}
fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
}
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff. Show more