70 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
XiaoMoMi 22a440ae23 geyser 2024-02-03 03:43:30 +08:00
XiaoMoMi c279c9a74d Update build.gradle.kts 2024-02-03 03:27:35 +08:00
XiaoMoMi a6b11d1fe7 2.3.2 2024-02-03 03:25:34 +08:00
XiaoMoMi 180b34f4c8 2.3.1 2024-02-02 03:08:10 +08:00
XiaoMoMi c69735ea2b 2.3.0.7 2024-02-02 02:19:17 +08:00
XiaoMoMi f7628c2db9 2.3.0.6 2024-01-29 00:14:47 +08:00
XiaoMoMi 4c0df8288b small fix 2024-01-28 01:01:01 +08:00
XiaoMoMi bd0abb3463 2.3.0.5 2024-01-28 00:29:37 +08:00
XiaoMoMi 65b70a639b 2.3.0.4 2024-01-27 18:58:45 +08:00
XiaoMoMi 8ff48ad395 2.3.0.3 2024-01-26 23:56:49 +08:00
XiaoMoMi dc149dbb05 2.3.0.2 2024-01-26 16:35:25 +08:00
XiaoMoMi f70918befd increase default height 2024-01-25 05:50:42 +08:00
XiaoMoMi 53f9a9a85d Update README.md 2024-01-25 05:29:22 +08:00
160 changed files with 4868 additions and 1713 deletions

No files matched your search

+72 -13
View File
@@ -1,21 +1,80 @@
# This project is currently under recoding
# Custom-Nameplates
### Custom-Nameplates
A unique way to customize nametags
![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/customnameplates" alt="GitBook">
<img src="https://img.shields.io/badge/docs-gitbook-brightgreen" alt="Gitbook"/>
</a>
### How to support the developer
CustomNameplates is a Paper plugin that provides unlimited customization for nametags, bossbars, actionbars and chat bubbles.
https://afdian.net/@xiaomomi
## How to build
https://polymart.org/resource/customnameplates.2543
### Windows
### How to compile
Open the project with your IDE and execute gradle build. Finally you will get the jar at /project/build/libs/CustomNameplates-xxx-all.jar.
### I don't know how to build with IDE
#### Command Line
Install JDK 17 and set the JDK installation path to JAVA_HOME as an environment variable.\
Start powershell and change directory to the project folder.\
Execute ".\gradlew build" and get the jar at /project/build/libs/CustomNameplates-xxx-all.jar.
Execute ".\gradlew build" and get the jar at /target/CustomNameplates-universe-version.jar.
### API
Plugin is scheduled to be recoded on December. At that time API would be available. For the moment you can use the jar as local library.
#### IDE
Import the project and execute gradle build action.
##### About Proxy
If you are using a proxy, configurate the proxy in gradle.properties. Otherwise comment the lines in gradle.properties.
## Support the developer
Polymart: https://polymart.org/resource/customnameplates.2543 \
Afdian: https://afdian.net/@xiaomomi
## Use CustomNameplates API
### Maven
```
<repositories>
<repository>
<id>jitpack</id>
<url>https://jitpack.io/</url>
</repository>
</repositories>
```
```
<dependencies>
<dependency>
<groupId>com.github.Xiao-MoMi</groupId>
<artifactId>Custom-Nameplates</artifactId>
<version>{LATEST}</version>
<scope>provided</scope>
</dependency>
</dependencies>
```
### Gradle (Groovy)
```
repositories {
maven { url 'https://jitpack.io' }
}
```
```
dependencies {
compileOnly 'com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}'
}
```
### Gradle (Kotlin)
```
repositories {
maven("https://jitpack.io/")
}
```
```
dependencies {
compileOnly("com.github.Xiao-MoMi:Custom-Nameplates:{LATEST}")
}
```
+8 -1
View File
@@ -1,5 +1,12 @@
dependencies {
implementation(project(":common"))
compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.5")
implementation(project(":common"))
implementation("net.kyori:adventure-api:4.16.0")
}
tasks {
shadowJar {
relocate ("net.kyori", "net.momirealms.customnameplates.libraries")
}
}
@@ -41,6 +41,13 @@ public interface DataStorageInterface {
*/
StorageType getStorageType();
/**
* Get a player's data by uuid
* This player can be an offline one
*
* @param uuid uuid
* @return player data
*/
CompletableFuture<Optional<PlayerData>> getPlayerData(UUID uuid);
CompletableFuture<Boolean> updatePlayerData(UUID uuid, PlayerData playerData);
@@ -82,6 +82,10 @@ public class OnlineUser {
return CustomNameplatesPlugin.get().getNameplateManager().getNameplate(temp);
}
/**
* This value might be inconsistent with the key get by "getBubbleKey()"
* Because if a player doesn't have a bubble, his bubble would be the default one
*/
@Nullable
public Bubble getBubble() {
String temp = nameplate;
@@ -26,5 +26,6 @@ public enum StorageType {
MySQL,
MariaDB,
MongoDB,
Redis
Redis,
None
}
@@ -23,6 +23,9 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* An event triggered when player sends a message
*/
public class BubblesSpawnEvent extends PlayerEvent implements Cancellable {
private boolean cancelled;
@@ -57,18 +60,34 @@ public class BubblesSpawnEvent extends PlayerEvent implements Cancellable {
return getHandlerList();
}
/**
* Get the bubble's key
*/
public String getBubble() {
return bubble;
}
/**
* Set the bubble
*
* @param bubble bubble's key
*/
public void setBubble(String bubble) {
this.bubble = bubble;
}
/**
* Get the bubble text content
*/
public String getText() {
return text;
}
/**
* Set the bubble text content
*
* @param text text
*/
public void setText(String text) {
this.text = text;
}
@@ -22,6 +22,9 @@ import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* Plugin reload event
*/
public class CustomNameplatesReloadEvent extends Event {
private static final HandlerList handlerList = new HandlerList();
@@ -24,6 +24,9 @@ import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Player data loaded event
*/
public class NameplateDataLoadEvent extends Event {
private static final HandlerList handlerList = new HandlerList();
@@ -36,10 +39,20 @@ public class NameplateDataLoadEvent extends Event {
this.onlineUser = onlineUser;
}
/**
* Get the UUID
*
* @return uuid
*/
public UUID getUUID() {
return uuid;
}
/**
* Get the online user
*
* @return online user
*/
public OnlineUser getOnlineUser() {
return onlineUser;
}
@@ -149,6 +149,12 @@ public interface AdventureManager {
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean isColorCode(char c);
/**
* Convert a color into decimal
*
* @param color color
* @return decimal
*/
int colorToDecimal(ChatColor color);
/**
@@ -175,5 +181,11 @@ public interface AdventureManager {
*/
String getMiniMessageFormat(Component component);
/**
* Get IChatComponent from Json
*
* @param json json
* @return component
*/
Object getIChatComponent(String json);
}
@@ -34,6 +34,11 @@ public interface BackGroundManager {
@Nullable
BackGround getBackGround(@NotNull String key);
/**
* Get all the backgrounds
*
* @return backgrounds
*/
Collection<BackGround> getBackGrounds();
/**
@@ -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,29 +27,127 @@ import java.util.List;
public interface BubbleManager {
/**
* Set a custom chat provider
*
* @param provider provider
* @return success or not
*/
boolean setCustomChatProvider(AbstractChatProvider provider);
/**
* Remove a custom chat provider
*
* @return success or not
*/
boolean removeCustomChatProvider();
/**
* Register a bubble into map
*
* @param key key
* @param bubble bubble
* @return success or not
*/
boolean registerBubble(String key, Bubble bubble);
/**
* Unregister a bubble from map
*
* @param key key
* @return success or not
*/
boolean unregisterBubble(String key);
/**
* Get a bubble from map
*
* @param bubble key
* @return bubble
*/
@Nullable Bubble getBubble(String bubble);
/**
* If a player has a certain bubble
*
* @param player player
* @param bubble key
* @return
*/
boolean hasBubble(Player player, String bubble);
/**
* Get a list of the bubbles that a player has
*
* @param player player
* @return bubbles' keys
*/
List<String> getAvailableBubbles(Player player);
/**
* Get a list of the bubbles' display names that a player has
*
* @param player player
* @return bubbles' display names
*/
List<String> getAvailableBubblesDisplayNames(Player player);
/**
* Get blacklist chat channels
*
* @return channels
*/
String[] getBlacklistChannels();
/**
* Get all the bubbles
*
* @return bubbles
*/
Collection<Bubble> getBubbles();
/**
* Whether a bubble exists
*/
boolean containsBubble(String key);
/**
* Equip a bubble for a player
*
* @param player player
* @param bubble bubble
* @return success or not
*/
boolean equipBubble(Player player, String bubble);
/**
* Unequip a bubble for a player
*
* @param player player
*/
void unEquipBubble(Player player);
/**
* Get the default bubble key
*
* @return bubble key
*/
String getDefaultBubble();
void onChat(Player player, String text, String channel);
/**
* Get all the bubbles' keys
*
* @return keys
*/
Collection<String> getBubbleKeys();
/**
* Trigger chat
*
* @param player player
* @param text text
*/
void onChat(Player player, String text);
}
@@ -34,6 +34,11 @@ public interface ImageManager {
@Nullable
ConfiguredChar getImage(@NotNull String key);
/**
* Get all the images
*
* @return images
*/
Collection<ConfiguredChar> getImages();
/**
@@ -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
@@ -48,7 +52,7 @@ public interface NameplateManager {
* @param entityID entityID
* @param entity entity
*/
boolean putEntityIDToMap(int entityID, Entity entity);
boolean putEntityIDToMap(int entityID, @NotNull Entity entity);
/**
* Remove the entity from map
@@ -66,7 +70,7 @@ public interface NameplateManager {
* @param uuid player uuid
* @param nameplate cached nameplate
*/
boolean putCachedNameplateToMap(UUID uuid, CachedNameplate nameplate);
boolean putCachedNameplateToMap(@NotNull UUID uuid, @NotNull CachedNameplate nameplate);
/**
* Remove CachedNameplate from map
@@ -75,7 +79,7 @@ public interface NameplateManager {
* @return removed CachedNameplate
*/
@Nullable
CachedNameplate removeCachedNameplateFromMap(UUID uuid);
CachedNameplate removeCachedNameplateFromMap(@NotNull UUID uuid);
/**
* Get player by entityID from the cache
@@ -102,7 +106,7 @@ public interface NameplateManager {
* @param player player
* @return if the nameplate is updated
*/
boolean updateCachedNameplate(Player player);
boolean updateCachedNameplate(@NotNull Player player);
/**
* Update a player's cached nameplate
@@ -111,7 +115,7 @@ public interface NameplateManager {
* @param player player
* @return if the nameplate is updated
*/
boolean updateCachedNameplate(Player player, Nameplate nameplate);
boolean updateCachedNameplate(@NotNull Player player, @Nullable Nameplate nameplate);
/**
* This should not be null when player's data is loaded (async process)
@@ -120,61 +124,192 @@ public interface NameplateManager {
* @return cached nameplate
*/
@Nullable
CachedNameplate getCacheNameplate(Player player);
CachedNameplate getCacheNameplate(@NotNull Player player);
/**
* Create a name tag for a player, the tag type is decided by the mode in nameplate.yml
* If mode is DISABLE, this method would return null
* The tag would be put into map automatically and you can get it by getNameplatePlayer(uuid)
*
* @param player player
*/
void createNameTag(Player player);
@Nullable
NameplatePlayer createNameTag(@NotNull Player player);
void putNameplatePlayerToMap(NameplatePlayer player);
/**
* Put a nameplater player to map
*
* @param player player
*/
void putNameplatePlayerToMap(@NotNull NameplatePlayer player);
NameplatePlayer getNameplatePlayer(UUID uuid);
/**
* Get a nameplate player from map
*
* @param uuid uuid
* @return nameplate player
*/
@Nullable
NameplatePlayer getNameplatePlayer(@NotNull UUID uuid);
NameplatePlayer removeNameplatePlayerFromMap(UUID uuid);
/**
* Remove nameplate player from map
*
* @param uuid uuid
* @return removed nameplate player
*/
@Nullable
NameplatePlayer removeNameplatePlayerFromMap(@NotNull UUID uuid);
String getNameplatePrefix(Player player);
/**
* Get the nameplate's prefix with text tags
*
* @param player player
* @return prefix with text tags
*/
@NotNull
String getNameplatePrefix(@NotNull Player player);
String getNameplateSuffix(Player player);
/**
* Get the nameplate's suffix with text tags
*
* @param player player
* @return suffix with text tags
*/
@NotNull
String getNameplateSuffix(@NotNull Player player);
String getFullNameTag(Player player);
/**
* Get the full nameplate tag
*
* @param player player
* @return nameplate tag
*/
@NotNull
String getFullNameTag(@NotNull Player player);
boolean registerNameplate(String key, Nameplate nameplate);
/**
* Register a custom nameplate into map
*
* @param key key
* @param nameplate nameplate
* @return success or not
*/
boolean registerNameplate(@NotNull String key, @NotNull Nameplate nameplate);
boolean equipNameplate(Player player, String nameplateKey, boolean temp);
/**
* Unregister a nameplate from map
*
* @param key key
* @return success or not
*/
boolean unregisterNameplate(@NotNull String key);
/**
* Equip a nameplate for a player
*
* @param player player
* @param nameplateKey key
* @param temp whether save to storage
* @return success or not
*/
boolean equipNameplate(@NotNull Player player, @NotNull String nameplateKey, boolean temp);
/**
* Remove a nameplate for a player
*
* @param player player
* @param temp whether save to storage
*/
void unEquipNameplate(Player player, boolean temp);
boolean unregisterNameplate(String key);
/**
* Is team managed on proxy side
*/
boolean isProxyMode();
/**
* Get preview duration
*/
int getPreviewDuration();
/**
* Get the tag mode
*/
@NotNull
TagMode getTagMode();
Nameplate getNameplate(String key);
/**
* Get a nameplate by key
*
* @param key key
* @return nameplate
*/
@Nullable
Nameplate getNameplate(@NotNull String key);
/**
* Get all the nameplates
*
* @return nameplates
*/
@NotNull
Collection<Nameplate> getNameplates();
/**
* Get all the nameplates' keys
*
* @return keys
*/
@NotNull
Collection<String> getNameplateKeys();
boolean containsNameplate(String key);
/**
* Whether a nameplate key exists
*/
boolean containsNameplate(@NotNull String key);
List<String> getAvailableNameplates(Player player);
/**
* Get all the nameplates that the player has
*
* @param player player
* @return nameplates' keys
*/
@NotNull
List<String> getAvailableNameplates(@NotNull Player player);
/**
* If player has permission for a certain nameplate
*/
boolean hasNameplate(Player player, String nameplate);
boolean hasNameplate(@NotNull Player player, @NotNull String nameplate);
List<String> getAvailableNameplateDisplayNames(Player player);
/**
* Get all the nameplates' display names that the player has
*
* @param player player
* @return nameplates' display names
*/
@NotNull
List<String> getAvailableNameplateDisplayNames(@NotNull Player player);
TeamColor getTeamColor(Player player);
/**
* Get the nameplate's team color
*
* @param player player
* @return team color
*/
@NotNull
TeamColor getTeamColor(@NotNull Player player);
/**
* Get team tag manager
*/
@NotNull
TeamTagManager getTeamTagManager();
/**
* Get unlimited tag manager
*/
@NotNull
UnlimitedTagManager getUnlimitedTagManager();
}
@@ -44,6 +44,11 @@ public interface PlaceholderManager {
@Nullable
StaticText getStaticText(String key);
/**
* Get all the static texts
*
* @return static texts
*/
Collection<StaticText> getStaticTexts();
/**
@@ -55,6 +60,11 @@ public interface PlaceholderManager {
@Nullable
SwitchText getSwitchText(String key);
/**
* Get all the switch texts
*
* @return switch texts
*/
Collection<SwitchText> getSwitchTexts();
/**
@@ -66,6 +76,11 @@ public interface PlaceholderManager {
@Nullable
DescentText getDescentText(String key);
/**
* Get all the descent texts
*
* @return descent texts
*/
Collection<DescentText> getDescentTexts();
/**
@@ -77,6 +92,11 @@ public interface PlaceholderManager {
@Nullable
ConditionalText getConditionalText(String key);
/**
* Get all the conditional texts
*
* @return conditional texts
*/
Collection<ConditionalText> getConditionalTexts();
/**
@@ -88,6 +108,11 @@ public interface PlaceholderManager {
@Nullable
NameplateText getNameplateText(String key);
/**
* Get all the nameplate texts
*
* @return nameplate texts
*/
Collection<NameplateText> getNameplateTexts();
/**
@@ -99,9 +124,25 @@ public interface PlaceholderManager {
@Nullable
BackGroundText getBackGroundText(String key);
/**
* Get all the background texts
*
* @return background texts
*/
Collection<BackGroundText> getBackGroundTexts();
/**
* Get a vanilla hud
*
* @param key key
* @return vanilla hud
*/
VanillaHud getVanillaHud(String key);
/**
* Get all the vanilla huds
*
* @return vanilla huds
*/
Collection<VanillaHud> getVanillaHuds();
}
@@ -26,5 +26,10 @@ public interface ResourcePackManager {
*/
void generateResourcePack();
/**
* Delete a directory
*
* @param file file
*/
void deleteDirectory(File file);
}
@@ -46,7 +46,8 @@ public interface TeamManager {
* Get the team player in
*
* @param player player
* @param viewer viewer
* @return team name
*/
String getTeamName(Player player);
String getTeamName(Player player, Player viewer);
}
@@ -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);
/**
@@ -24,6 +24,14 @@ import org.jetbrains.annotations.NotNull;
public interface UnlimitedTagManager {
/**
* Create a named entity (ArmorStand) for an entity
* To apply the changes, you should add it to the EntityTagEntity
*
* @param entity entity
* @param setting setting
* @return named entity
*/
@NotNull StaticTextEntity createNamedEntity(EntityTagEntity entity, StaticTextTagSetting setting);
/**
@@ -37,9 +45,19 @@ public interface UnlimitedTagManager {
@NotNull
DynamicTextEntity createNamedEntity(EntityTagPlayer player, DynamicTextTagSetting setting);
/**
* Create or get a tag instance
*
* @param entity entity
* @return entity tag instance
*/
EntityTagEntity createOrGetTagForEntity(Entity entity);
/**
* Create or get a tag instance
*
* @param player player
* @return entity tag instance
*/
EntityTagPlayer createOrGetTagForPlayer(Player player);
EntityTagPlayer createOrGetTagForPlayer(Player player, boolean manageTeam);
}
@@ -17,18 +17,52 @@
package net.momirealms.customnameplates.api.manager;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletionStage;
public interface VersionManager {
/**
* Is folia scheduler implemented
*/
boolean isFolia();
String getServerVersion();
/**
* Check update
*
* @return return true if plugin needs update
*/
CompletionStage<Boolean> checkUpdate();
boolean isVersionNewerThan1_20_5();
/**
* Get plugin version
*
* @return version
*/
@NotNull
String getPluginVersion();
/**
* Is the plugin the latest version
*
* @return latest or not
*/
boolean isLatest();
/**
* Get pack format
*
* @return pack format
*/
int getPackFormat();
boolean isVersionNewerThan1_19();
boolean isMojmap();
boolean isVersionNewerThan1_19_R2();
boolean isVersionNewerThan1_19_R3();
@@ -36,11 +70,4 @@ public interface VersionManager {
boolean isVersionNewerThan1_20();
boolean isVersionNewerThan1_20_R2();
String getPluginVersion();
boolean isLatest();
int getPackFormat();
}
@@ -19,15 +19,43 @@ package net.momirealms.customnameplates.api.manager;
import net.momirealms.customnameplates.api.mechanic.font.FontData;
import net.momirealms.customnameplates.common.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface WidthManager {
boolean registerFontData(Key key, FontData fontData);
/**
* Register a font data into the plugin
* If there already exists one, it would fail
*
* @param key key
* @param fontData data
* @return success or not
*/
boolean registerFontData(@NotNull Key key, @NotNull FontData fontData);
boolean unregisterFontData(Key key);
/**
* Unregister a font data from map
*
* @param key key
* @return success or not
*/
boolean unregisterFontData(@NotNull Key key);
@Nullable FontData getFontData(Key key);
/**
* Get font data by key
*
* @param key key
* @return font data
*/
@Nullable
FontData getFontData(@NotNull Key key);
int getTextWidth(String textWithTags);
/**
* Get text's width (MiniMessage format text)
*
* @param textWithTags text
* @return width
*/
int getTextWidth(@NotNull String textWithTags);
}
@@ -15,9 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.team;
public interface Team {
package net.momirealms.customnameplates.api.mechanic.bubble;
public enum ChannelMode {
ALL,
JOINED,
CAN_JOIN
}
@@ -15,16 +15,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.paper.mechanic.bubble.listener;
package net.momirealms.customnameplates.api.mechanic.bubble.provider;
import net.momirealms.customnameplates.paper.mechanic.bubble.BubbleManagerImpl;
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 BubbleManagerImpl chatBubblesManager;
protected BubbleManager chatBubblesManager;
public AbstractChatListener(BubbleManagerImpl 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);
}
@@ -25,17 +25,32 @@ public class CharacterArranger {
currentChar = (char) (currentChar + '\u0001');
}
/**
* Get and increase the char arranged automatically by the plugin
*
* @return char
*/
public static char getAndIncrease() {
char temp = currentChar;
increase();
return temp;
}
/**
* Increase and get the char arranged automatically by the plugin
*
* @return char
*/
public static char increaseAndGet() {
increase();
return currentChar;
}
/**
* Reset the initial char
*
* @param c char
*/
public static void reset(char c) {
currentChar = c;
}
@@ -29,18 +29,40 @@ public class FontData {
this.defaultWidth = defaultWidth;
}
/**
* Register a char's width
*
* @param codePoint code point
* @param width width
*/
public void registerCharWidth(int codePoint, int width) {
widthData.put(codePoint, width);
}
/**
* Get a character's width
*
* @param codePoint code point
* @return width
*/
public int getWidth(int codePoint) {
return widthData.getOrDefault(codePoint, defaultWidth);
}
/**
* Get the data map
*
* @return map
*/
public HashMap<Integer, Integer> getWidthData() {
return widthData;
}
/**
* Override the data with another one
*
* @param fontData font data
*/
public void overrideWith(FontData fontData) {
if (fontData == null) return;
widthData.putAll(fontData.getWidthData());
@@ -66,6 +66,12 @@ public enum OffsetFont {
return this.height;
}
/**
* Get offset characters
*
* @param offset offset
* @return chars
*/
public static String getOffsetChars(int offset) {
if (offset >= 0) {
return getShortestPosChars(offset);
@@ -74,6 +80,12 @@ public enum OffsetFont {
}
}
/**
* get negative characters
*
* @param n n > 0
* @return chars
*/
public static String getShortestNegChars(int n) {
StringBuilder stringBuilder = new StringBuilder();
while (n >= 128) {
@@ -126,6 +138,12 @@ public enum OffsetFont {
return stringBuilder.toString();
}
/**
* get positive characters
*
* @param n n > 0
* @return chars
*/
public static String getShortestPosChars(int n) {
StringBuilder stringBuilder = new StringBuilder();
while (n >= 128) {
@@ -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);
}
}
);
}
}
@@ -18,7 +18,7 @@
package net.momirealms.customnameplates.api.mechanic.placeholder;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.entity.Player;
import org.bukkit.OfflinePlayer;
import java.util.HashMap;
@@ -41,9 +41,9 @@ public class SwitchText {
this.toParse = toParse;
}
public String getValue(Player player) {
public String getValue(OfflinePlayer player) {
String parsed = PlaceholderAPI.setPlaceholders(player, toParse);
return valueMap.getOrDefault(parsed, defaultValue);
return PlaceholderAPI.setPlaceholders(player, valueMap.getOrDefault(parsed, defaultValue));
}
public static class Builder {
@@ -18,12 +18,10 @@
package net.momirealms.customnameplates.api.mechanic.placeholder;
import me.clip.placeholderapi.PlaceholderAPI;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.mechanic.font.OffsetFont;
import net.momirealms.customnameplates.api.util.FontUtils;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
public class VanillaHud {
@@ -21,13 +21,37 @@ import net.momirealms.customnameplates.api.mechanic.misc.ViewerText;
import net.momirealms.customnameplates.api.mechanic.tag.NameplatePlayer;
public interface TeamTagPlayer extends NameplatePlayer {
/**
* Set a player's prefix
*
* @param prefix prefix
*/
void setPrefix(String prefix);
/**
* Set a player's suffix
*
* @param suffix suffix
*/
void setSuffix(String suffix);
/**
* Get a player's prefix
*
* @return prefix
*/
ViewerText getPrefix();
/**
* Get a player's suffix
*
* @return suffix
*/
ViewerText getSuffix();
/**
* Destroy the tag
*/
void destroy();
}
@@ -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,14 +35,12 @@ 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);
void setManageTeams(boolean manageTeams);
double getHatOffset();
void destroy();
@@ -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() {
@@ -21,7 +21,11 @@ import org.bukkit.OfflinePlayer;
public class Condition {
private final OfflinePlayer player;
private OfflinePlayer player;
public Condition() {
this.player = null;
}
public Condition(OfflinePlayer player) {
this.player = player;
@@ -30,4 +34,26 @@ public class Condition {
public OfflinePlayer getOfflinePlayer() {
return player;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final Condition condition;
public Builder() {
this.condition = new Condition();
}
public Builder player(OfflinePlayer player) {
condition.player = player;
return this;
}
public Condition build() {
return condition;
}
}
}
@@ -22,6 +22,13 @@ import org.bukkit.entity.Entity;
public class LocationUtils {
/**
* Get distance by two entities
*
* @param e1 entity
* @param e2 entity
* @return distance
*/
public static double getDistance(Entity e1, Entity e2) {
Location loc1 = e1.getLocation();
Location loc2 = e2.getLocation();
+6 -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.0.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/")
@@ -43,6 +43,8 @@ allprojects {
maven("https://repo.kryptonmc.org/releases")
maven("https://repo.oraxen.com/releases/")
maven("https://nexus.codecrafter47.de/content/repositories/public/")
maven("https://repo.opencollab.dev/main/")
maven("https://repo.md-5.net/content/groups/public/")
}
}
+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 {
@@ -30,7 +30,6 @@ public class CustomNameplatesBungeeCord extends Plugin implements Listener {
instance = this;
this.getProxy().registerChannel(CHANNEL);
this.getProxy().getPluginManager().registerListener(this, this);
this.getLogger().warning("It's not necessary to install the plugin on BungeeCord now!");
}
@Override
+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
+27 -18
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,44 +25,50 @@ 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")
compileOnly("io.th0rgal:oraxen:1.165.0")
compileOnly("com.github.FrancoBM12:API-MagicCosmetics:2.2.5")
compileOnly("commons-io:commons-io:2.15.1")
compileOnly("org.geysermc.geyser:api:2.2.0-SNAPSHOT")
compileOnly("LibsDisguises:LibsDisguises:10.0.42")
// chat channels
compileOnly(files("libs/VentureChat-3.7.1.jar"))
compileOnly(files("libs/TrChat-2.0.11.jar"))
compileOnly(files("libs/carbonchat-paper-3.0.0-beta.26.jar"))
compileOnly(files("libs/AdvancedChat-1.3.7.jar"))
compileOnly("net.william278:huskchat:2.7.1")
// api module
implementation(project(":api"))
implementation(project(":common"))
implementation("com.github.Xiao-MoMi:Sparrow-Heart:0.35")
// adventure
implementation("net.kyori:adventure-api:4.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.
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;
@@ -43,22 +46,47 @@ import net.momirealms.customnameplates.paper.setting.CNLocale;
import net.momirealms.customnameplates.paper.storage.StorageManagerImpl;
import net.momirealms.customnameplates.paper.util.Migration;
import net.momirealms.customnameplates.paper.util.ReflectionUtils;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import 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();
}
@@ -70,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);
@@ -93,6 +120,10 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
else this.getAdventure().sendConsoleMessage("[CustomNameplates] Update is available: <u>https://polymart.org/resource/2543<!u>");
});
this.getServer().getPluginManager().registerEvents((VersionManagerImpl) versionManager, this);
if (CNConfig.generatePackOnStart)
this.resourcePackManager.generateResourcePack();
if (CNConfig.metrics)
new Metrics(this, 16649);
}
@Override
@@ -100,7 +131,7 @@ public class CustomNameplatesPluginImpl extends CustomNameplatesPlugin implement
if (scheduler != null) ((SchedulerImpl) this.scheduler).shutdown();
if (actionBarManager != null) ((ActionBarManagerImpl) actionBarManager).unload();
if (nameplateManager != null) ((NameplateManagerImpl) this.nameplateManager).disable();
if (teamManager != null) ((TeamManagerImpl) this.teamManager).unload();
if (teamManager != null) ((TeamManagerImpl) this.teamManager).disable();
if (bossBarManager != null) ((BossBarManagerImpl) this.bossBarManager).unload();
if (imageManager != null) ((ImageManagerImpl) this.imageManager).unload();
if (backGroundManager != null) ((BackGroundManagerImpl) this.backGroundManager).unload();
@@ -150,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;
}
@@ -178,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\":\"\"}";
}
}
}
@@ -107,7 +107,7 @@ public class CommandManager {
public static CommandAPICommand getListCommand() {
return new CommandAPICommand("list")
.withPermission("bubbles.list")
.withPermission("bubbles.command.list")
.executesPlayer((player, args) -> {
if (!CNConfig.bubbleModule) return;
List<String> bubbles = CustomNameplatesPlugin.get().getBubbleManager().getAvailableBubblesDisplayNames(player);
@@ -116,7 +116,7 @@ public class CommandManager {
for (String availableBubble : bubbles) {
stringJoiner.add(availableBubble);
}
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_AVAILABLE_BUBBLE.replace("{Bubble}", stringJoiner.toString()));
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_AVAILABLE_BUBBLE.replace("{Bubble}", stringJoiner.toString()).replace("{Bubbles}", stringJoiner.toString()));
} else {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_HAVE_NO_BUBBLE);
}
@@ -125,15 +125,22 @@ public class CommandManager {
public static CommandAPICommand getEquipCommand() {
return new CommandAPICommand("equip")
.withPermission("bubbles.equip")
.withPermission("bubbles.command.equip")
.withArguments(new StringArgument("bubble").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getBubbleManager().getAvailableBubbles((Player) commandSenderSuggestionInfo.sender()).toArray(new String[0]))))
.executesPlayer((player, args) -> {
if (!CNConfig.bubbleModule) return;
String bubble = (String) args.get("bubble");
if (!CustomNameplatesPlugin.get().getBubbleManager().equipBubble(player, bubble)) {
if (!CustomNameplatesPlugin.get().getBubbleManager().containsBubble(bubble)) {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_BUBBLE_NOT_EXIST);
return;
}
if (!CustomNameplatesPlugin.get().getBubbleManager().hasBubble(player, bubble)) {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_BUBBLE_NOT_AVAILABLE);
return;
}
CustomNameplatesPlugin.get().getBubbleManager().equipBubble(player, bubble);
Bubble bubbleInstance = CustomNameplatesPlugin.get().getBubbleManager().getBubble(bubble);
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_EQUIP_BUBBLE.replace("{Bubble}", bubbleInstance.getDisplayName()));
});
@@ -141,7 +148,7 @@ public class CommandManager {
public static CommandAPICommand getUnEquipCommand() {
return new CommandAPICommand("unequip")
.withPermission("bubbles.unequip")
.withPermission("bubbles.command.unequip")
.executesPlayer((player, args) -> {
if (!CNConfig.bubbleModule) return;
CustomNameplatesPlugin.get().getBubbleManager().unEquipBubble(player);
@@ -218,7 +225,7 @@ public class CommandManager {
public static CommandAPICommand getPreviewCommand() {
return new CommandAPICommand("preview")
.withPermission("nameplates.preview")
.withPermission("nameplates.command.preview")
.executesPlayer((player, args) -> {
if (!CNConfig.nameplateModule) return;
NameplatePlayer nameplatePlayer = CustomNameplatesPlugin.get().getNameplateManager().getNameplatePlayer(player.getUniqueId());
@@ -290,15 +297,23 @@ public class CommandManager {
public static CommandAPICommand getEquipCommand() {
return new CommandAPICommand("equip")
.withPermission("nameplates.equip")
.withPermission("nameplates.command.equip")
.withArguments(new StringArgument("nameplate").replaceSuggestions(ArgumentSuggestions.strings(commandSenderSuggestionInfo -> CustomNameplatesPlugin.get().getNameplateManager().getAvailableNameplates((Player) commandSenderSuggestionInfo.sender()).toArray(new String[0]))))
.executesPlayer((player, args) -> {
if (!CNConfig.nameplateModule) return;
String nameplate = (String) args.get("nameplate");
if (!CustomNameplatesPlugin.get().getNameplateManager().equipNameplate(player, nameplate, false)) {
if (!CustomNameplatesPlugin.get().getNameplateManager().containsNameplate(nameplate)) {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_NAMEPLATE_NOT_EXISTS);
return;
}
if (!CustomNameplatesPlugin.get().getNameplateManager().hasNameplate(player, nameplate)) {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_NAMEPLATE_NOT_AVAILABLE);
return;
}
CustomNameplatesPlugin.get().getNameplateManager().equipNameplate(player, nameplate, false);
Nameplate nameplateInstance = CustomNameplatesPlugin.get().getNameplateManager().getNameplate(nameplate);
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_EQUIP_NAMEPLATE.replace("{Nameplate}", nameplateInstance.getDisplayName()));
});
@@ -306,7 +321,7 @@ public class CommandManager {
public static CommandAPICommand getUnEquipCommand() {
return new CommandAPICommand("unequip")
.withPermission("nameplates.unequip")
.withPermission("nameplates.command.unequip")
.executesPlayer((player, args) -> {
if (!CNConfig.nameplateModule) return;
CustomNameplatesPlugin.get().getNameplateManager().unEquipNameplate(player, false);
@@ -316,7 +331,7 @@ public class CommandManager {
public static CommandAPICommand getListCommand() {
return new CommandAPICommand("list")
.withPermission("nameplates.list")
.withPermission("nameplates.command.list")
.executesPlayer((player, args) -> {
if (!CNConfig.nameplateModule) return;
List<String> nameplates = CustomNameplatesPlugin.get().getNameplateManager().getAvailableNameplateDisplayNames(player);
@@ -325,7 +340,7 @@ public class CommandManager {
for (String availableNameplate : nameplates) {
stringJoiner.add(availableNameplate);
}
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_AVAILABLE_NAMEPLATE.replace("{Nameplates}", stringJoiner.toString()));
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_AVAILABLE_NAMEPLATE.replace("{Nameplates}", stringJoiner.toString()).replace("{Nameplate}", stringJoiner.toString()));
} else {
AdventureManagerImpl.getInstance().sendMessageWithPrefix(player, CNLocale.MSG_HAVE_NO_NAMEPLATE);
}
@@ -383,7 +398,7 @@ public class CommandManager {
public static CommandAPICommand getAboutCommand() {
return new CommandAPICommand("about")
.withPermission("customnameplates.about")
.withPermission("customnameplates.admin")
.executes((sender, args) -> {
AdventureManagerImpl.getInstance().sendMessage(sender, "<#3CB371>⚓ CustomNameplates <gray>- <#98FB98>" + CustomNameplatesPlugin.getInstance().getVersionManager().getPluginVersion());
AdventureManagerImpl.getInstance().sendMessage(sender, "<#7FFFAA>A plugin that provides adjustable images for texts");
@@ -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());
}
}
}
}
@@ -62,7 +62,7 @@ public class ActionBarReceiver {
}
case UPDATE -> {
if (controller.isShown()) {
controller.initialize();
controller.initialize(condition);
AdventureManagerImpl.getInstance().sendActionbar(player, controller.getLatestContent());
} else {
AdventureManagerImpl.getInstance().sendActionbar(player, "");
@@ -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;
}
}
@@ -98,7 +98,7 @@ public class BossBarReceiver {
if (this.bossBars[j].left().isShown())
this.bossBars[j].right().hide();
controller.initialize();
controller.initialize(condition);
pair.right().setMiniMessageText(pair.left().getLatestContent());
pair.right().show();
@@ -19,16 +19,22 @@ package net.momirealms.customnameplates.paper.mechanic.bubble;
import me.clip.placeholderapi.PlaceholderAPI;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.data.OnlineUser;
import net.momirealms.customnameplates.api.event.BubblesSpawnEvent;
import net.momirealms.customnameplates.api.event.NameplateDataLoadEvent;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.manager.RequirementManager;
import net.momirealms.customnameplates.api.mechanic.bubble.Bubble;
import net.momirealms.customnameplates.api.mechanic.bubble.ChannelMode;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.mechanic.character.CharacterArranger;
import net.momirealms.customnameplates.api.mechanic.character.ConfiguredChar;
import net.momirealms.customnameplates.api.mechanic.tag.unlimited.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;
@@ -36,29 +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.AbstractChatListener;
import net.momirealms.customnameplates.paper.mechanic.bubble.listener.AsyncChatListener;
import net.momirealms.customnameplates.paper.mechanic.bubble.listener.TrChatListener;
import net.momirealms.customnameplates.paper.mechanic.bubble.listener.VentureChatListener;
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;
@@ -75,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;
@@ -90,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) HandlerList.unregisterAll(chatListener);
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", "");
@@ -115,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() {
@@ -126,16 +153,49 @@ public class BubbleManagerImpl implements BubbleManager {
}
}
private void registerListener() {
PluginManager pluginManager = Bukkit.getPluginManager();
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.chatProvider = new HuskChatProvider(this);
} else if (CNConfig.carbonChatChannel) {
this.chatProvider = new CarbonChatProvider(this);
} else if (CNConfig.advancedChatChannel) {
this.chatProvider = new AdvancedChatProvider(this);
} else {
this.chatListener = new AsyncChatListener(this);
try {
Class.forName("io.papermc.paper.event.player.AsyncChatEvent");
this.chatProvider = new PaperAsyncChatProvider(this);
} catch (ClassNotFoundException e) {
this.chatProvider = new AsyncChatProvider(this);
}
}
pluginManager.registerEvents(chatListener, plugin);
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() {
@@ -208,11 +268,22 @@ 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;
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> onChat(player, finalText));
return;
}
var optionalUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId());
if (optionalUser.isEmpty()) {
@@ -258,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 {
@@ -273,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);
}
@@ -1,35 +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.bubble.listener;
import net.momirealms.customnameplates.paper.mechanic.bubble.BubbleManagerImpl;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.AsyncPlayerChatEvent;
public class AsyncChatListener extends AbstractChatListener {
public AsyncChatListener(BubbleManagerImpl chatBubblesManager) {
super(chatBubblesManager);
}
@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
if (event.isCancelled()) return;
chatBubblesManager.onChat(event.getPlayer(), event.getMessage());
}
}
@@ -1,43 +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.bubble.listener;
import me.arasple.mc.trchat.api.event.TrChatEvent;
import me.arasple.mc.trchat.module.display.channel.Channel;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.paper.mechanic.bubble.BubbleManagerImpl;
import org.bukkit.event.EventHandler;
public class TrChatListener extends AbstractChatListener {
public TrChatListener(BubbleManagerImpl chatBubblesManager) {
super(chatBubblesManager);
}
@EventHandler (ignoreCancelled = true)
public void onTrChat(TrChatEvent event) {
if (!event.getForward()) return;
Channel channelName = event.getChannel();
for (String channel : chatBubblesManager.getBlacklistChannels()) {
if (channelName.equals(channel)) return;
}
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(event.getSession().getPlayer(), event.getMessage());
});
}
}
@@ -1,46 +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.bubble.listener;
import mineverse.Aust1n46.chat.api.MineverseChatPlayer;
import mineverse.Aust1n46.chat.api.events.VentureChatEvent;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.paper.mechanic.bubble.BubbleManagerImpl;
import org.bukkit.event.EventHandler;
public class VentureChatListener extends AbstractChatListener {
public VentureChatListener(BubbleManagerImpl chatBubblesManager) {
super(chatBubblesManager);
}
@EventHandler
public void onVentureChat(VentureChatEvent event) {
String channelName = event.getChannel().getName();
for (String channel : chatBubblesManager.getBlacklistChannels()) {
if (channelName.equals(channel)) return;
}
final MineverseChatPlayer chatPlayer = event.getMineverseChatPlayer();
if (chatPlayer == null) {
return;
}
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(chatPlayer.getPlayer(), event.getChat());
});
}
}
@@ -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);
});
}
}
}
@@ -0,0 +1,65 @@
/*
* 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.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.AsyncPlayerChatEvent;
public class AsyncChatProvider extends AbstractChatProvider {
public AsyncChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
}
@Override
public void register() {
Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get());
}
@Override
public void unregister() {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
return true;
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
return true;
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
// This event is not async sometimes
@EventHandler (ignoreCancelled = true)
public void onChat(AsyncPlayerChatEvent event) {
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(event.getPlayer(), event.getMessage()));
}
}
@@ -0,0 +1,169 @@
/*
* 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.draycia.carbon.api.CarbonChat;
import net.draycia.carbon.api.channels.ChannelRegistry;
import net.draycia.carbon.api.channels.ChatChannel;
import net.draycia.carbon.api.event.CarbonEventSubscription;
import net.draycia.carbon.api.event.events.CarbonChatEvent;
import net.draycia.carbon.api.users.CarbonPlayer;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
public class CarbonChatProvider extends AbstractChatProvider {
private final CarbonChat api;
private CarbonEventSubscription<CarbonChatEvent> subscription;
private Method originalMessageMethod;
private Method channelKeyMethod;
private Method getChannelByKeyMethod;
public CarbonChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
this.api = net.draycia.carbon.api.CarbonChatProvider.carbonChat();
try {
this.originalMessageMethod = CarbonChatEvent.class.getMethod("originalMessage");
this.channelKeyMethod = ChatChannel.class.getMethod("key");
this.getChannelByKeyMethod = ChannelRegistry.class.getMethod("channel", ReflectionUtils.getKeyClass());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void register() {
subscription = api.eventHandler().subscribe(CarbonChatEvent.class, event -> {
if (event.cancelled())
return;
ChatChannel chatChannel = event.chatChannel();
Object key = getChannelKey(chatChannel);
if (key == null) return;
String channel = ReflectionUtils.getKeyAsString(key);
for (String black : chatBubblesManager.getBlacklistChannels()) {
if (channel.equals(black)) return;
}
Player player = Bukkit.getPlayer(event.sender().uuid());
if (player == null || !player.isOnline())
return;
Object component = getComponentFromEvent(event);
String message = ReflectionUtils.getMiniMessageTextFromNonShadedComponent(component);
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(player, message, channel));
});
}
@Override
public void unregister() {
if (subscription != null) {
subscription.dispose();
}
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
CarbonPlayer cPlayer = null;
for (CarbonPlayer carbonPlayer : api.server().players()) {
if (carbonPlayer.uuid().equals(player.getUniqueId())) {
cPlayer = carbonPlayer;
break;
}
}
if (cPlayer == null) {
return false;
}
ChatChannel selectedChannel = cPlayer.selectedChannel();
ChatChannel currentChannel = selectedChannel != null ? selectedChannel : api.channelRegistry().defaultChannel();
Object key = getChannelKey(currentChannel);
String str = ReflectionUtils.getKeyAsString(key);
return str.equals(channelID);
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
ChannelRegistry registry = api.channelRegistry();
Object key = ReflectionUtils.getKerFromString(channelID);
if (key == null) {
return false;
}
ChatChannel channel = null;
try {
channel = (ChatChannel) getChannelByKeyMethod.invoke(registry, key);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
if (channel == null) {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
String perm = channel.permission();
if (perm == null) {
return true;
}
return player.hasPermission(perm);
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
CarbonPlayer sPlayer = carbonPlayer(sender.getUniqueId());
CarbonPlayer rPlayer = carbonPlayer(receiver.getUniqueId());
if (sPlayer == null || rPlayer == null) {
return false;
}
return rPlayer.ignoring(sPlayer.uuid());
}
@Nullable
private CarbonPlayer carbonPlayer(UUID uuid) {
CarbonPlayer carbonPlayer = null;
for (CarbonPlayer cPlayer : api.server().players()) {
if (cPlayer.uuid().equals(uuid)) {
carbonPlayer = cPlayer;
break;
}
}
return carbonPlayer;
}
private Object getChannelKey(ChatChannel channel) {
try {
return this.channelKeyMethod.invoke(channel);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
private Object getComponentFromEvent(CarbonChatEvent event) {
try {
return this.originalMessageMethod.invoke(event);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return ReflectionUtils.getEmptyComponent();
}
}
@@ -0,0 +1,90 @@
/*
* 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.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.william278.huskchat.bukkit.BukkitHuskChat;
import net.william278.huskchat.bukkit.event.ChatMessageEvent;
import net.william278.huskchat.channel.Channel;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
public class HuskChatProvider extends AbstractChatProvider {
public HuskChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
}
@Override
public void register() {
Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get());
}
@Override
public void unregister() {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
String channel = BukkitHuskChat.getInstance().getPlayerCache().getPlayerChannel(player.getUniqueId());
if (channel == null) {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
return channel.equals(channelID);
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
Channel channel = BukkitHuskChat.getInstance().getSettings().getChannels().get(channelID);
if (channel == null) {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
String receivePerm = channel.getReceivePermission();
if (receivePerm == null) {
return true;
}
return player.hasPermission(receivePerm);
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@EventHandler (ignoreCancelled = true)
public void onHuskChat(ChatMessageEvent event) {
String channel = event.getChannelId();
for (String black : chatBubblesManager.getBlacklistChannels()) {
if (channel.equals(black)) return;
}
Player player = Bukkit.getPlayer(event.getSender().getUuid());
if (player == null || !player.isOnline())
return;
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(player, event.getMessage(), channel);
});
}
}
@@ -0,0 +1,86 @@
/*
* 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 io.papermc.paper.event.player.AsyncChatEvent;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.paper.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PaperAsyncChatProvider extends AbstractChatProvider {
private Method messageMethod;
public PaperAsyncChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
try {
this.messageMethod = AsyncChatEvent.class.getMethod("message");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void register() {
Bukkit.getPluginManager().registerEvents(this, CustomNameplatesPlugin.get());
}
@Override
public void unregister() {
HandlerList.unregisterAll(this);
}
@Override
public boolean hasJoinedChannel(Player player, String channelID) {
return true;
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
return true;
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@EventHandler (ignoreCancelled = true)
public void onChat(AsyncChatEvent event) {
Object component = getComponentFromEvent(event);
String message = ReflectionUtils.getMiniMessageTextFromNonShadedComponent(component);
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> chatBubblesManager.onChat(event.getPlayer(), message));
}
private Object getComponentFromEvent(AsyncChatEvent event) {
try {
return this.messageMethod.invoke(event);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return ReflectionUtils.getEmptyComponent();
}
}
@@ -0,0 +1,92 @@
/*
* 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 me.arasple.mc.trchat.TrChat;
import me.arasple.mc.trchat.api.event.TrChatEvent;
import me.arasple.mc.trchat.module.display.channel.Channel;
import me.arasple.mc.trchat.module.internal.filter.FilteredObject;
import me.arasple.mc.trchat.taboolib.platform.BukkitAdapter;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
public class TrChatProvider extends AbstractChatProvider {
private final BukkitAdapter adapter;
public TrChatProvider(BubbleManager chatBubblesManager) {
super(chatBubblesManager);
this.adapter = new BukkitAdapter();
}
@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) {
if (TrChat.INSTANCE.api().getChannelManager().getChannel(channelID) instanceof Channel channel) {
return channel.getListeners().contains(player.getName());
} else {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
}
@Override
public boolean canJoinChannel(Player player, String channelID) {
if (TrChat.INSTANCE.api().getChannelManager().getChannel(channelID) instanceof Channel channel) {
String perm = channel.getSettings().getJoinPermission();
return player.hasPermission(perm);
} else {
LogUtils.warn("Channel " + channelID + " doesn't exist.");
return false;
}
}
@Override
public boolean isIgnoring(Player sender, Player receiver) {
return false;
}
@EventHandler (ignoreCancelled = true)
public void onTrChat(TrChatEvent event) {
if (!event.getForward()) return;
Channel channel = event.getChannel();
String channelName = channel.getId();
for (String black : chatBubblesManager.getBlacklistChannels()) {
if (channelName.equals(black)) return;
}
FilteredObject object = TrChat.INSTANCE.api().getFilterManager().filter(event.getMessage(), adapter.adaptPlayer(event.getPlayer()), true);
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(event.getSession().getPlayer(), object.getFiltered(), channelName);
});
}
}
@@ -0,0 +1,87 @@
/*
* 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 mineverse.Aust1n46.chat.api.MineverseChatAPI;
import mineverse.Aust1n46.chat.api.MineverseChatPlayer;
import mineverse.Aust1n46.chat.api.events.VentureChatEvent;
import mineverse.Aust1n46.chat.channel.ChatChannel;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.api.manager.BubbleManager;
import net.momirealms.customnameplates.api.mechanic.bubble.provider.AbstractChatProvider;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
public class VentureChatProvider extends AbstractChatProvider {
public VentureChatProvider(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) {
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();
for (String channel : chatBubblesManager.getBlacklistChannels()) {
if (channelName.equals(channel)) return;
}
final MineverseChatPlayer chatPlayer = event.getMineverseChatPlayer();
if (chatPlayer == null) {
return;
}
CustomNameplatesPlugin.get().getScheduler().runTaskAsync(() -> {
chatBubblesManager.onChat(chatPlayer.getPlayer(), event.getChat(), channelName);
});
}
}
@@ -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);
}
@@ -442,7 +454,9 @@ public class WidthManagerImpl implements WidthManager {
}
}
}
int charWidth = (int) (((double) (x_final - i * single) / single * 8) + 1);
int pixels = x_final - i * single + 1;
double times = (double) width / 128;
int charWidth = (int) (pixels / times);
yml.set(ConfigUtils.native2ascii(c), charWidth);
i++;
}
@@ -501,7 +515,9 @@ public class WidthManagerImpl implements WidthManager {
}
}
}
int charWidth = (int) (((double) (x_final - i * single) / single * 8) + 1);
int pixels = x_final - i * single + 1;
double times = (double) width / 128;
int charWidth = (int) (pixels / times);
yml.set(ConfigUtils.native2ascii(c), charWidth);
i++;
}
@@ -548,7 +564,10 @@ public class WidthManagerImpl implements WidthManager {
}
}
}
int charWidth = (int) (((double) (x_final - i * single) / single * 8) + 1);
int pixels = x_final - i * single + 1;
double times = (double) width / 128;
int charWidth = (int) (pixels / times);
yml.set(ConfigUtils.native2ascii(c), charWidth);
i++;
}
@@ -652,11 +671,12 @@ public class WidthManagerImpl implements WidthManager {
char[] chars = ConfigUtils.convertUnicodeStringToChars(line);
for (char c : chars) {
int width = bufferedImage.getWidth();
double times = (double) width / 144;
int single = width / 16;
int x_final = i * single;
outer:
for (int x = i * single; x < (i+1) * single; x++) {
for (int y = j * single; y < (j+1) * single; y++) {
for (int y = (int) (j * 12 * times); y < (j+1) * 12 * times; y++) {
int rgb = bufferedImage.getRGB(x, y);
int alpha = (rgb >> 24) & 0xff;
if (alpha != 0) {
@@ -665,7 +685,9 @@ public class WidthManagerImpl implements WidthManager {
}
}
}
int charWidth = (int) (((double) (x_final - i * single) / single * 8) + 1);
int pixels = x_final - i * single + 1;
int charWidth = (int) (pixels / times);
yml.set(ConfigUtils.native2ascii(c), charWidth);
i++;
}
@@ -786,7 +808,9 @@ public class WidthManagerImpl implements WidthManager {
}
}
}
int charWidth = (int) (((double) (x_final - i * single) / single * 8) + 1);
int pixels = x_final - i * single + 1;
double times = (double) width / 128;
int charWidth = (int) (pixels / times);
yml.set(unicode, charWidth);
i++;
}
@@ -846,8 +870,12 @@ public class WidthManagerImpl implements WidthManager {
}
}
public boolean isNotReplacedUnifont(int codePoint) {
return !bitMapChars.contains(codePoint);
}
@Override
public boolean registerFontData(Key key, FontData fontData) {
public boolean registerFontData(@NotNull Key key, @NotNull FontData fontData) {
if (fontDataMap.containsKey(key)) {
return false;
}
@@ -856,18 +884,18 @@ public class WidthManagerImpl implements WidthManager {
}
@Override
public boolean unregisterFontData(Key key) {
public boolean unregisterFontData(@NotNull Key key) {
return fontDataMap.remove(key) != null;
}
@Nullable
@Override
public FontData getFontData(Key key) {
public FontData getFontData(@NotNull Key key) {
return fontDataMap.get(key);
}
@Override
public int getTextWidth(String textWithTags) {
public int getTextWidth(@NotNull String textWithTags) {
return cacheSystem.getWidthFromCache(textWithTags);
}
@@ -894,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;
}
@@ -924,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());
@@ -21,6 +21,7 @@ import me.clip.placeholderapi.PlaceholderAPI;
import net.momirealms.customnameplates.api.manager.RequirementManager;
import net.momirealms.customnameplates.api.requirement.Condition;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.LogUtils;
import org.bukkit.entity.Player;
public class DisplayController {
@@ -35,6 +36,7 @@ public class DisplayController {
private int timeLeft;
private int index;
private final TimeLimitText[] texts;
private boolean metAnyCondition;
public DisplayController(
Player player,
@@ -44,10 +46,10 @@ public class DisplayController {
) {
this.owner = player;
this.checkFrequency = checkFrequency;
this.checkTimer = 0;
this.refreshTimer = 0;
this.requirements = requirements;
this.texts = texts;
this.checkTimer = checkFrequency - 1;
}
public NextStage stateCheck(Condition condition) {
@@ -78,21 +80,33 @@ public class DisplayController {
}
public boolean updateText(Condition condition) {
timeLeft--;
if (timeLeft <= 0) {
if (timeLeft > 0)
timeLeft--;
// Definitely goto "if" on init
if (timeLeft == 0) {
int triedTimes = 0;
do {
index++;
if (index >= texts.length) {
index = 0;
}
if (triedTimes == texts.length) {
timeLeft = Math.max(checkFrequency, 1);
metAnyCondition = false;
LogUtils.warn("No text is available for player " + owner.getName() + ". Please check your conditions.");
return updateText("");
}
triedTimes++;
} while (!RequirementManager.isRequirementMet(condition, texts[index].getRequirements()));
metAnyCondition = true;
timeLeft = texts[index].getDuration();
refreshTimer = 0;
return updateText(texts[index].getText());
}
if (texts[index].getRefreshFrequency() <= 0) {
if (!metAnyCondition || texts[index].getRefreshFrequency() <= 0) {
return false;
}
@@ -114,12 +128,13 @@ public class DisplayController {
return true;
}
public void initialize() {
index = 0;
public void initialize(Condition condition) {
index = texts.length - 1;
checkTimer = 0;
refreshTimer = 0;
timeLeft = texts[0].getDuration();
latestValue = PlaceholderAPI.setPlaceholders(owner, texts[0].getText());
timeLeft = 1;
// The text would definitely be refreshed
updateText(condition);
}
public String getLatestContent() {
@@ -1,9 +0,0 @@
package net.momirealms.customnameplates.paper.mechanic.misc;
import org.bukkit.event.Listener;
public class MessageManager implements Listener {
}
@@ -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,10 +22,12 @@ import net.momirealms.customnameplates.api.manager.VersionManager;
import net.momirealms.customnameplates.api.util.LogUtils;
import net.momirealms.customnameplates.paper.CustomNameplatesPluginImpl;
import net.momirealms.customnameplates.paper.adventure.AdventureManagerImpl;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.InputStream;
@@ -39,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;
@@ -54,49 +53,55 @@ 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
@Override
public String getPluginVersion() {
return pluginVersion;
@@ -109,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 -> {
@@ -152,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<>();
@@ -236,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,9 +51,11 @@ 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;
@@ -81,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
@@ -113,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() {
@@ -135,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() {
@@ -145,6 +153,14 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
public void load() {
this.unlimitedTagManager.load();
Bukkit.getPluginManager().registerEvents(this, plugin);
ProtocolLibrary.getProtocolManager().addPacketListener(entityDestroyListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entitySpawnListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityLookListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityMoveListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityTeleportListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityAddEffectListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityRemoveEffectListener);
if (!CNConfig.nameplateModule) return;
this.loadConfig();
@@ -160,13 +176,6 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
for (Player online : Bukkit.getOnlinePlayers()) {
createNameTag(online);
}
Bukkit.getPluginManager().registerEvents(this, plugin);
ProtocolLibrary.getProtocolManager().addPacketListener(entityDestroyListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entitySpawnListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityLookListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityMoveListener);
ProtocolLibrary.getProtocolManager().addPacketListener(entityTeleportListener);
}
private void loadConfig() {
@@ -259,6 +268,8 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
@EventHandler (ignoreCancelled = true, priority = EventPriority.LOW)
public void onDataLoaded(NameplateDataLoadEvent event) {
if (!CNConfig.nameplateModule) return;
OnlineUser data = event.getOnlineUser();
String nameplate = data.getNameplateKey();
if (nameplate.equals("none")) {
@@ -286,26 +297,44 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
var player = event.getPlayer();
handlePlayerJoin(player);
}
@Override
public void handlePlayerJoin(Player player) {
this.putEntityIDToMap(player.getEntityId(), player);
if (!CNConfig.isOtherTeamPluginHooked() && !isProxyMode())
plugin.getTeamManager().createTeam(player);
plugin.getScheduler().runTaskAsyncLater(() -> {
if (player.isOnline())
this.createNameTag(player);
}, 200, TimeUnit.MILLISECONDS);
// nameplate module part
if (CNConfig.nameplateModule) {
if (!CNConfig.isOtherTeamPluginHooked() && !isProxyMode()) {
plugin.getTeamManager().createTeam(player);
}
plugin.getScheduler().runTaskAsyncLater(() -> {
if (player.isOnline())
this.createNameTag(player);
}, 50, TimeUnit.MILLISECONDS);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
var player = event.getPlayer();
this.removeCachedNameplateFromMap(player.getUniqueId());
this.removeEntityIDFromMap(player.getEntityId());
handlePlayerQuit(player);
}
@Override
public void handlePlayerQuit(Player player) {
this.removeEntityIDFromMap(player.getEntityId());
this.teamTagManager.handlePlayerQuit(player);
this.unlimitedTagManager.handlePlayerQuit(player);
this.removeNameplatePlayerFromMap(player.getUniqueId());
if (!CNConfig.isOtherTeamPluginHooked() && !isProxyMode()) {
plugin.getTeamManager().removeTeam(player);
// nameplate module part
if (CNConfig.nameplateModule) {
this.removeCachedNameplateFromMap(player.getUniqueId());
if (!CNConfig.isOtherTeamPluginHooked() && !isProxyMode()) {
plugin.getTeamManager().removeTeam(player);
}
}
}
@@ -322,8 +351,19 @@ 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, Entity entity) {
public boolean putEntityIDToMap(int entityID, @NotNull Entity entity) {
if (this.entityID2EntityMap.containsKey(entityID))
return false;
this.entityID2EntityMap.put(entityID, entity);
@@ -336,7 +376,7 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
@Override
public boolean putCachedNameplateToMap(UUID uuid, CachedNameplate nameplate) {
public boolean putCachedNameplateToMap(@NotNull UUID uuid, @NotNull CachedNameplate nameplate) {
if (this.cachedNameplateMap.containsKey(uuid)) {
return false;
}
@@ -345,7 +385,7 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
@Override
public CachedNameplate removeCachedNameplateFromMap(UUID uuid) {
public CachedNameplate removeCachedNameplateFromMap(@NotNull UUID uuid) {
return this.cachedNameplateMap.remove(uuid);
}
@@ -364,7 +404,7 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
@Override
public boolean updateCachedNameplate(Player player) {
public boolean updateCachedNameplate(@NotNull Player player) {
Optional<OnlineUser> onlineUser = plugin.getStorageManager().getOnlineUser(player.getUniqueId());
if (onlineUser.isEmpty()) return false;
Nameplate nameplate = onlineUser.get().getNameplate();
@@ -372,45 +412,49 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
@Override
public boolean updateCachedNameplate(Player player, Nameplate nameplate) {
public boolean updateCachedNameplate(@NotNull Player player, Nameplate nameplate) {
CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId());
if (cachedNameplate == null) return false;
return updateCachedNameplate(cachedNameplate, player, nameplate);
}
@Override
public CachedNameplate getCacheNameplate(Player player) {
public CachedNameplate getCacheNameplate(@NotNull Player player) {
return cachedNameplateMap.get(player.getUniqueId());
}
@Override
public void createNameTag(Player player) {
public NameplatePlayer createNameTag(@NotNull Player player) {
if (tagMode == TagMode.TEAM) {
putNameplatePlayerToMap(this.teamTagManager.createTagForPlayer(player, teamPrefix, teamSuffix));
EntityTagPlayer tagPlayer = this.unlimitedTagManager.createOrGetTagForPlayer(player, false);
NameplatePlayer nameplatePlayer = this.teamTagManager.createTagForPlayer(player, teamPrefix, teamSuffix);
putNameplatePlayerToMap(nameplatePlayer);
this.unlimitedTagManager.createOrGetTagForPlayer(player);
return nameplatePlayer;
} else if (tagMode == TagMode.UNLIMITED) {
EntityTagPlayer tagPlayer = this.unlimitedTagManager.createOrGetTagForPlayer(player, true);
EntityTagPlayer tagPlayer = this.unlimitedTagManager.createOrGetTagForPlayer(player);
for (DynamicTextTagSetting setting : tagSettings) {
tagPlayer.addTag(setting);
}
putNameplatePlayerToMap(tagPlayer);
return tagPlayer;
} else {
EntityTagPlayer tagPlayer = this.unlimitedTagManager.createOrGetTagForPlayer(player, false);
this.unlimitedTagManager.createOrGetTagForPlayer(player);
return null;
}
}
@Override
public void putNameplatePlayerToMap(NameplatePlayer player) {
public void putNameplatePlayerToMap(@NotNull NameplatePlayer player) {
this.nameplatePlayerMap.put(player.getPlayer().getUniqueId(), player);
}
@Override
public NameplatePlayer getNameplatePlayer(UUID uuid) {
public NameplatePlayer getNameplatePlayer(@NotNull UUID uuid) {
return this.nameplatePlayerMap.get(uuid);
}
@Override
public NameplatePlayer removeNameplatePlayerFromMap(UUID uuid) {
public NameplatePlayer removeNameplatePlayerFromMap(@NotNull UUID uuid) {
return this.nameplatePlayerMap.remove(uuid);
}
@@ -443,22 +487,25 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
return true;
}
@NotNull
@Override
public String getNameplatePrefix(Player player) {
public String getNameplatePrefix(@NotNull Player player) {
CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId());
if (cachedNameplate == null) return "";
return cachedNameplate.getTagPrefix();
}
@NotNull
@Override
public String getNameplateSuffix(Player player) {
public String getNameplateSuffix(@NotNull Player player) {
CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId());
if (cachedNameplate == null) return "";
return cachedNameplate.getTagSuffix();
}
@NotNull
@Override
public String getFullNameTag(Player player) {
public String getFullNameTag(@NotNull Player player) {
CachedNameplate cachedNameplate = cachedNameplateMap.get(player.getUniqueId());
if (cachedNameplate == null) {
return player.getName();
@@ -471,8 +518,9 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
+ cachedNameplate.getNameSuffix();
}
@NotNull
@Override
public List<String> getAvailableNameplates(Player player) {
public List<String> getAvailableNameplates(@NotNull Player player) {
List<String> nameplates = new ArrayList<>();
for (String nameplate : nameplateMap.keySet()) {
if (hasNameplate(player, nameplate)) {
@@ -482,8 +530,9 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
return nameplates;
}
@NotNull
@Override
public List<String> getAvailableNameplateDisplayNames(Player player) {
public List<String> getAvailableNameplateDisplayNames(@NotNull Player player) {
List<String> nameplates = new ArrayList<>();
for (Map.Entry<String, Nameplate> entry : nameplateMap.entrySet()) {
if (hasNameplate(player, entry.getKey())) {
@@ -494,7 +543,7 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
@Override
public boolean equipNameplate(Player player, String nameplateKey, boolean temp) {
public boolean equipNameplate(@NotNull Player player, @NotNull String nameplateKey, boolean temp) {
Nameplate nameplate = getNameplate(nameplateKey);
if (nameplate == null && nameplateKey.equals("none")) {
return false;
@@ -545,14 +594,14 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
}
@Override
public boolean registerNameplate(String key, Nameplate nameplate) {
public boolean registerNameplate(@NotNull String key, @NotNull Nameplate nameplate) {
if (this.nameplateMap.containsKey(key)) return false;
this.nameplateMap.put(key, nameplate);
return true;
}
@Override
public boolean unregisterNameplate(String key) {
public boolean unregisterNameplate(@NotNull String key) {
return this.nameplateMap.remove(key) != null;
}
@@ -566,39 +615,43 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
return previewDuration;
}
@NotNull
@Override
public TagMode getTagMode() {
return tagMode;
}
@Override
@Nullable
public Nameplate getNameplate(String key) {
@Override
public Nameplate getNameplate(@NotNull String key) {
return nameplateMap.get(key);
}
@NotNull
@Override
public Collection<Nameplate> getNameplates() {
return nameplateMap.values();
}
@NotNull
@Override
public Collection<String> getNameplateKeys() {
return nameplateMap.keySet();
}
@Override
public boolean containsNameplate(String key) {
public boolean containsNameplate(@NotNull String key) {
return nameplateMap.containsKey(key);
}
@Override
public boolean hasNameplate(Player player, String nameplate) {
public boolean hasNameplate(@NotNull Player player, @NotNull String nameplate) {
return player.hasPermission("nameplates.equip." + nameplate);
}
@NotNull
@Override
public TeamColor getTeamColor(Player player) {
public TeamColor getTeamColor(@NotNull Player player) {
CachedNameplate nameplate = getCacheNameplate(player);
return nameplate == null ? TeamColor.WHITE : nameplate.getTeamColor();
}
@@ -609,11 +662,13 @@ public class NameplateManagerImpl implements NameplateManager, Listener {
return defaultNameplate;
}
@NotNull
@Override
public TeamTagManager getTeamTagManager() {
return teamTagManager;
}
@NotNull
@Override
public UnlimitedTagManager getUnlimitedTagManager() {
return unlimitedTagManager;
@@ -636,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,11 +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,11 +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,11 +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,11 +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;
}
public synchronized void onPacketSending(PacketEvent event) {
@Override
public void onPacketSending(PacketEvent event) {
if (event.isCancelled()) return;
manager.onEntitySpawn(event.getPlayer(), event.getPacket().getIntegers().read(0));
}
}
@@ -30,11 +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);
}
}
}
@@ -26,9 +26,7 @@ import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import net.momirealms.customnameplates.api.CustomNameplatesPlugin;
import net.momirealms.customnameplates.paper.mechanic.nameplate.NameplateManagerImpl;
import net.momirealms.customnameplates.paper.mechanic.nameplate.tag.team.TeamTagManagerImpl;
import java.util.ArrayList;
@@ -40,11 +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;
@@ -40,7 +41,7 @@ public class TeamTagManagerImpl implements TeamTagManager {
private final NameplateManager manager;
private final ConcurrentHashMap<UUID, TeamPlayer> teamPlayerMap;
private CancellableTask refreshTask;
private PlayerInfoListener tabListener;
private final PlayerInfoListener tabListener;
public TeamTagManagerImpl(NameplateManager manager) {
this.manager = manager;
@@ -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]);
}
}
Loaded 100 of 160 files, more files were not shown because too many files have changed in this diff. Show more