Compare commits

..

49 Commits

Author SHA1 Message Date
b42bbb4887 ItemStackAdapter for Json: removed backward compatibility of serialized data (was temporary during the server upgrade) 2025-07-20 00:22:41 +02:00
34809d4618 ItemStackAdapter for Json: fix again deserialization 2025-07-18 17:17:08 +02:00
843d9c3509 ItemStackAdapter for Json: deserialized json cannot contains both old and new data. When both are present (because old server needs the old one), keeping only the new data. 2025-07-18 17:09:30 +02:00
1716e0b5a8 ItemStackAdapter for Json: make the generated json compatible with pre 1.21.5 deserializer
-> temporary fix so the older servers does not throw an error when trying to deserialize (e.g. when using the lobby compass menu)
2025-07-18 16:53:22 +02:00
254b885648 Fix reflection for 1.21.7/8 (round 6) 2025-07-18 15:12:38 +02:00
e2c0098eb9 Fix reflection for 1.21.7/8 (round 5) 2025-07-18 14:37:12 +02:00
2fc3eb50f5 Added missing javadoc 2025-07-18 01:56:28 +02:00
fc44151f2b Fix reflection for 1.21.7/8 (round 4) 2025-07-18 01:52:17 +02:00
f8a7c5f1e7 Small dependency upgrade 2025-07-18 01:39:58 +02:00
a9ea8c3038 Fix reflection for 1.21.7/8 (round 3) 2025-07-18 01:37:57 +02:00
e611d06987 MC 1.21.8 2025-07-17 19:12:03 +02:00
638e57bb7f Fix reflection wrappers for 1.21.7 (round 2) 2025-07-17 14:41:44 +02:00
0009dd22cd Fix reflection wrappers for 1.21.7 2025-07-17 13:22:05 +02:00
79474b14d2 Make pandalib compile against Paper API 1.21.7 (prepare to full 1.21.7 update) 2025-07-14 22:31:55 +02:00
ee4812bdbb Adjust debugging message for command registration 2025-07-14 00:29:32 +02:00
7d2a5e7862 Fix another reflection wrapper inheritance 2025-07-14 00:26:04 +02:00
c09362c75e Fix reflection wrapper inheritance 2025-07-14 00:20:00 +02:00
457262049d Fix some reflection wrapping issues 2025-07-14 00:16:38 +02:00
ebbbc3a1f0 Fixes to support last Paper 1.21.4 build 2025-07-14 00:02:07 +02:00
8c0db895da Added MC 1.21.7 2025-07-01 21:39:39 +02:00
5943b10d16 Added MC 1.21.6 2025-06-21 18:14:18 +02:00
cda7ebadcc Updated Bungeecord version 2025-06-21 18:13:51 +02:00
dbdf1eeb7c Fix ItemStackBuilder#canBreak 2025-05-24 17:26:42 +02:00
500163d8f4 Spaces around hyphen of versions range in MinecraftVersionUtil#toString() 2025-04-05 00:14:06 +02:00
9374f8d280 Updated MC version json file 2025-04-04 00:29:52 +02:00
f5194334de Fix PerformanceAnalysisManager 2025-02-20 21:54:00 +01:00
21777d4b9e Fully use chat components in PerformanceAnalysisManager lag bar 2025-02-20 21:31:13 +01:00
3b4cf63c48 Updated Skull class handling custom heads. No more relying on MHF_* accounts 2025-02-17 00:30:38 +01:00
e2b2ab466d New RandomUtil method shuffleMap 2025-02-12 23:10:48 +01:00
50e21896ba Override VaultAPI Permission extra methods #playerAdd and @playerRemove to ignore the current player world when another plugin changes permissions 2025-02-06 23:19:06 +01:00
07af67b33f Less verbose client websocket when reconnecting 2025-01-20 00:00:14 +01:00
f4d0ccca51 Do not put the HttpClient of a persistent websocket into a try-with-resources 2025-01-19 23:30:56 +01:00
51bc0bd6e8 Removed unused reflected method. 2025-01-19 17:53:53 +01:00
c229b14779 ItemStackBuilder: make canPlaceOn and canBreak actually working (must use old deprecated API because the new API is not yet working) 2025-01-18 19:17:33 +01:00
49942b35da Add support for item data components in ItemStackBuilder 2025-01-17 00:50:15 +01:00
0ffe3198e6 Fixed hundreds of small issues, code improvements, typos, ... 2025-01-16 00:25:23 +01:00
ace34fc0e8 MC 1.21.4 + small fixes 2025-01-13 23:57:48 +01:00
27c444f3b4 test: adjusted BadCommandUsage javadoc 2025-01-11 23:52:18 +01:00
c589da2a14 mvn: updated papermc repo url 2025-01-11 23:27:38 +01:00
3fe4a1b244 Big Javadoc update + cleaned/refactored some code 2025-01-11 00:17:44 +01:00
1925dd9b36 Removed utility method to remove stacked entities (as Paper API now provides a way to teleport entities riding other entities) 2025-01-01 17:59:04 +01:00
d637b92f6c Fix deprecation in 1.21.3 API 2025-01-01 13:47:38 +01:00
af4bab0d12 1.21.3 API 2024-12-30 00:00:41 +01:00
44dc690736 Few javadoc update 2024-12-27 23:15:55 +01:00
c9af5ad308 New module pandalib-config 2024-12-27 23:15:37 +01:00
27403a6e20 Backup : ignore error when a source file has been deleted during the backup precess 2024-12-26 19:51:25 +01:00
38a42dcca0 Fix reflection again 2024-12-26 00:30:09 +01:00
5782046b7a Reduce verbosity on some reflection errors 2024-12-26 00:24:17 +01:00
2b407d7f27 Fix various reflection issues for Paper 1.21.1 2024-12-26 00:23:53 +01:00
130 changed files with 3018 additions and 2474 deletions

View File

@@ -9,17 +9,18 @@ that are detailed in their respective Readme file (if any).
- `pandalib-util` General purpose utility and helper classes;
- `pandalib-chat` A chat API working on top of the Adventure API;
- `pandalib-db` An ORM working with a MySQL server through JDBC;
- `pandalib-bungee` Utility and helper classes to use in Bungeecord plugins. Also provides platform implementation for `pandalib-players` and `pandalib-commands`;
- `pandalib-bungee` Utility and helper classes to use in BungeeCord plugins. Also provides platform implementation for `pandalib-players` and `pandalib-commands`;
- `pandalib-paper` Utility and helper classes to use in Spigot/Paper plugins. Also provides platform implementation for `pandalib-players` and `pandalib-commands`;
- `pandalib-reflect` A reflection wrapper to make reflective operation easier;
- `pandalib-permissions` A general purpose permission system;
- `pandalib-bungee-permissions` Integration of the permission system `pandalib-permissions` into Bungeecord;
- `pandalib-bungee-permissions` Integration of the permission system `pandalib-permissions` into BungeeCord;
- `pandalib-paper-permissions` Integration of the permission system `pandalib-permissions` into Bukkit, Vault and WEPIF permission systems;
- `pandalib-players` A library to handle classes representing online or offline players;
- `pandalib-players-permissible` An extension of `pandalib-players` with support for the permission system `pandalib-permissions`;
- `pandalib-netapi` A poorly designed, but working TCP network library;
- `pandalib-config` Utility and helper classes to handle configuration related files and folders;
- `pandalib-commands` An abstract command manager working on top of [Brigadier](https://github.com/Mojang/brigadier);
- `pandalib-cli` Utility and helper classes for a standalone CLI Java application.
- `pandalib-cli` Utility and helper classes for a standalone CLI Java application;
- `pandalib-core` A catch-all module for some helper classes that didn't have their own module yet;
### Use in your projects

View File

@@ -21,11 +21,6 @@
</repositories>
<dependencies>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-chat</artifactId>

View File

@@ -1,7 +1,7 @@
package fr.pandacube.lib.bungee.chat;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.chat.Chat.FormatableChat;
import fr.pandacube.lib.chat.Chat.FormattableChat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
@@ -15,22 +15,22 @@ public class ChatBungee {
/**
* Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent}.
* Creates a {@link FormattableChat} from the provided Bungee {@link BaseComponent}.
* @param c the {@link BaseComponent}.
* @return a new {@link FormatableChat}.
* @return a new {@link FormattableChat}.
*/
public static FormatableChat chatComponent(BaseComponent c) {
public static FormattableChat chatComponent(BaseComponent c) {
return Chat.chatComponent(toAdventure(c));
}
/**
* Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent BaseComponent[]}.
* Creates a {@link FormattableChat} from the provided Bungee {@link BaseComponent BaseComponent[]}.
* @param c the array of {@link BaseComponent}.
* @return a new {@link FormatableChat}.
* @return a new {@link FormattableChat}.
*/
public static FormatableChat chatComponent(BaseComponent[] c) {
public static FormattableChat chatComponent(BaseComponent[] c) {
return Chat.chatComponent(toAdventure(c));
}
@@ -71,4 +71,7 @@ public class ChatBungee {
BaseComponent[] arr = toBungeeArray(component);
return arr.length == 1 ? arr[0] : new net.md_5.bungee.api.chat.TextComponent(arr);
}
private ChatBungee() {}
}

View File

@@ -1,7 +1,7 @@
package fr.pandacube.lib.bungee;
import fr.pandacube.lib.bungee.util.BungeeDailyLogRotateFileHandler;
import fr.pandacube.lib.bungee.util.PluginMessagePassthrough;
import fr.pandacube.lib.bungee.util.PluginMessagePassThrough;
import net.md_5.bungee.api.plugin.Plugin;
/**
@@ -24,7 +24,7 @@ public class PandaLibBungee {
* Method to be called in {@link Plugin#onEnable()} method.
*/
public static void onEnable() {
PluginMessagePassthrough.init(plugin);
PluginMessagePassThrough.init(plugin);
BungeeDailyLogRotateFileHandler.init(true);
}

View File

@@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.List;
/**
* Class that holds the configuration varables for {@link BungeeBackupManager}.
* Class that holds the configuration variables for {@link BungeeBackupManager}.
*/
@SuppressWarnings("CanBeFinal")
public class BungeeBackupConfig {

View File

@@ -7,14 +7,14 @@ import fr.pandacube.lib.core.backup.RotatedLogsBackupProcess;
import java.io.File;
/**
* Handles the backup processes for a Bungeecord instance.
* Handles the backup processes for a BungeeCord instance.
*/
public class BungeeBackupManager extends BackupManager {
BungeeBackupConfig config;
/**
* Instanciate a new {@link BungeeBackupManager}.
* Creates a new {@link BungeeBackupManager}.
* @param config the configuration.
*/
public BungeeBackupManager(BungeeBackupConfig config) {

View File

@@ -6,7 +6,7 @@ import java.io.File;
import java.util.function.BiPredicate;
/**
* The backup process responsible for the working directory of the current Bungeecord instance.
* The backup process responsible for the working directory of the current BungeeCord instance.
*/
public class BungeeWorkdirProcess extends BackupProcess {

View File

@@ -6,7 +6,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
/**
* Represents any player on a Bungeecord proxy, either offline or online.
* Represents any player on a BungeeCord proxy, either offline or online.
*/
public interface BungeeOffPlayer extends AbstractOffPlayer {

View File

@@ -20,7 +20,7 @@ import net.md_5.bungee.protocol.packet.PluginMessage;
import java.util.Locale;
/**
* Represents any online player on a Bungeecord proxy.
* Represents any online player on a BungeeCord proxy.
*/
public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlayer {

View File

@@ -19,16 +19,16 @@ import net.md_5.bungee.event.EventHandler;
* <p>
* Usage example, in your plugin init code:
* <pre>{@code
* PluginMessagePassthrough.init(yourPluginInstance);
* PluginMessagePassthrough.register("worldedit:cui"); // plugin message used by WorldEdit
* PluginMessagePassThrough.init(yourPluginInstance);
* PluginMessagePassThrough.register("worldedit:cui"); // plugin message used by WorldEdit
* }</pre>
*/
public class PluginMessagePassthrough implements Listener {
public class PluginMessagePassThrough implements Listener {
private static final List<String> channels = Collections.synchronizedList(new ArrayList<>());
private static final PluginMessagePassthrough instance = new PluginMessagePassthrough();
private static final PluginMessagePassThrough instance = new PluginMessagePassThrough();
/**
* Initialize the {@link PluginMessagePassthrough}.
* Initialize the {@link PluginMessagePassThrough}.
* It registers the required event listener.
* @param plugin the plugin instance.
*/
@@ -92,7 +92,7 @@ public class PluginMessagePassthrough implements Listener {
}
private PluginMessagePassthrough() { }
private PluginMessagePassThrough() { }
/**

View File

@@ -54,7 +54,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<version>${gson.version}</version>
</dependency>
</dependencies>

View File

@@ -35,8 +35,8 @@ import java.util.function.UnaryOperator;
* This class implements {@link ComponentLike} and {@link HoverEventSource} so they can be used directly in
* Adventure API and its implementation without using the final methods of this builder.
* <p>
* The unique possible concrete subclass of this class, {@link FormatableChat}, takes care of the formatting of the
* built component. The rationale for this design is explained in the documentation of {@link FormatableChat}.
* The unique possible concrete subclass of this class, {@link FormattableChat}, takes care of the formatting of the
* built component. The rationale for this design is explained in the documentation of {@link FormattableChat}.
*/
public abstract sealed class Chat extends ChatStatic implements HoverEventSource<Component>, ComponentLike {
@@ -296,7 +296,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param key the keybinding to display.
* @return this.
*/
public Chat thenKeyBind(String key) { return then(keybind(key)); }
public Chat thenKeyBind(String key) { return then(keyBind(key)); }
/**
* Appends a component with the provided score name and objective.
@@ -454,7 +454,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* Appends a component filling a chat line with the configured decoration character and
* color and a left-aligned text.
* @param leftText the text aligned to the left.
* @return a new {@link FormatableChat} filling a chat line with the configured decoration character
* @return a new {@link FormattableChat} filling a chat line with the configured decoration character
* and color and a left-aligned text.
*/
public Chat thenLeftText(ComponentLike leftText) { return then(leftText(leftText, console)); }
@@ -463,7 +463,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* Appends a component filling a chat line with the configured decoration character and
* color and a right-aligned text.
* @param rightText the text aligned to the right.
* @return a new {@link FormatableChat} filling a chat line with the configured decoration character
* @return a new {@link FormattableChat} filling a chat line with the configured decoration character
* and color and a right-aligned text.
*/
public Chat thenRightText(ComponentLike rightText) { return then(rightText(rightText, console)); }
@@ -472,7 +472,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* Appends a component filling a chat line with the configured decoration character and
* color and a centered text.
* @param centerText the text aligned to the center.
* @return a new {@link FormatableChat} filling a chat line with the configured decoration character
* @return a new {@link FormattableChat} filling a chat line with the configured decoration character
* and color and a centered text.
*/
public Chat thenCenterText(ComponentLike centerText) {
@@ -481,7 +481,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
/**
* Appends a component filling a chat line with the configured decoration character and color.
* @return a new {@link FormatableChat} filling a chat line with a decoration character and color.
* @return a new {@link FormattableChat} filling a chat line with a decoration character and color.
*/
public Chat thenFilledLine() { return then(filledLine(console)); }
@@ -520,10 +520,10 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* .thenText("!"); // short for .then(Chat.text("!"))
* // the red color for "!" is not needed because the parent component is already red.
* }</pre>
* When calling {@link #then(Component) #then(...)} on a {@link FormatableChat}, the method returns itself, cast
* When calling {@link #then(Component) #then(...)} on a {@link FormattableChat}, the method returns itself, cast
* to {@link Chat}, to prevent future formatting (that the programmer would think it formats the previously appended
* subcomponent). If the formatting of the currently built component is needed, since {@link Chat} is a sealed
* class which only subclass is {@link FormatableChat}, you can cast the builder, and use the format methods again.
* class which only subclass is {@link FormattableChat}, you can cast the builder, and use the format methods again.
* <pre>{@code
* Chat component = Chat.text("Hello ").red()
* .then(Chat.text("world").darkRed().bold())
@@ -532,8 +532,8 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* ((FormatableChat)component).underlined(); // this will not format only the last appended text.
* }</pre>
*/
public static final class FormatableChat extends Chat {
/* package */ FormatableChat(ComponentBuilder<?, ?> c) {
public static final class FormattableChat extends Chat {
/* package */ FormattableChat(ComponentBuilder<?, ?> c) {
super(c);
}
@@ -543,33 +543,33 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param c true for console, false for game UI.
* @return this.
*/
public FormatableChat console(boolean c) { console = c; return this; }
public FormattableChat console(boolean c) { console = c; return this; }
/**
* Configure the width of the line.
* @param w the width to consider when rendering the line. In pixel for game UI rendering, n character for
* console rendering.
* @return this.
*/
public FormatableChat maxWidth(int w) { maxWidth = w; return this; }
public FormattableChat maxWidth(int w) { maxWidth = w; return this; }
/**
* Sets the color of this component.
* @param c the color.
* @return this.
*/
public FormatableChat color(TextColor c) { builder.color(c); return this; }
public FormattableChat color(TextColor c) { builder.color(c); return this; }
/**
* Sets the color of this component.
* @param c the color.
* @return this.
*/
public FormatableChat color(Color c) { return color(c == null ? null : TextColor.color(c.getRGB())); }
public FormattableChat color(Color c) { return color(c == null ? null : TextColor.color(c.getRGB())); }
/**
* Sets the color of this component.
* @param c the color.
* @return this.
*/
public FormatableChat color(String c) {
public FormattableChat color(String c) {
if (c == null)
return color((TextColor) null);
TextColor tc = c.startsWith("#")
@@ -585,138 +585,138 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* Sets the color of this component to {@link NamedTextColor#BLACK}.
* @return this.
*/
public FormatableChat black() { return color(NamedTextColor.BLACK); }
public FormattableChat black() { return color(NamedTextColor.BLACK); }
/**
* Sets the color of this component to {@link NamedTextColor#DARK_BLUE}.
* @return this.
*/
public FormatableChat darkBlue() { return color(NamedTextColor.DARK_BLUE); }
public FormattableChat darkBlue() { return color(NamedTextColor.DARK_BLUE); }
/**
* Sets the color of this component to {@link NamedTextColor#DARK_GREEN}.
* @return this.
*/
public FormatableChat darkGreen() { return color(NamedTextColor.DARK_GREEN); }
public FormattableChat darkGreen() { return color(NamedTextColor.DARK_GREEN); }
/**
* Sets the color of this component to {@link NamedTextColor#DARK_AQUA}.
* @return this.
*/
public FormatableChat darkAqua() { return color(NamedTextColor.DARK_AQUA); }
public FormattableChat darkAqua() { return color(NamedTextColor.DARK_AQUA); }
/**
* Sets the color of this component to {@link NamedTextColor#DARK_RED}.
* @return this.
*/
public FormatableChat darkRed() { return color(NamedTextColor.DARK_RED); }
public FormattableChat darkRed() { return color(NamedTextColor.DARK_RED); }
/**
* Sets the color of this component to {@link NamedTextColor#DARK_PURPLE}.
* @return this.
*/
public FormatableChat darkPurple() { return color(NamedTextColor.DARK_PURPLE); }
public FormattableChat darkPurple() { return color(NamedTextColor.DARK_PURPLE); }
/**
* Sets the color of this component to {@link NamedTextColor#GOLD}.
* @return this.
*/
public FormatableChat gold() { return color(NamedTextColor.GOLD); }
public FormattableChat gold() { return color(NamedTextColor.GOLD); }
/**
* Sets the color of this component to {@link NamedTextColor#GRAY}.
* @return this.
*/
public FormatableChat gray() { return color(NamedTextColor.GRAY); }
public FormattableChat gray() { return color(NamedTextColor.GRAY); }
/**
* Sets the color of this component to {@link NamedTextColor#DARK_GRAY}.
* @return this.
*/
public FormatableChat darkGray() { return color(NamedTextColor.DARK_GRAY); }
public FormattableChat darkGray() { return color(NamedTextColor.DARK_GRAY); }
/**
* Sets the color of this component to {@link NamedTextColor#BLUE}.
* @return this.
*/
public FormatableChat blue() { return color(NamedTextColor.BLUE); }
public FormattableChat blue() { return color(NamedTextColor.BLUE); }
/**
* Sets the color of this component to {@link NamedTextColor#GREEN}.
* @return this.
*/
public FormatableChat green() { return color(NamedTextColor.GREEN); }
public FormattableChat green() { return color(NamedTextColor.GREEN); }
/**
* Sets the color of this component to {@link NamedTextColor#AQUA}.
* @return this.
*/
public FormatableChat aqua() { return color(NamedTextColor.AQUA); }
public FormattableChat aqua() { return color(NamedTextColor.AQUA); }
/**
* Sets the color of this component to {@link NamedTextColor#RED}.
* @return this.
*/
public FormatableChat red() { return color(NamedTextColor.RED); }
public FormattableChat red() { return color(NamedTextColor.RED); }
/**
* Sets the color of this component to {@link NamedTextColor#LIGHT_PURPLE}.
* @return this.
*/
public FormatableChat lightPurple() { return color(NamedTextColor.LIGHT_PURPLE); }
public FormattableChat lightPurple() { return color(NamedTextColor.LIGHT_PURPLE); }
/**
* Sets the color of this component to {@link NamedTextColor#YELLOW}.
* @return this.
*/
public FormatableChat yellow() { return color(NamedTextColor.YELLOW); }
public FormattableChat yellow() { return color(NamedTextColor.YELLOW); }
/**
* Sets the color of this component to {@link NamedTextColor#WHITE}.
* @return this.
*/
public FormatableChat white() { return color(NamedTextColor.WHITE); }
public FormattableChat white() { return color(NamedTextColor.WHITE); }
/**
* Sets the color of this component to {@link ChatConfig#successColor}.
* @return this.
*/
public FormatableChat successColor() { return color(ChatConfig.successColor); }
public FormattableChat successColor() { return color(ChatConfig.successColor); }
/**
* Sets the color of this component to {@link ChatConfig#failureColor}.
* @return this.
*/
public FormatableChat failureColor() { return color(ChatConfig.failureColor); }
public FormattableChat failureColor() { return color(ChatConfig.failureColor); }
/**
* Sets the color of this component to {@link ChatConfig#infoColor}.
* @return this.
*/
public FormatableChat infoColor() { return color(ChatConfig.infoColor); }
public FormattableChat infoColor() { return color(ChatConfig.infoColor); }
/**
* Sets the color of this component to {@link ChatConfig#warningColor}.
* @return this.
*/
public FormatableChat warningColor() { return color(ChatConfig.warningColor); }
public FormattableChat warningColor() { return color(ChatConfig.warningColor); }
/**
* Sets the color of this component to {@link ChatConfig#dataColor}.
* @return this.
*/
public FormatableChat dataColor() { return color(ChatConfig.dataColor); }
public FormattableChat dataColor() { return color(ChatConfig.dataColor); }
/**
* Sets the color of this component to {@link ChatConfig#decorationColor}.
* @return this.
*/
public FormatableChat decorationColor() { return color(ChatConfig.decorationColor); }
public FormattableChat decorationColor() { return color(ChatConfig.decorationColor); }
/**
* Sets the color of this component to {@link ChatConfig#urlColor}.
* @return this.
*/
public FormatableChat urlColor() { return color(ChatConfig.urlColor); }
public FormattableChat urlColor() { return color(ChatConfig.urlColor); }
/**
* Sets the color of this component to {@link ChatConfig#commandColor}.
* @return this.
*/
public FormatableChat commandColor() { return color(ChatConfig.commandColor); }
public FormattableChat commandColor() { return color(ChatConfig.commandColor); }
/**
* Sets the color of this component to {@link ChatConfig#highlightedCommandColor}.
* @return this.
*/
public FormatableChat highlightedCommandColor() { return color(ChatConfig.highlightedCommandColor); }
public FormattableChat highlightedCommandColor() { return color(ChatConfig.highlightedCommandColor); }
/**
* Sets the color of this component to {@link ChatConfig#broadcastColor}.
* @return this.
*/
public FormatableChat broadcastColor() { return color(ChatConfig.broadcastColor); }
public FormattableChat broadcastColor() { return color(ChatConfig.broadcastColor); }
private FormatableChat setStyle(Consumer<Style.Builder> styleOp) { builder.style(styleOp); return this; }
private FormatableChat setDecoration(TextDecoration deco, Boolean state) {
private FormattableChat setStyle(Consumer<Style.Builder> styleOp) { builder.style(styleOp); return this; }
private FormattableChat setDecoration(TextDecoration deco, Boolean state) {
return setStyle(b -> b.decoration(deco, State.byBoolean(state)));
}
@@ -726,56 +726,56 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param b true to enable, false to disable, or null to inherit from parent.
* @return this.
*/
public FormatableChat bold(Boolean b) { return setDecoration(TextDecoration.BOLD, b); }
public FormattableChat bold(Boolean b) { return setDecoration(TextDecoration.BOLD, b); }
/**
* Enables the bold status of this component.
* @return this.
*/
public FormatableChat bold() { return bold(true); }
public FormattableChat bold() { return bold(true); }
/**
* Sets the italic status of this component.
* @param i true to enable, false to disable, or null to inherit from parent.
* @return this.
*/
public FormatableChat italic(Boolean i) { return setDecoration(TextDecoration.ITALIC, i); }
public FormattableChat italic(Boolean i) { return setDecoration(TextDecoration.ITALIC, i); }
/**
* Enables the italic status of this component.
* @return this.
*/
public FormatableChat italic() { return italic(true); }
public FormattableChat italic() { return italic(true); }
/**
* Sets the underlined status of this component.
* @param u true to enable, false to disable, or null to inherit from parent.
* @return this.
*/
public FormatableChat underlined(Boolean u) { return setDecoration(TextDecoration.UNDERLINED, u); }
public FormattableChat underlined(Boolean u) { return setDecoration(TextDecoration.UNDERLINED, u); }
/**
* Enables the underlined status of this component.
* @return this.
*/
public FormatableChat underlined() { return underlined(true); }
public FormattableChat underlined() { return underlined(true); }
/**
* Sets the strikethrough status of this component.
* @param s true to enable, false to disable, or null to inherit from parent.
* @return this.
*/
public FormatableChat strikethrough(Boolean s) { return setDecoration(TextDecoration.STRIKETHROUGH, s); }
public FormattableChat strikethrough(Boolean s) { return setDecoration(TextDecoration.STRIKETHROUGH, s); }
/**
* Enables the strikethrough status of this component.
* @return this.
*/
public FormatableChat strikethrough() { return strikethrough(true); }
public FormattableChat strikethrough() { return strikethrough(true); }
/**
* Sets the obfuscated status of this component.
* @param o true to enable, false to disable, or null to inherit from parent.
* @return this.
*/
public FormatableChat obfuscated(Boolean o) { return setDecoration(TextDecoration.OBFUSCATED, o); }
public FormattableChat obfuscated(Boolean o) { return setDecoration(TextDecoration.OBFUSCATED, o); }
/**
* Enables the obfuscated status of this component.
* @return this.
*/
public FormatableChat obfuscated() { return obfuscated(true); }
public FormattableChat obfuscated() { return obfuscated(true); }
/**
@@ -783,7 +783,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param f the font namespaced key.
* @return this.
*/
public FormatableChat font(Key f) { return setStyle(s -> s.font(f)); }
public FormattableChat font(Key f) { return setStyle(s -> s.font(f)); }
/**
@@ -791,7 +791,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param i the text to insert.
* @return this.
*/
public FormatableChat shiftClickInsertion(String i) { builder.insertion(i); return this; }
public FormattableChat shiftClickInsertion(String i) { builder.insertion(i); return this; }
/**
@@ -799,37 +799,37 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param e the {@link ClickEvent}.
* @return this.
*/
private FormatableChat click(ClickEvent e) { builder.clickEvent(e); return this; }
private FormattableChat click(ClickEvent e) { builder.clickEvent(e); return this; }
/**
* Configure this component to execute the specified command when clicked.
* @param cmdWithSlash the command to execute.
* @return this.
*/
public FormatableChat clickCommand(String cmdWithSlash) { return click(ClickEvent.runCommand(cmdWithSlash)); }
public FormattableChat clickCommand(String cmdWithSlash) { return click(ClickEvent.runCommand(cmdWithSlash)); }
/**
* Configure this component to insert in the chat-box the specified command when clicked.
* @param cmdWithSlash the command to suggest.
* @return this.
*/
public FormatableChat clickSuggest(String cmdWithSlash) { return click(ClickEvent.suggestCommand(cmdWithSlash)); }
public FormattableChat clickSuggest(String cmdWithSlash) { return click(ClickEvent.suggestCommand(cmdWithSlash)); }
/**
* Configure this component to copy into clipboard the specified text when clicked.
* @param value the text to copy.
* @return this.
*/
public FormatableChat clickClipboard(String value) { return click(ClickEvent.copyToClipboard(value)); }
public FormattableChat clickClipboard(String value) { return click(ClickEvent.copyToClipboard(value)); }
/**
* Configure this component to open the specified URL when clicked.
* @param url the URL to open.
* @return this.
*/
public FormatableChat clickURL(String url) { return click(ClickEvent.openUrl(url)); }
public FormattableChat clickURL(String url) { return click(ClickEvent.openUrl(url)); }
/**
* Configure this component to change the page of the opened book when clicked.
* @param page the page to go to.
* @return this.
*/
public FormatableChat clickBookPage(int page) { return click(ClickEvent.changePage(page)); }
public FormattableChat clickBookPage(int page) { return click(ClickEvent.changePage(page)); }
/**
@@ -837,31 +837,31 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
* @param e the {@link HoverEventSource}.
* @return this.
*/
public FormatableChat hover(HoverEventSource<?> e) { builder.hoverEvent(e); return this; }
public FormattableChat hover(HoverEventSource<?> e) { builder.hoverEvent(e); return this; }
/**
* Configure this component to show the provided component when hovered.
* @param v the component to show.
* @return this.
*/
public FormatableChat hover(Component v) { return hover((HoverEventSource<Component>) v); }
public FormattableChat hover(Component v) { return hover((HoverEventSource<Component>) v); }
/**
* Configure this component to show the provided component when hovered.
* @param v the component to show.
* @return this.
*/
public FormatableChat hover(Chat v) { return hover((HoverEventSource<Component>) v); }
public FormattableChat hover(Chat v) { return hover((HoverEventSource<Component>) v); }
/**
* Configure this component to show the provided component when hovered.
* @param v the component to show.
* @return this.
*/
public FormatableChat hover(ComponentLike v) { return hover(v.asComponent()); }
public FormattableChat hover(ComponentLike v) { return hover(v.asComponent()); }
/**
* Configure this component to show the provided legacy text when hovered.
* @param legacyText the legacy text to show.
* @return this.
*/
public FormatableChat hover(String legacyText) { return hover(legacyText(legacyText)); }
public FormattableChat hover(String legacyText) { return hover(legacyText(legacyText)); }
}
@@ -932,14 +932,14 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource
/**
* Force the italic formatting to be set to false if it is not explicitly set in the component.
* This is useful for item lores that defaults to italic in the game UI.
* This is useful for item lore that defaults to italic in the game UI.
* @param c the {@link Chat} in which to set the italic property if needed.
* @return the provided {@link Chat} instance.
*/
public static Chat italicFalseIfNotSet(Chat c) {
c.builder.style(b -> {
if (b.build().decoration(TextDecoration.ITALIC) == State.NOT_SET) {
((FormatableChat) c).italic(false);
((FormattableChat) c).italic(false);
}
});
return c;

View File

@@ -5,7 +5,7 @@ import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import fr.pandacube.lib.chat.Chat.FormatableChat;
import fr.pandacube.lib.chat.Chat.FormattableChat;
/**
* Builder for a {@link Chat} component for filling a chat line, with decoration and eventual aligned text.
@@ -150,10 +150,10 @@ public class ChatFilledLine implements ComponentLike {
/**
* Renders this line to a {@link FormatableChat}.
* @return a new {@link FormatableChat} built by this {@link ChatFilledLine}.
* Renders this line to a {@link FormattableChat}.
* @return a new {@link FormattableChat} built by this {@link ChatFilledLine}.
*/
public FormatableChat toChat() {
public FormattableChat toChat() {
int maxWidth = (this.maxWidth != null)
? this.maxWidth
: console ? ChatUtil.CONSOLE_NB_CHAR_DEFAULT : ChatUtil.DEFAULT_CHAT_WIDTH;
@@ -170,7 +170,7 @@ public class ChatFilledLine implements ComponentLike {
int textWidth = ChatUtil.componentWidth(text.asComponent(), console);
if (textWidth > maxWidth)
return (FormatableChat) text;
return (FormattableChat) text;
int repeatedCharWidth = ChatUtil.charW(decorationChar, console, decorationBold);
int nbCharLeft = 0, nbCharRight = 0;
@@ -179,12 +179,12 @@ public class ChatFilledLine implements ComponentLike {
case CENTER -> {
nbCharLeft = nbCharRight = (maxWidth - textWidth) / 2 / repeatedCharWidth;
if (nbCharLeft == 0)
return (FormatableChat) text;
return (FormattableChat) text;
}
case LEFT, RIGHT -> {
int remWidth = textWidth + nbSide * repeatedCharWidth;
if (remWidth > maxWidth)
return (FormatableChat) text;
return (FormattableChat) text;
boolean left = alignment == Alignment.LEFT;
int nbOtherSide = (maxWidth - remWidth) / repeatedCharWidth;
nbCharLeft = left ? nbSide : nbOtherSide;
@@ -197,7 +197,7 @@ public class ChatFilledLine implements ComponentLike {
.then(text);
if (decorationChar != ' ' || spacesDecorationRightSide)
d.then(Chat.text(ChatUtil.repeatedChar(decorationChar, nbCharRight)).color(decorationColor).bold(decorationBold));
return (FormatableChat) d;
return (FormattableChat) d;
}

View File

@@ -1,6 +1,6 @@
package fr.pandacube.lib.chat;
import fr.pandacube.lib.chat.Chat.FormatableChat;
import fr.pandacube.lib.chat.Chat.FormattableChat;
import net.kyori.adventure.text.BlockNBTComponent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
@@ -27,27 +27,27 @@ public abstract class ChatStatic {
private static FormatableChat chatComponent(Component c) {
return new FormatableChat(componentToBuilder(c));
private static FormattableChat chatComponent(Component c) {
return new FormattableChat(componentToBuilder(c));
}
/**
* Creates a {@link FormatableChat} from the provided {@link ComponentLike}.
* Creates a {@link FormattableChat} from the provided {@link ComponentLike}.
* If the provided component is an instance of {@link Chat}, its content will be duplicated, and the provided one
* will be untouched.
* @param c the {@link ComponentLike}.
* @return a new {@link FormatableChat}.
* @return a new {@link FormattableChat}.
*/
public static FormatableChat chatComponent(ComponentLike c) {
public static FormattableChat chatComponent(ComponentLike c) {
return chatComponent(c.asComponent());
}
/**
* Creates a {@link FormatableChat} with an empty main text content.
* @return a new empty {@link FormatableChat}.
* Creates a {@link FormattableChat} with an empty main text content.
* @return a new empty {@link FormattableChat}.
*/
public static FormatableChat chat() {
return new FormatableChat(Component.text());
public static FormattableChat chat() {
return new FormattableChat(Component.text());
}
@@ -56,60 +56,60 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content.
* Creates a {@link FormattableChat} with the provided plain text as its main text content.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content.
* @return a new {@link FormattableChat} with the provided text as its main text content.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
* instead.
*/
public static FormatableChat text(Object plainText) {
public static FormattableChat text(Object plainText) {
if (plainText instanceof ComponentLike) {
throw new IllegalArgumentException("Expected any object except instance of " + ComponentLike.class + ". Received " + plainText + ". Please use ChatStatic.chatComponent(ComponentLike) instead.");
}
return new FormatableChat(Component.text().content(Objects.toString(plainText)));
return new FormattableChat(Component.text().content(Objects.toString(plainText)));
}
/**
* Creates a {@link FormatableChat} with the provided legacy text as its content, using the section {@code "§"}
* Creates a {@link FormattableChat} with the provided legacy text as its content, using the section {@code "§"}
* character.
* @param legacyText the legacy text to use as the content, that uses the {@code "§"} character.
* @return a new {@link FormatableChat} with the provided text as its content.
* @return a new {@link FormattableChat} with the provided text as its content.
* @throws IllegalArgumentException If the {@code legacyText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
* instead.
*/
public static FormatableChat legacyText(Object legacyText) {
public static FormattableChat legacyText(Object legacyText) {
return legacyText(legacyText, LegacyComponentSerializer.SECTION_CHAR);
}
/**
* Creates a {@link FormatableChat} with the provided legacy text as its content, using the ampersand {@code "&"}
* Creates a {@link FormattableChat} with the provided legacy text as its content, using the ampersand {@code "&"}
* character.
* @param legacyText the legacy text to use as the content, that uses the {@code "&"} character.
* @return a new {@link FormatableChat} with the provided text as its content.
* @return a new {@link FormattableChat} with the provided text as its content.
* @throws IllegalArgumentException If the {@code legacyText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
* instead.
*/
public static FormatableChat legacyAmpersandText(Object legacyText) {
public static FormattableChat legacyAmpersandText(Object legacyText) {
return legacyText(legacyText, LegacyComponentSerializer.AMPERSAND_CHAR);
}
/**
* Creates a {@link FormatableChat} with the provided legacy text as its content, using the specified
* Creates a {@link FormattableChat} with the provided legacy text as its content, using the specified
* legacyCharacter.
* @param legacyText the legacy text to use as the content.
* @param legacyCharacter the character used in the provided text to prefix color and format code.
* @return a new {@link FormatableChat} with the provided text as its content.
* @return a new {@link FormattableChat} with the provided text as its content.
* @throws IllegalArgumentException If the {@code legacyText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
* instead.
*/
private static FormatableChat legacyText(Object legacyText, char legacyCharacter) {
private static FormattableChat legacyText(Object legacyText, char legacyCharacter) {
if (legacyText instanceof ComponentLike) {
throw new IllegalArgumentException("Expected any object except instance of " + ComponentLike.class + ". Received " + legacyText + ". Please use ChatStatic.chatComponent(ComponentLike) instead.");
}
@@ -118,118 +118,118 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} with the provided MiniMessage text as its content.
* Creates a {@link FormattableChat} with the provided MiniMessage text as its content.
* @param miniMessageText the MiniMessage text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its content.
* @return a new {@link FormattableChat} with the provided text as its content.
*/
public static FormatableChat miniMessageText(String miniMessageText) {
public static FormattableChat miniMessageText(String miniMessageText) {
return chatComponent(MiniMessage.miniMessage().deserialize(miniMessageText));
}
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
* Creates a {@link FormattableChat} with the provided plain text as its main text content, and colored using the
* {@link ChatConfig#infoColor configured info color}.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#infoColor()} instead.
* {@link FormattableChat#infoColor()} instead.
*/
public static FormatableChat infoText(Object plainText) {
public static FormattableChat infoText(Object plainText) {
return text(plainText).infoColor();
}
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
* Creates a {@link FormattableChat} with the provided plain text as its main text content, and colored using the
* {@link ChatConfig#warningColor configured warning color}.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#warningColor()} instead.
* {@link FormattableChat#warningColor()} instead.
*/
public static FormatableChat warningText(Object plainText) {
public static FormattableChat warningText(Object plainText) {
return text(plainText).warningColor();
}
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
* Creates a {@link FormattableChat} with the provided plain text as its main text content, and colored using the
* {@link ChatConfig#dataColor configured data color}.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#dataColor()} instead.
* {@link FormattableChat#dataColor()} instead.
*/
public static FormatableChat dataText(Object plainText) {
public static FormattableChat dataText(Object plainText) {
return text(plainText).dataColor();
}
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
* Creates a {@link FormattableChat} with the provided plain text as its main text content, and colored using the
* {@link ChatConfig#decorationColor configured decorationColor color}.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#decorationColor()} instead.
* {@link FormattableChat#decorationColor()} instead.
*/
public static FormatableChat decorationText(Object plainText) {
public static FormattableChat decorationText(Object plainText) {
return text(plainText).decorationColor();
}
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
* Creates a {@link FormattableChat} with the provided plain text as its main text content, and colored using the
* {@link ChatConfig#successColor configured success color}.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#successColor()} instead.
* {@link FormattableChat#successColor()} instead.
*/
public static FormatableChat successText(Object plainText) {
public static FormattableChat successText(Object plainText) {
return text(plainText).successColor();
}
/**
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
* Creates a {@link FormattableChat} with the provided plain text as its main text content, and colored using the
* {@link ChatConfig#failureColor configured failure color}.
* @param plainText the text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#failureColor()} instead.
* {@link FormattableChat#failureColor()} instead.
*/
public static FormatableChat failureText(Object plainText) {
public static FormattableChat failureText(Object plainText) {
return text(plainText).failureColor();
}
/**
* Creates a {@link FormatableChat} with the provided legacy text as its main text content, and colored in white in
* Creates a {@link FormattableChat} with the provided legacy text as its main text content, and colored in white in
* case there is no color on the generated parent component.
* @param legacyText the legacy text to use as the content.
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
* @return a new {@link FormattableChat} with the provided text as its main text content, and the configured color.
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
* {@link FormatableChat#failureColor()} instead.
* {@link FormattableChat#failureColor()} instead.
*/
public static FormatableChat playerNameText(String legacyText) {
FormatableChat fc = legacyText(legacyText);
public static FormattableChat playerNameText(String legacyText) {
FormattableChat fc = legacyText(legacyText);
fc.builder.colorIfAbsent(NamedTextColor.WHITE);
return fc;
}
/**
* Creates a {@link FormatableChat} from the provided {@link Component}, coloring in white the generated parent
* Creates a {@link FormattableChat} from the provided {@link Component}, coloring in white the generated parent
* component in case there is no color defined.
* If the provided component is an instance of {@link Chat}, its content will be duplicated, and the provided one
* will be untouched.
* @param c the {@link Component}.
* @return a new {@link FormatableChat}.
* @return a new {@link FormattableChat}.
*/
public static FormatableChat playerNameComponent(ComponentLike c) {
FormatableChat fc = chatComponent(c);
public static FormattableChat playerNameComponent(ComponentLike c) {
FormattableChat fc = chatComponent(c);
fc.builder.colorIfAbsent(NamedTextColor.WHITE);
return fc;
}
@@ -238,32 +238,32 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} with the provided translation key and parameters.
* Creates a {@link FormattableChat} with the provided translation key and parameters.
* @param key the translation key.
* @param with the translation parameters.
* @return a new {@link FormatableChat} with the provided translation key and parameters.
* @return a new {@link FormattableChat} with the provided translation key and parameters.
*/
public static FormatableChat translation(String key, Object... with) {
return new FormatableChat(Component.translatable().key(key).arguments(Chat.filterObjToTranslationArgumentLike(with)));
public static FormattableChat translation(String key, Object... with) {
return new FormattableChat(Component.translatable().key(key).arguments(Chat.filterObjToTranslationArgumentLike(with)));
}
/**
* Creates a {@link FormatableChat} with the provided keybinding.
* Creates a {@link FormattableChat} with the provided keybinding.
* @param key the keybinding to display.
* @return a new {@link FormatableChat} with the provided keybinding.
* @return a new {@link FormattableChat} with the provided keybinding.
*/
public static FormatableChat keybind(String key) {
return new FormatableChat(Component.keybind().keybind(key));
public static FormattableChat keyBind(String key) {
return new FormattableChat(Component.keybind().keybind(key));
}
/**
* Creates a {@link FormatableChat} with the provided score name and objective.
* Creates a {@link FormattableChat} with the provided score name and objective.
* @param name the score name.
* @param objective the score objective.
* @return a new {@link FormatableChat} with the provided score name and objective.
* @return a new {@link FormattableChat} with the provided score name and objective.
*/
public static FormatableChat score(String name, String objective) {
return new FormatableChat(Component.score().name(name).objective(objective));
public static FormattableChat score(String name, String objective) {
return new FormattableChat(Component.score().name(name).objective(objective));
}
@@ -272,49 +272,49 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} that leads to a URL when clicked.
* Creates a {@link FormattableChat} that leads to a URL when clicked.
* @param inner the component to make clickable.
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
* @param hover the content to display when hovering the component.
* @return a new {@link FormatableChat} that leads to a URL when clicked.
* @return a new {@link FormattableChat} that leads to a URL when clicked.
*/
public static FormatableChat clickableURL(ComponentLike inner, String url, HoverEventSource<?> hover) {
public static FormattableChat clickableURL(ComponentLike inner, String url, HoverEventSource<?> hover) {
Objects.requireNonNull(url, "url");
if (inner == null)
inner = text(url);
if (hover == null)
hover = text(ChatUtil.wrapInLimitedPixels(url, 240));
return (FormatableChat) chat().clickURL(url).urlColor().hover(hover).then(inner);
return (FormattableChat) chat().clickURL(url).urlColor().hover(hover).then(inner);
}
/**
* Creates a {@link FormatableChat} that leads to a URL when clicked.
* Creates a {@link FormattableChat} that leads to a URL when clicked.
* <p>
* When hovered, the component will display the url. To customize the hover content, use
* {@link #clickableURL(ComponentLike, String, HoverEventSource)}.
* @param inner the component to make clickable.
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
* @return a new {@link FormatableChat} that leads to a URL when clicked.
* @return a new {@link FormattableChat} that leads to a URL when clicked.
*/
public static FormatableChat clickableURL(ComponentLike inner, String url) {
public static FormattableChat clickableURL(ComponentLike inner, String url) {
return clickableURL(inner, url, null);
}
/**
* Creates a {@link FormatableChat} that leads to a URL when clicked.
* Creates a {@link FormattableChat} that leads to a URL when clicked.
* <p>
* The text on which to click will be the URL itself. To configure the clicked text, use
* {@link #clickableURL(ComponentLike, String, HoverEventSource)}.
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
* @param hover the content to display when hovering the component.
* @return a new {@link FormatableChat} that leads to a URL when clicked.
* @return a new {@link FormattableChat} that leads to a URL when clicked.
*/
public static FormatableChat clickableURL(String url, HoverEventSource<?> hover) {
public static FormattableChat clickableURL(String url, HoverEventSource<?> hover) {
return clickableURL(null, url, hover);
}
/**
* Creates a {@link FormatableChat} that leads to a URL when clicked.
* Creates a {@link FormattableChat} that leads to a URL when clicked.
* <p>
* The text on which to click will be the URL itself. To configure the clicked text, use
* {@link #clickableURL(ComponentLike, String)}.
@@ -322,9 +322,9 @@ public abstract class ChatStatic {
* When hovered, the component will display the url. To customize the hover content, use
* {@link #clickableURL(String, HoverEventSource)}.
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
* @return a new {@link FormatableChat} that leads to a URL when clicked.
* @return a new {@link FormattableChat} that leads to a URL when clicked.
*/
public static FormatableChat clickableURL(String url) {
public static FormattableChat clickableURL(String url) {
return clickableURL(null, url, null);
}
@@ -334,14 +334,14 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} that runs a command when clicked.
* Creates a {@link FormattableChat} that runs a command when clicked.
* @param inner the component to make clickable.
* @param commandWithSlash the command to run. Must start with {@code "/"}.
* @param hover the content to display when hovering the component.
* @return a new {@link FormatableChat} that runs a command when clicked.
* @return a new {@link FormattableChat} that runs a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableCommand(ComponentLike inner, String commandWithSlash, HoverEventSource<?> hover) {
public static FormattableChat clickableCommand(ComponentLike inner, String commandWithSlash, HoverEventSource<?> hover) {
Objects.requireNonNull(commandWithSlash, "commandWithSlash");
if (!commandWithSlash.startsWith("/"))
throw new IllegalArgumentException("commandWithSlash must start with a '/' character.");
@@ -349,39 +349,39 @@ public abstract class ChatStatic {
inner = text(commandWithSlash);
if (hover == null)
hover = text(ChatUtil.wrapInLimitedPixels(commandWithSlash, 240));
return (FormatableChat) chat().clickCommand(commandWithSlash).commandColor().hover(hover).then(inner);
return (FormattableChat) chat().clickCommand(commandWithSlash).commandColor().hover(hover).then(inner);
}
/**
* Creates a {@link FormatableChat} that runs a command when clicked.
* Creates a {@link FormattableChat} that runs a command when clicked.
* <p>
* When hovered, the component will display the command itself. To customize the hover content, use
* {@link #clickableCommand(ComponentLike, String, HoverEventSource)}.
* @param inner the component to make clickable.
* @param commandWithSlash the command to run. Must start with {@code "/"}.
* @return a new {@link FormatableChat} that runs a command when clicked.
* @return a new {@link FormattableChat} that runs a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableCommand(ComponentLike inner, String commandWithSlash) {
public static FormattableChat clickableCommand(ComponentLike inner, String commandWithSlash) {
return clickableCommand(inner, commandWithSlash, null);
}
/**
* Creates a {@link FormatableChat} that runs a command when clicked.
* Creates a {@link FormattableChat} that runs a command when clicked.
* <p>
* The text on which to click will be the command itself. To configure the clicked text, use
* {@link #clickableCommand(ComponentLike, String, HoverEventSource)}.
* @param commandWithSlash the command to run. Must start with {@code "/"}.
* @param hover the content to display when hovering the component.
* @return a new {@link FormatableChat} that runs a command when clicked.
* @return a new {@link FormattableChat} that runs a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableCommand(String commandWithSlash, HoverEventSource<?> hover) {
public static FormattableChat clickableCommand(String commandWithSlash, HoverEventSource<?> hover) {
return clickableCommand(null, commandWithSlash, hover);
}
/**
* Creates a {@link FormatableChat} that runs a command when clicked.
* Creates a {@link FormattableChat} that runs a command when clicked.
* <p>
* The text on which to click will be the command itself. To configure the clicked text, use
* {@link #clickableCommand(ComponentLike, String)}.
@@ -389,10 +389,10 @@ public abstract class ChatStatic {
* When hovered, the component will display the command itself. To customize the hover content, use
* {@link #clickableCommand(String, HoverEventSource)}.
* @param commandWithSlash the command to run. Must start with {@code "/"}.
* @return a new {@link FormatableChat} that runs a command when clicked.
* @return a new {@link FormattableChat} that runs a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableCommand(String commandWithSlash) {
public static FormattableChat clickableCommand(String commandWithSlash) {
return clickableCommand(null, commandWithSlash, null);
}
@@ -402,14 +402,14 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* Creates a {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* @param inner the component to make clickable.
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
* @param hover the content to display when hovering the component.
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* @return a new {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableSuggest(ComponentLike inner, String commandWithSlash, HoverEventSource<?> hover) {
public static FormattableChat clickableSuggest(ComponentLike inner, String commandWithSlash, HoverEventSource<?> hover) {
Objects.requireNonNull(commandWithSlash, "commandWithSlash");
if (!commandWithSlash.startsWith("/"))
throw new IllegalArgumentException("commandWithSlash must start with a '/' character.");
@@ -417,39 +417,39 @@ public abstract class ChatStatic {
inner = text(commandWithSlash);
if (hover == null)
hover = text(ChatUtil.wrapInLimitedPixels(commandWithSlash, 240));
return (FormatableChat) chat().clickSuggest(commandWithSlash).commandColor().hover(hover).then(inner);
return (FormattableChat) chat().clickSuggest(commandWithSlash).commandColor().hover(hover).then(inner);
}
/**
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* Creates a {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* <p>
* When hovered, the component will display the command itself. To customize the hover content, use
* {@link #clickableSuggest(ComponentLike, String, HoverEventSource)}.
* @param inner the component to make clickable.
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* @return a new {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableSuggest(ComponentLike inner, String commandWithSlash) {
public static FormattableChat clickableSuggest(ComponentLike inner, String commandWithSlash) {
return clickableSuggest(inner, commandWithSlash, null);
}
/**
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* Creates a {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* <p>
* The text on which to click will be the command itself. To configure the clicked text, use
* {@link #clickableSuggest(ComponentLike, String, HoverEventSource)}.
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
* @param hover the content to display when hovering the component.
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* @return a new {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableSuggest(String commandWithSlash, HoverEventSource<?> hover) {
public static FormattableChat clickableSuggest(String commandWithSlash, HoverEventSource<?> hover) {
return clickableSuggest(null, commandWithSlash, hover);
}
/**
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* Creates a {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* <p>
* The text on which to click will be the command itself. To configure the clicked text, use
* {@link #clickableSuggest(ComponentLike, String)}.
@@ -457,10 +457,10 @@ public abstract class ChatStatic {
* When hovered, the component will display the command itself. To customize the hover content, use
* {@link #clickableSuggest(String, HoverEventSource)}.
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
* @return a new {@link FormattableChat} that pre-fill the chat box with a command when clicked.
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
*/
public static FormatableChat clickableSuggest(String commandWithSlash) {
public static FormattableChat clickableSuggest(String commandWithSlash) {
return clickableSuggest(null, commandWithSlash, null);
}
@@ -472,112 +472,112 @@ public abstract class ChatStatic {
/**
* Creates a {@link FormatableChat} filling a chat line with decoration and a left-aligned text.
* Creates a {@link FormattableChat} filling a chat line with decoration and a left-aligned text.
* @param text the text aligned to the left.
* @param decorationChar the character used for decoration around the text.
* @param decorationColor the color used for the decoration characters.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with decoration and a left-aligned text.
* @return a new {@link FormattableChat} filling a chat line with decoration and a left-aligned text.
* @see ChatFilledLine#leftText(ComponentLike)
*/
public static FormatableChat leftText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
public static FormattableChat leftText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
return ChatFilledLine.leftText(text).decoChar(decorationChar).decoColor(decorationColor).spacesAroundText().console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with the configured decoration character and
* Creates a {@link FormattableChat} filling a chat line with the configured decoration character and
* color and a left-aligned text.
* @param text the text aligned to the left.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with the configured decoration character
* @return a new {@link FormattableChat} filling a chat line with the configured decoration character
* and color and a left-aligned text.
* @see ChatFilledLine#leftText(ComponentLike)
* @see ChatConfig#decorationChar
* @see ChatConfig#decorationColor
*/
public static FormatableChat leftText(ComponentLike text, boolean console) {
public static FormattableChat leftText(ComponentLike text, boolean console) {
return ChatFilledLine.leftText(text).spacesAroundText().console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with decoration and a right-aligned text.
* Creates a {@link FormattableChat} filling a chat line with decoration and a right-aligned text.
* @param text the text aligned to the right.
* @param decorationChar the character used for decoration around the text.
* @param decorationColor the color used for the decoration characters.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with decoration and a right-aligned
* @return a new {@link FormattableChat} filling a chat line with decoration and a right-aligned
* text.
* @see ChatFilledLine#rightText(ComponentLike)
*/
public static FormatableChat rightText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
public static FormattableChat rightText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
return ChatFilledLine.rightText(text).decoChar(decorationChar).decoColor(decorationColor).spacesAroundText().console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with the configured decoration character and
* Creates a {@link FormattableChat} filling a chat line with the configured decoration character and
* color and a right-aligned text.
* @param text the text aligned to the right.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with the configured decoration character
* @return a new {@link FormattableChat} filling a chat line with the configured decoration character
* and color and a right-aligned text.
* @see ChatFilledLine#rightText(ComponentLike)
* @see ChatConfig#decorationChar
* @see ChatConfig#decorationColor
*/
public static FormatableChat rightText(ComponentLike text, boolean console) {
public static FormattableChat rightText(ComponentLike text, boolean console) {
return ChatFilledLine.rightText(text).spacesAroundText().console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with decoration and a centered text.
* Creates a {@link FormattableChat} filling a chat line with decoration and a centered text.
* @param text the text aligned to the center.
* @param decorationChar the character used for decoration around the text.
* @param decorationColor the color used for the decoration characters.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with decoration and a centered text.
* @return a new {@link FormattableChat} filling a chat line with decoration and a centered text.
* @see ChatFilledLine#centerText(ComponentLike)
*/
public static FormatableChat centerText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
public static FormattableChat centerText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
return ChatFilledLine.centerText(text).decoChar(decorationChar).decoColor(decorationColor).spacesAroundText().console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with the configured decoration character and
* Creates a {@link FormattableChat} filling a chat line with the configured decoration character and
* color and a centered text.
* @param text the text aligned to the center.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with the configured decoration character
* @return a new {@link FormattableChat} filling a chat line with the configured decoration character
* and color and a centered text.
* @see ChatFilledLine#centerText(ComponentLike)
* @see ChatConfig#decorationChar
* @see ChatConfig#decorationColor
*/
public static FormatableChat centerText(ComponentLike text, boolean console) {
public static FormattableChat centerText(ComponentLike text, boolean console) {
return ChatFilledLine.centerText(text).spacesAroundText().console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with a decoration character and color.
* Creates a {@link FormattableChat} filling a chat line with a decoration character and color.
* @param decorationChar the character used for decoration.
* @param decorationColor the color used for the decoration characters.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with a decoration character and color.
* @return a new {@link FormattableChat} filling a chat line with a decoration character and color.
* @see ChatFilledLine#filled()
*/
public static FormatableChat filledLine(char decorationChar, TextColor decorationColor, boolean console) {
public static FormattableChat filledLine(char decorationChar, TextColor decorationColor, boolean console) {
return ChatFilledLine.filled().decoChar(decorationChar).decoColor(decorationColor).console(console).toChat();
}
/**
* Creates a {@link FormatableChat} filling a chat line with the configured decoration character and
* Creates a {@link FormattableChat} filling a chat line with the configured decoration character and
* color.
* @param console if the line is rendered on console (true) or IG (false).
* @return a new {@link FormatableChat} filling a chat line with a decoration character and color.
* @return a new {@link FormattableChat} filling a chat line with a decoration character and color.
* @see ChatFilledLine#filled()
* @see ChatConfig#decorationChar
* @see ChatConfig#decorationColor
*/
public static FormatableChat filledLine(boolean console) {
public static FormattableChat filledLine(boolean console) {
return ChatFilledLine.filled().console(console).toChat();
}

View File

@@ -1,6 +1,6 @@
package fr.pandacube.lib.chat;
import fr.pandacube.lib.chat.Chat.FormatableChat;
import fr.pandacube.lib.chat.Chat.FormattableChat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.TextComponent;
@@ -152,7 +152,7 @@ public class ChatUtil {
else
first = false;
FormatableChat pDisplay = Chat.clickableCommand(Chat.text(page), String.format(cmdFormat, page), Chat.text("Aller à la page " + page));
FormattableChat pDisplay = Chat.clickableCommand(Chat.text(page), String.format(cmdFormat, page), Chat.text("Aller à la page " + page));
if (page == currentPage) {
pDisplay.highlightedCommandColor();
}
@@ -180,12 +180,12 @@ public class ChatUtil {
* @param elements the components to join.
* @return a new {@link Chat} instance with all the provided {@code component} joined using the separators.
*/
public static FormatableChat joinGrammatically(ComponentLike regularSeparator, ComponentLike finalSeparator, List<? extends ComponentLike> elements) {
public static FormattableChat joinGrammatically(ComponentLike regularSeparator, ComponentLike finalSeparator, List<? extends ComponentLike> elements) {
int size = elements == null ? 0 : elements.size();
int last = size - 1;
return switch (size) {
case 0, 1, 2 -> join(finalSeparator, elements);
default -> (FormatableChat) join(regularSeparator, elements.subList(0, last))
default -> (FormattableChat) join(regularSeparator, elements.subList(0, last))
.then(finalSeparator)
.then(elements.get(last));
};
@@ -202,8 +202,8 @@ public class ChatUtil {
* @param elements the components to join.
* @return a new {@link Chat} instance with all the provided {@code component} joined using the separators.
*/
public static FormatableChat join(ComponentLike separator, Iterable<? extends ComponentLike> elements) {
FormatableChat c = chat();
public static FormattableChat join(ComponentLike separator, Iterable<? extends ComponentLike> elements) {
FormattableChat c = chat();
if (elements == null)
return c;
boolean first = true;
@@ -596,7 +596,7 @@ public class ChatUtil {
for (int i = 0; i < sizes.length; i++) {
sumSizes += sizes[i];
FormatableChat subC = ChatStatic.text(repeatedChar(PROGRESS_BAR_FULL_CHAR, sizes[i]));
FormattableChat subC = ChatStatic.text(repeatedChar(PROGRESS_BAR_FULL_CHAR, sizes[i]));
if (colors != null && i < colors.length && colors[i] != null)
subC.color(colors[i]);

View File

@@ -15,42 +15,26 @@
<packaging>jar</packaging>
<repositories>
<repository>
<id>minecraft-libraries</id>
<name>Minecraft Libraries</name>
<url>https://libraries.minecraft.net</url>
</repository>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-reflect</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-commands</artifactId>
<version>${project.version}</version>
</dependency>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-log</artifactId>
<version>${bungeecord.version}</version>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-commands</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
<artifactId>bungeecord-log</artifactId>
<version>${bungeecord.version}</version>
</dependency>

View File

@@ -1,22 +1,25 @@
package fr.pandacube.lib.cli;
import fr.pandacube.lib.cli.commands.CLIBrigadierDispatcher;
import fr.pandacube.lib.cli.log.CLILogger;
import fr.pandacube.lib.util.log.Log;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import java.io.IOException;
import java.util.logging.Logger;
import fr.pandacube.lib.cli.commands.CLIBrigadierDispatcher;
import fr.pandacube.lib.cli.log.CLILogger;
import jline.console.ConsoleReader;
import org.fusesource.jansi.AnsiConsole;
import fr.pandacube.lib.util.log.Log;
/**
* Class to handle general standard IO operation for a CLI application. It uses Jlines {@link ConsoleReader} for the
* Class to handle general standard IO operation for a CLI application. It uses Jlines {@link LineReader} for the
* console rendering, a JUL {@link Logger} for logging, and Brigadier to handle commands.
*/
public class CLI extends Thread {
private final ConsoleReader reader;
private final LineReader reader;
private final Logger logger;
@@ -28,10 +31,11 @@ public class CLI extends Thread {
super("Console Thread");
setDaemon(true);
AnsiConsole.systemInstall();
reader = new ConsoleReader();
reader.setPrompt(">");
reader.addCompleter(CLIBrigadierDispatcher.instance);
Terminal terminal = TerminalBuilder.builder().build();
reader = LineReaderBuilder.builder().terminal(terminal)
.completer(CLIBrigadierDispatcher.instance)
.build()
;
// configure logger's formatter
System.setProperty("net.md_5.bungee.log-date-format", "yyyy-MM-dd HH:mm:ss");
@@ -40,10 +44,10 @@ public class CLI extends Thread {
/**
* Gets the Jline {@link ConsoleReader} of this CLI instance.
* @return the Jline {@link ConsoleReader} of this CLI instance.
* Gets the Jline {@link LineReader} of this CLI instance.
* @return the Jline {@link LineReader} of this CLI instance.
*/
public ConsoleReader getConsoleReader() {
public LineReader getConsoleReader() {
return reader;
}
@@ -65,15 +69,14 @@ public class CLI extends Thread {
int i = 0;
String line;
try {
while((line = reader.readLine()) != null) {
if (line.trim().equals(""))
while((line = reader.readLine(">")) != null) {
if (line.trim().isEmpty())
continue;
String cmdLine = line;
new Thread(() -> CLIBrigadierDispatcher.instance.execute(cmdLine), "CLICmdThread #"+(i++)).start();
Thread.ofVirtual().name("CLICmdThread #"+(i++))
.start(() -> CLIBrigadierDispatcher.instance.execute(cmdLine));
}
} catch (IOException e) {
Log.severe(e);
}
} catch (UserInterruptException | EndOfFileException ignore) { }
}

View File

@@ -32,6 +32,7 @@ public abstract class CLIApplication {
/**
* Creates a new application instance.
*/
@SuppressWarnings("CallToPrintStackTrace")
protected CLIApplication() {
instance = this;
CLI tmpCLI = null;

View File

@@ -38,16 +38,9 @@ public abstract class CLIBrigadierCommand extends BrigadierCommand<CLICommandSen
}
protected abstract LiteralArgumentBuilder<CLICommandSender> buildCommand();
protected String[] getAliases() {
return new String[0];
}
public boolean isPlayer(CLICommandSender sender) {
public boolean isPlayer(CLICommandSender sender) {
return sender.isPlayer();
}

View File

@@ -3,8 +3,11 @@ package fr.pandacube.lib.cli.commands;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions;
import fr.pandacube.lib.commands.BrigadierDispatcher;
import jline.console.completer.Completer;
import net.kyori.adventure.text.ComponentLike;
import org.jline.reader.Candidate;
import org.jline.reader.Completer;
import org.jline.reader.LineReader;
import org.jline.reader.ParsedLine;
import java.util.List;
@@ -39,17 +42,15 @@ public class CLIBrigadierDispatcher extends BrigadierDispatcher<CLICommandSender
@Override
public int complete(String buffer, int cursor, List<CharSequence> candidates) {
String bufferBeforeCursor = buffer.substring(0, cursor);
public void complete(LineReader lineReader, ParsedLine parsedLine, List<Candidate> candidates) {
String bufferBeforeCursor = parsedLine.line().substring(0, parsedLine.cursor());
Suggestions completeResult = getSuggestions(bufferBeforeCursor);
completeResult.getList().stream()
.map(Suggestion::getText)
.map(Candidate::new)
.forEach(candidates::add);
return completeResult.getRange().getStart();
}
/**

View File

@@ -41,6 +41,9 @@ public interface CLICommandSender extends Audience {
*/
void sendMessage(String message);
@SuppressWarnings({"UnstableApiUsage", "deprecation"})
@Override // force implementation of super-interface default method
void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type);
}

View File

@@ -38,7 +38,7 @@ public class CLIConsoleCommandSender implements CLICommandSender {
}
@Override
public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) {
public void sendMessage(@NotNull Identity source, @NotNull Component message, @SuppressWarnings({"UnstableApiUsage", "deprecation"}) @NotNull MessageType type) {
sendMessage(Chat.chatComponent(message).getLegacyText());
}
}

View File

@@ -14,7 +14,7 @@ import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.chat.Chat.FormatableChat;
import fr.pandacube.lib.chat.Chat.FormattableChat;
import fr.pandacube.lib.chat.ChatTreeNode;
import fr.pandacube.lib.cli.CLIApplication;
import fr.pandacube.lib.util.log.Log;
@@ -195,13 +195,13 @@ public class CommandAdmin extends CLIBrigadierCommand {
private Component displayCurrentNode(CommandNode<CLICommandSender> node, boolean redirectTarget, CLICommandSender sender) {
if (node == null)
throw new IllegalArgumentException("node must not be null");
FormatableChat d;
FormattableChat d;
if (node instanceof RootCommandNode) {
d = text("(root)").italic()
.hover("Root command node");
}
else if (node instanceof ArgumentCommandNode) {
ArgumentType<?> type = ((ArgumentCommandNode<?, ?>) node).getType();
else if (node instanceof ArgumentCommandNode<?, ?> argNode) {
ArgumentType<?> type = argNode.getType();
String typeStr = type.getClass().getSimpleName();
if (type instanceof IntegerArgumentType
|| type instanceof LongArgumentType
@@ -260,10 +260,10 @@ public class CommandAdmin extends CLIBrigadierCommand {
return d.get();
}
private static class DisplayCommandNode {
final List<CommandNode<CLICommandSender>> nodes = new ArrayList<>();
final List<DisplayCommandNode> children = new ArrayList<>();

View File

@@ -45,7 +45,7 @@
<dependency>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
<version>1.0.18</version>
<version>${brigadier.version}</version>
</dependency>
</dependencies>

View File

@@ -3,35 +3,39 @@ package fr.pandacube.lib.commands;
import java.util.logging.Logger;
/**
* Throw an instance of this exception to indicate to the plugin command handler that the user has missused the command.
* The message, if provided, must indicate the reason of the mussusage of the command. It will be displayed on the
* Throw an instance of this exception to indicate to the plugin command handler that the user has badly used the command.
* The message, if provided, must indicate the reason of the bad usage of the command. It will be displayed on the
* screen with eventual indications of how to use the command (help command for example).
* If a {@link Throwable} cause is provided, it will be relayed to the plugin {@link Logger}.
*
*/
public class BadCommandUsage extends RuntimeException {
/** Constructs a new runtime exception with no message or cause.
/**
* Constructs a new runtime exception with no message or cause.
*/
public BadCommandUsage() {
super();
}
/** Constructs a new runtime exception with the specified cause.
/**
* Constructs a new runtime exception with the specified cause.
* @param cause the cause.
*/
public BadCommandUsage(Throwable cause) {
super(cause);
}
/** Constructs a new runtime exception with the specified message.
/**
* Constructs a new runtime exception with the specified message.
* @param message the message.
*/
public BadCommandUsage(String message) {
super(message);
}
/** Constructs a new runtime exception with the specified message and cause.
/**
* Constructs a new runtime exception with the specified message and cause.
* @param message the message.
* @param cause the cause.
*/

View File

@@ -223,14 +223,14 @@ public abstract class BrigadierCommand<S> {
/**
* Wraps the provided {@link SuggestionsSupplier} into a Brigadiers {@link SuggestionProvider}.
* @param suggestions the suggestions to wrap.
* @param senderUnwrapper function to convert the command sender provided by brigadier into the command sender
* @param senderUnWrapper function to convert the command sender provided by brigadier into the command sender
* supported by {@link SuggestionsSupplier}.
* @return a {@link SuggestionProvider} generating the suggestions from the provided {@link SuggestionsSupplier}.
* @param <AS> the type of command sender supported by the {@link SuggestionsSupplier}.
*/
protected <AS> SuggestionProvider<S> wrapSuggestions(SuggestionsSupplier<AS> suggestions, Function<S, AS> senderUnwrapper) {
protected <AS> SuggestionProvider<S> wrapSuggestions(SuggestionsSupplier<AS> suggestions, Function<S, AS> senderUnWrapper) {
return (context, builder) -> {
AS sender = senderUnwrapper.apply(context.getSource());
AS sender = senderUnWrapper.apply(context.getSource());
String message = builder.getInput();
try {
int tokenStartPos = builder.getStart();

View File

@@ -241,7 +241,7 @@ public interface SuggestionsSupplier<S> {
return (s, ti, token, a) -> {
try {
List<Long> proposedValues = new ArrayList<>();
if (token.length() == 0) {
if (token.isEmpty()) {
long start = Math.max(Math.max(Math.min(-4, max - 9), min), -9);
long end = Math.min(Math.min(start + 9, max), 9);
ListUtil.addLongRangeToList(proposedValues, start, end);
@@ -399,7 +399,7 @@ public interface SuggestionsSupplier<S> {
*/
default SuggestionsSupplier<S> quotableString() {
return (s, ti, token, a) -> {
boolean startWithQuote = token.length() > 0 && (token.charAt(0) == '"' || token.charAt(0) == '\'');
boolean startWithQuote = !token.isEmpty() && (token.charAt(0) == '"' || token.charAt(0) == '\'');
String realToken = startWithQuote ? unescapeBrigadierQuotable(token.substring(1), token.charAt(0)) : token;
String[] argsCopy = Arrays.copyOf(a, a.length);
argsCopy[a.length - 1] = realToken;

33
pandalib-config/pom.xml Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pandalib-parent</artifactId>
<groupId>fr.pandacube.lib</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pandalib-config</artifactId>
<packaging>jar</packaging>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
<version>${bungeecord.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,7 +1,4 @@
package fr.pandacube.lib.core.config;
import fr.pandacube.lib.chat.ChatColorUtil;
import fr.pandacube.lib.util.log.Log;
package fr.pandacube.lib.config;
import java.io.BufferedReader;
import java.io.File;
@@ -56,7 +53,7 @@ public abstract class AbstractConfig {
while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
if (ignoreEmpty && trimmedLine.equals(""))
if (ignoreEmpty && trimmedLine.isEmpty())
continue;
if (ignoreHashtagComment && trimmedLine.startsWith("#"))
@@ -114,25 +111,6 @@ public abstract class AbstractConfig {
}
/**
* Utility method to that translate the {@code '&'} formatted string to the legacy format.
* @param string the string to convert.
* @return a legacy formatted string (using {@code '§'}).
*/
public static String getTranslatedColorCode(String string) {
return ChatColorUtil.translateAlternateColorCodes('&', string);
}
/**
* Logs the message as a warning into console, prefixed with the context of this config.
* @param message the message to log.
*/
protected void warning(String message) {
Log.warning("Error in configuration '"+configFile.getName()+"': " + message);
}
/**
* The type of config.
*/

View File

@@ -1,4 +1,4 @@
package fr.pandacube.lib.core.config;
package fr.pandacube.lib.config;
import java.io.File;
import java.io.IOException;

View File

@@ -5,6 +5,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
@@ -117,7 +118,11 @@ public class ZipCompressor {
}
for (Entry entry : entriesToCompress) {
entry.zip();
try {
entry.zip();
} catch (NoSuchFileException ignored) {
// file has been deleted since
}
}
synchronized (stateLock) {

View File

@@ -3,6 +3,7 @@ package fr.pandacube.lib.core.json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.Strictness;
import com.google.gson.ToNumberStrategy;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
@@ -32,7 +33,7 @@ public class Json {
boolean isFloat = value.contains(".");
if (isFloat) {
// if float, will only parse to Double
// if is float, will only parse to Double
// (see org.yaml.snakeyaml.constructor.SafeConstructor.ConstructYamlFloat)
try {
Double d = Double.valueOf(value);
@@ -74,21 +75,21 @@ public class Json {
public static final Gson gson = build(Function.identity());
/**
* {@link Gson} instance with {@link GsonBuilder#setLenient()}, {@link GsonBuilder#setPrettyPrinting()} and support
* {@link Gson} instance with {@link Strictness#LENIENT}, {@link GsonBuilder#setPrettyPrinting()} and support
* for Java records and additional {@link TypeAdapterFactory} provided with
* {@link #registerTypeAdapterFactory(TypeAdapterFactory)}.
*/
public static final Gson gsonPrettyPrinting = build(GsonBuilder::setPrettyPrinting);
/**
* {@link Gson} instance with {@link GsonBuilder#setLenient()}, {@link GsonBuilder#serializeNulls()} and support for
* {@link Gson} instance with {@link Strictness#LENIENT}, {@link GsonBuilder#serializeNulls()} and support for
* Java records and additional {@link TypeAdapterFactory} provided with
* {@link #registerTypeAdapterFactory(TypeAdapterFactory)}.
*/
public static final Gson gsonSerializeNulls = build(GsonBuilder::serializeNulls);
/**
* {@link Gson} instance with {@link GsonBuilder#setLenient()}, {@link GsonBuilder#serializeNulls()},
* {@link Gson} instance with {@link Strictness#LENIENT}, {@link GsonBuilder#serializeNulls()},
* {@link GsonBuilder#setPrettyPrinting()} and support for Java records and additional {@link TypeAdapterFactory}
* provided with {@link #registerTypeAdapterFactory(TypeAdapterFactory)}.
*/
@@ -105,7 +106,7 @@ public class Json {
.registerTypeAdapterFactory(new CustomAdapterFactory())
.disableHtmlEscaping()
.setObjectToNumberStrategy(YAML_EQUIVALENT_NUMBER_STRATEGY)
.setLenient();
.setStrictness(Strictness.LENIENT);
return builderModifier.apply(base).create();
}

View File

@@ -134,30 +134,30 @@ public class ThrowableAdapter implements JsonSerializer<Throwable>, JsonDeserial
}
private static <T extends Throwable> ThrowableSubAdapter<T> defaultSubAdapter(Class<T> clazz) {
BiFunction<String, Throwable, T> constructor = null;
BiFunction<String, Throwable, T> constructionFunction = null;
// try (String, Throwable) constructor
try {
Constructor<T> constr = clazz.getConstructor(String.class, Throwable.class);
if (constr.canAccess(null)) {
constructor = (m, t) -> ThrowableUtil.wrapReflectEx(() -> constr.newInstance(m, t));
Constructor<T> constructor = clazz.getConstructor(String.class, Throwable.class);
if (constructor.canAccess(null)) {
constructionFunction = (m, t) -> ThrowableUtil.wrapReflectEx(() -> constructor.newInstance(m, t));
}
} catch (ReflectiveOperationException ignore) { }
// try (String) constructor
try {
Constructor<T> constr = clazz.getConstructor(String.class);
if (constr.canAccess(null)) {
constructor = ThrowableSubAdapter.messageOnly((m) -> ThrowableUtil.wrapReflectEx(() -> constr.newInstance(m)));
Constructor<T> constructor = clazz.getConstructor(String.class);
if (constructor.canAccess(null)) {
constructionFunction = ThrowableSubAdapter.messageOnly((m) -> ThrowableUtil.wrapReflectEx(() -> constructor.newInstance(m)));
}
} catch (ReflectiveOperationException ignore) { }
if (constructor == null) {
if (constructionFunction == null) {
Log.warning("Provided Throwable class '" + clazz + "' does not have any of those constructors or are not accessible: (String, Throwable), (String).");
return null;
}
return new ThrowableSubAdapter<>(constructor);
return new ThrowableSubAdapter<>(constructionFunction);
}

View File

@@ -37,8 +37,8 @@ public class MinecraftVersionUtil {
/**
* Decompose a version string into a series of integers.
* @param v a string representation of a version (eg. 1.19.1).
* @return an array of int representing the provided version (eg. [1, 19, 1]).
* @param v a string representation of a version (e.g. 1.19.1).
* @return an array of int representing the provided version (e.g. [1, 19, 1]).
*/
public static int[] decomposedVersion(String v) {
try {
@@ -114,7 +114,7 @@ public class MinecraftVersionUtil {
else {
// merge
if (i - firstConsecutive > 1)
keptVersions.add(versions.get(firstConsecutive) + "-" + versions.get(i));
keptVersions.add(versions.get(firstConsecutive) + " - " + versions.get(i));
else {
keptVersions.add(versions.get(firstConsecutive));
keptVersions.add(versions.get(i));

View File

@@ -68,12 +68,12 @@ public class ProtocolVersion implements Comparable<ProtocolVersion> {
private static void init() {
// try online source first
try {
HttpResponse<String> response = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build()
.send(HttpRequest.newBuilder(URI.create(ONLINE_DATA_URL)).build(),
BodyHandlers.ofString()
try (HttpClient cl = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build()) {
HttpResponse<String> response = cl.send(
HttpRequest.newBuilder(URI.create(ONLINE_DATA_URL)).build(),
BodyHandlers.ofString()
);
if (response.statusCode() == 200) {
MinecraftVersionList data = Json.gson.fromJson(response.body(), MinecraftVersionList.class);
@@ -123,7 +123,7 @@ public class ProtocolVersion implements Comparable<ProtocolVersion> {
/**
* Gets the {@link ProtocolVersion} associated with the provided Minecraft version.
* @param version The Minecraft version, in the format "X.X[.X]" (eg. "1.17" or "1.8.8").
* @param version The Minecraft version, in the format "X.X[.X]" (e.g. "1.17" or "1.8.8").
* @return an instance of {@link ProtocolVersion}.
*/
public static ProtocolVersion ofVersion(String version) {

View File

@@ -71,7 +71,11 @@
"1.21.1": 767,
"1.21.2": 768,
"1.21.3": 768,
"1.21.4": 769
"1.21.4": 769,
"1.21.5": 770,
"1.21.6": 771,
"1.21.7": 772,
"1.21.8": 772
},
"versionsOfProtocol": {
"4": [
@@ -233,6 +237,16 @@
],
"769": [
"1.21.4"
],
"770": [
"1.21.5"
],
"771": [
"1.21.6"
],
"772": [
"1.21.7",
"1.21.8"
]
}
}

View File

@@ -227,7 +227,7 @@ public final class DB {
*/
public static <E extends SQLElement<E>> E getFirst(Class<E> elemClass, SQLWhere<E> where, SQLOrderBy<E> orderBy, Integer offset) throws DBException {
SQLElementList<E> elements = getAll(elemClass, where, orderBy, 1, offset);
return (elements.size() == 0) ? null : elements.get(0);
return (elements.isEmpty()) ? null : elements.get(0);
}
/**

View File

@@ -42,7 +42,7 @@ public class SQLType<T> {
@Override
public boolean equals(Object obj) {
return obj instanceof SQLType o
return obj instanceof SQLType<?> o
&& toString().equals(o.toString());
}

View File

@@ -16,7 +16,7 @@
<repositories>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<!-- WorldEdit -->

View File

@@ -7,6 +7,7 @@ import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.ServicePriority;
import java.util.List;
@@ -120,6 +121,13 @@ import java.util.List;
return true;
}
@Override
public boolean playerAdd(Player player, String permission) {
// override because the super class sets the permission on the current world of the player, that is probably
// not intended by the calling plugin
return playerAdd(null, player, permission);
}
@Deprecated
@Override
public boolean playerRemove(String world, String player, String permission) {
@@ -136,6 +144,14 @@ import java.util.List;
return true;
}
@Override
public boolean playerRemove(Player player, String permission) {
// to stay coherent with the override of #playerAdd(Player, String), we also override this method.
// Will try first to remove the permission on the world itself (like super-method), then if it doesn't exist,
// removes on the server level.
return super.playerRemove(player, permission) || playerRemove(null, player, permission);
}
@Override
public boolean groupHas(String world, String group, String permission) {
checkEnabled();

View File

@@ -16,12 +16,17 @@
<repositories>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>fabricmc</id>
<url>https://maven.fabricmc.net/</url>
</repository>
<repository>
<id>minecraft-libraries</id>
<name>Minecraft Libraries</name>
<url>https://libraries.minecraft.net</url>
</repository>
</repositories>
<dependencies>
@@ -84,6 +89,12 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>datafixerupper</artifactId>
<version>${datafixerupper.version}</version>
</dependency>
<!-- Paper -->
<dependency>
<groupId>io.papermc.paper</groupId>

View File

@@ -5,27 +5,47 @@ import fr.pandacube.lib.paper.json.PaperJson;
import fr.pandacube.lib.paper.modules.PerformanceAnalysisManager;
import org.bukkit.plugin.Plugin;
/**
* Main class for pandalib-paper.
*/
public class PandaLibPaper {
private static Plugin plugin;
/**
* Method to call in plugin's {@link Plugin#onLoad()} method.
* @param plugin the plugin instance.
*/
public static void onLoad(Plugin plugin) {
PandaLibPaper.plugin = plugin;
PaperJson.init();
}
/**
* Method to call in plugin's {@link Plugin#onEnable()} method.
*/
public static void onEnable() {
PerformanceAnalysisManager.getInstance(); // initialize
ServerStopEvent.init();
}
/**
* Method to call in plugin's {@link Plugin#onDisable()} method.
*/
public static void disable() {
PerformanceAnalysisManager.getInstance().cancelInternalBossBar();
PerformanceAnalysisManager.getInstance().deinit();
}
/**
* Gets the plugin instance.
* @return the plugin instance provided with {@link #onLoad(Plugin)}.
*/
public static Plugin getPlugin() {
return plugin;
}
private PandaLibPaper() {}
}

View File

@@ -12,6 +12,11 @@ import java.util.List;
@SuppressWarnings("CanBeFinal")
public class PaperBackupConfig {
/**
* Creates a new Paper backup config.
*/
public PaperBackupConfig() {}
/**
* Set to true to enable worlds backup.
* Defaults to true.

View File

@@ -130,12 +130,12 @@ public class PaperBackupManager extends BackupManager implements Listener {
private final Set<String> dirtyForSave = new HashSet<>();
@EventHandler(priority = EventPriority.MONITOR)
public void onWorldLoad(WorldLoadEvent event) {
void onWorldLoad(WorldLoadEvent event) {
initWorldProcess(event.getWorld().getName());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onWorldSave(WorldSaveEvent event) {
void onWorldSave(WorldSaveEvent event) {
if (event.getWorld().getLoadedChunks().length > 0
|| dirtyForSave.contains(event.getWorld().getName())) {
compressWorlds.get(event.getWorld().getName()).setDirtyAfterSave();
@@ -148,18 +148,18 @@ public class PaperBackupManager extends BackupManager implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerChangeWorldEvent(PlayerChangedWorldEvent event) {
void onPlayerChangeWorldEvent(PlayerChangedWorldEvent event) {
dirtyForSave.add(event.getFrom().getName());
dirtyForSave.add(event.getPlayer().getWorld().getName());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
void onPlayerJoin(PlayerJoinEvent event) {
dirtyForSave.add(event.getPlayer().getWorld().getName());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
void onPlayerQuit(PlayerQuitEvent event) {
dirtyForSave.add(event.getPlayer().getWorld().getName());
}

View File

@@ -10,11 +10,19 @@ import net.kyori.adventure.bossbar.BossBar.Color;
import net.kyori.adventure.bossbar.BossBar.Overlay;
import org.bukkit.Bukkit;
/**
* A backup process with specific logic around Paper server.
*/
public abstract class PaperBackupProcess extends BackupProcess {
private BossBar bossBar;
/**
* Instantiates a new backup process.
* @param bm the associated backup manager.
* @param id the process identifier.
*/
protected PaperBackupProcess(PaperBackupManager bm, String id) {
super(bm, id);
}

View File

@@ -3,8 +3,15 @@ package fr.pandacube.lib.paper.backup;
import java.io.File;
import java.util.function.BiPredicate;
/**
* A backup process with specific logic around Paper server working directory.
*/
public class PaperWorkdirProcess extends PaperBackupProcess {
/**
* Instantiates a new backup process for the paper server working directory.
* @param bm the associated backup manager.
*/
protected PaperWorkdirProcess(PaperBackupManager bm) {
super(bm, "workdir");
}

View File

@@ -1,9 +1,9 @@
package fr.pandacube.lib.paper.backup;
import fr.pandacube.lib.chat.LegacyChatFormat;
import fr.pandacube.lib.paper.scheduler.SchedulerUtil;
import fr.pandacube.lib.paper.world.WorldUtil;
import fr.pandacube.lib.util.log.Log;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.World;
@@ -11,14 +11,22 @@ import java.io.File;
import java.text.DateFormat;
import java.util.Date;
/**
* A backup process with specific logic around Paper server world.
*/
public class PaperWorldProcess extends PaperBackupProcess {
private final String worldName;
private boolean autoSave = true;
protected PaperWorldProcess(PaperBackupManager bm, final String n) {
super(bm, "worlds/" + n);
worldName = n;
private boolean autoSave = true;
/**
* Instantiates a new backup process for a world.
* @param bm the associated backup manager.
* @param worldName the name of the world.
*/
protected PaperWorldProcess(PaperBackupManager bm, final String worldName) {
super(bm, "worlds/" + worldName);
this.worldName = worldName;
}
private World getWorld() {
@@ -62,11 +70,11 @@ public class PaperWorldProcess extends PaperBackupProcess {
public void displayNextSchedule() {
if (hasNextScheduled()) {
Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " is dirty. Next backup on "
Log.info("[Backup] " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + " is dirty. Next backup on "
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(getNext())));
}
else {
Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " is clean. Next backup not scheduled.");
Log.info("[Backup] " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + " is clean. Next backup not scheduled.");
}
}
@@ -80,7 +88,7 @@ public class PaperWorldProcess extends PaperBackupProcess {
public void setDirtyAfterSave() {
if (!isDirty()) { // don't set dirty if it is already
setDirtySinceNow();
Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " was saved and is now dirty. Next backup on "
Log.info("[Backup] " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + " was saved and is now dirty. Next backup on "
+ DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG)
.format(new Date(getNext()))
);

View File

@@ -18,8 +18,8 @@ import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftVector;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.VanillaCommandWrapper;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Coordinates;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Vec3Argument;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.APICommandMeta;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.BukkitCommandNode;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.PluginCommandNode;
import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
import fr.pandacube.lib.players.standalone.AbstractPlayerManager;
@@ -46,7 +46,6 @@ import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.unwrap;
import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap;
/**
@@ -57,10 +56,21 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
private static CommandDispatcher<CommandSourceStack> vanillaPaperDispatcher = null;
/**
* Gets the Brigadier dispatcher provided by paper API during {@link LifecycleEvents#COMMANDS}.
* <p>
* This Dispatcher is not the vanilla one. Instead, Paper implementation wraps the vanilla one to handle proper registration
* of commands from plugins.
* @return the Brigadier dispatcher.
*/
public static CommandDispatcher<CommandSourceStack> getVanillaPaperDispatcher() {
return vanillaPaperDispatcher;
}
/**
* Gets the root node of the dispatcher from {@link #getVanillaPaperDispatcher()}.
* @return the root node, or null if {@link #getVanillaPaperDispatcher()} is also null.
*/
public static RootCommandNode<CommandSourceStack> getRootNode() {
return vanillaPaperDispatcher == null ? null : vanillaPaperDispatcher.getRoot();
}
@@ -135,6 +145,9 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
*/
protected final String[] aliases;
/**
* The command description.
*/
protected final String description;
private final RegistrationPolicy registrationPolicy;
@@ -185,6 +198,10 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
registeredAliases = new HashSet<>(event.registrar().register(commandNode, description, List.of(aliases)));
doPostRegistrationFixes();
@SuppressWarnings("unchecked")
fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode<CommandSourceStack> registeredNode = wrap(vanillaPaperDispatcher.getRoot().getChild(commandNode.getName()), fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode.class);
if (registrationPolicy == RegistrationPolicy.ALL) {
// enforce registration of aliases
for (String alias : aliases) {
@@ -207,11 +224,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
for (String aliasToForce : forceRegistrationAgain) {
CommandNode<CommandSourceStack> actualNode = vanillaPaperDispatcher.getRoot().getChild(aliasToForce);
@SuppressWarnings("unchecked")
fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode<CommandSourceStack> wrappedCommandNode = wrap(actualNode, fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode.class);
if (actualNode != null) {
//Log.info("Forcing registration of alias /" + aliasToForce + " for command /" + commandNode.getName() + ": replacing " + getCommandIdentity(actualNode) + "?");
if (PluginCommandNode.REFLECT.get().isInstance(actualNode)) {
PluginCommandNode pcn = wrap(actualNode, PluginCommandNode.class);
if (pcn.getPlugin().equals(plugin))
if (wrappedCommandNode.apiCommandMeta() != null) {
APICommandMeta meta = wrappedCommandNode.apiCommandMeta();
if (meta.plugin().equals(plugin))
return;
}
else if (BukkitCommandNode.REFLECT.get().isInstance(actualNode)) {
@@ -219,12 +237,16 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
if (bcn.getBukkitCommand() instanceof PluginCommand pc && pc.getPlugin().equals(plugin))
return;
}
Log.warning("Forcing registration of alias /" + aliasToForce + " for command /" + commandNode.getName() + ": replacing " + getCommandIdentity(actualNode));
vanillaPaperDispatcher.getRoot().getChildren().removeIf(c -> c.getName().equals(aliasToForce));
}
/*else {
Log.info("Forcing registration of alias /" + aliasToForce + " for command /" + commandNode.getName() + ": no command found for alias. Adding alias.");
}*/
LiteralCommandNode<CommandSourceStack> newPCN = unwrap(new PluginCommandNode(aliasToForce, plugin.getPluginMeta(), commandNode, description));
/*else {
Log.info("Forcing registration of alias /" + aliasToForce + " for command /" + commandNode.getName() + ": no command found for alias. Adding alias.");
}*/
LiteralCommandNode<CommandSourceStack> newPCN = getAliasNode(commandNode, aliasToForce);
@SuppressWarnings("unchecked")
fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode<CommandSourceStack> wrappedNewPCN = wrap(newPCN, fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode.class);
wrappedNewPCN.apiCommandMeta(registeredNode.apiCommandMeta());
vanillaPaperDispatcher.getRoot().addChild(newPCN);
}
});
@@ -248,7 +270,9 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
fixedNodes.add(originalNode);
if (originalNode.getRedirect() != null) {
try {
@SuppressWarnings("rawtypes")
ReflectClass<CommandNode> cmdNodeClass = Reflect.ofClass(CommandNode.class);
@SuppressWarnings("unchecked")
CommandNode<CommandSourceStack> unwrappedNode = (CommandNode<CommandSourceStack>) cmdNodeClass.field("unwrappedCached").getValue(originalNode);
if (unwrappedNode != null) {
cmdNodeClass.field("modifier").setValue(unwrappedNode, cmdNodeClass.field("modifier").getValue(originalNode));
@@ -280,11 +304,8 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
}
private static String getCommandIdentity(CommandNode<CommandSourceStack> command) {
if (PluginCommandNode.REFLECT.get().isInstance(command)) {
PluginCommandNode wrappedPCN = wrap(command, PluginCommandNode.class);
return "Node /" + command.getName() + " from plugin " + wrappedPCN.getPlugin().getName();
}
else if (BukkitCommandNode.REFLECT.get().isInstance(command)) {
if (BukkitCommandNode.REFLECT.get().isInstance(command)) {
BukkitCommandNode wrappedBCN = wrap(command, BukkitCommandNode.class);
Command bukkitCmd = wrappedBCN.getBukkitCommand();
if (bukkitCmd instanceof PluginCommand cmd) {
@@ -302,16 +323,22 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
return "Node /" + command.getName() + " wrapping " + bukkitCmd.getClass().getName() + " /" + bukkitCmd.getName();
}
else {
return "Node /" + command.getName() + " (unspecific)";
@SuppressWarnings("unchecked")
fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode<CommandSourceStack> wrappedCommandNode = wrap(command, fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode.class);
if (wrappedCommandNode.apiCommandMeta() != null) {
APICommandMeta meta = wrappedCommandNode.apiCommandMeta();
return "Node /" + command.getName() + " from plugin " + meta.plugin().getName();
}
else {
return "Node /" + command.getName() + " (unspecific)";
}
}
}
private static Boolean isPluginCommand(CommandNode<CommandSourceStack> command) {
if (PluginCommandNode.REFLECT.get().isInstance(command)) {
return true;
}
else if (BukkitCommandNode.REFLECT.get().isInstance(command)) {
if (BukkitCommandNode.REFLECT.get().isInstance(command)) {
BukkitCommandNode wrappedBCN = wrap(command, BukkitCommandNode.class);
Command bukkitCmd = wrappedBCN.getBukkitCommand();
if (bukkitCmd instanceof PluginCommand) {
@@ -329,10 +356,16 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
return null;
}
else {
return false;
@SuppressWarnings("unchecked")
fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode<CommandSourceStack> wrappedCommandNode = wrap(command, fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode.class);
return wrappedCommandNode.apiCommandMeta() != null;
}
}
/**
* Gets the aliases that are actually registered in the server.
* @return the actually registered aliases.
*/
protected Set<String> getRegisteredAliases() {
return Set.copyOf(registeredAliases);
}
@@ -379,12 +412,15 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour
@Override
public boolean isConsole(CommandSourceStack wrapper) {
return isConsole(getCommandSender(wrapper));
}
@Override
public boolean isPlayer(CommandSourceStack wrapper) {
return isPlayer(getCommandSender(wrapper));
}
@Override
public Predicate<CommandSourceStack> hasPermission(String permission) {
return wrapper -> getCommandSender(wrapper).hasPermission(permission);
}

View File

@@ -10,28 +10,38 @@ import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.ServerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired at the beginning of the server stop process.
* More specifically, this event is called when the first plugin is disabling ({@link PluginDisableEvent}) while
* {@link Bukkit#isStopping()} returns true.
* <p>
* This event can be useful when a plugin want to execute stuff on server stop as soon as possible in the process,
* but not when the plugin itself is disabling (because some part of the Bukkit API is not usable at that moment).
*/
public class ServerStopEvent extends ServerEvent {
private static final HandlerList handlers = new HandlerList();
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
/**
* Gets the handler list of the event.
* @return the handler list of the event.
*/
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private static boolean hasTriggered = false;
private static boolean isInit = false;
/**
* Register the event used to detect the server stop.
*/
public static void init() {
if (isInit)
return;
BukkitEvent.register(new Listener() {
@EventHandler(priority = EventPriority.LOWEST)
@@ -45,7 +55,25 @@ public class ServerStopEvent extends ServerEvent {
}
});
isInit = true;
}
private ServerStopEvent() {}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@@ -37,13 +37,21 @@ public class GUIHotBar implements Listener {
private final int defaultSlot;
private final List<Player> currentPlayers = new ArrayList<>();
/**
* Set up a new gui hot bar. You should not instantiate more than one hot bar.
* @param defaultSlot the default slot (currently held item) when the player joins the hot bar.
*/
public GUIHotBar(int defaultSlot) {
this.defaultSlot = Math.max(0, Math.min(8, defaultSlot));
BukkitEvent.register(this);
}
/**
* Disables this hot bar.
* @param clearPlayerMenuItems if the items of this hot bar should be removed from the players inventories.
*/
public void disable(boolean clearPlayerMenuItems) {
removeAllPlayers(clearPlayerMenuItems);
@@ -53,9 +61,10 @@ public class GUIHotBar implements Listener {
/**
* Add the item to this hot bar menu. if there is already players hooked to this hot bar, the item will be directly added to
* their inventories.
* @param i the item stack
* @param i the item stack.
* @param setter code executed to put the item in the inventory. Additionally, check for permission before doing the addition.
* @param run the Runnable to run when the user right-click on the item in the hot bar.
* @return itself for daisy-chaining.
*/
public GUIHotBar addItem(ItemStack i, BiConsumer<PlayerInventory, ItemStack> setter, Consumer<Player> run) {
itemsAndSetters.put(i, setter);
@@ -71,6 +80,7 @@ public class GUIHotBar implements Listener {
* Add the hot bar elements to this player, or update them if applicable.
* <br>
* The player is automatically removed when they quit. You can remove it before by calling {@link #removePlayer(Player, boolean)}.
* @param p the player to add.
*/
public void addPlayer(Player p) {
if (!currentPlayers.contains(p))
@@ -85,6 +95,7 @@ public class GUIHotBar implements Listener {
/**
* Detach this player from this hot bar manager and removes the managed items from the players inventory.
* @param p the player to remove.
*/
public void removePlayer(Player p) {
removePlayer(p, true);
@@ -92,6 +103,8 @@ public class GUIHotBar implements Listener {
/**
* Detach this player from this hot bar manager and optionally removes the managed items from the players inventory.
* @param p the player to remove.
* @param clearMenuItems if the items from this hot bar should be removed from the player inventory.
*/
public void removePlayer(Player p, boolean clearMenuItems) {
if (!currentPlayers.contains(p))
@@ -106,18 +119,28 @@ public class GUIHotBar implements Listener {
currentPlayers.remove(p);
}
/**
* Tells if the provided player is attached to this hot bar.
* @param p the player to check.
* @return true if the player is attached, false otherwise.
*/
public boolean containsPlayer(Player p) {
return currentPlayers.contains(p);
}
/**
* Detach all players from this hot bar.
*/
public void removeAllPlayers() {
removeAllPlayers(true);
}
/**
* Detach all players from this hot bar.
* @param clearMenuItems if the items from this hot bar should be removed from the player inventory.
*/
public void removeAllPlayers(boolean clearMenuItems) {
for (Player p : new ArrayList<>(currentPlayers))
removePlayer(p, clearMenuItems);
@@ -127,7 +150,7 @@ public class GUIHotBar implements Listener {
public void addItemToPlayer(Player p, ItemStack is) {
private void addItemToPlayer(Player p, ItemStack is) {
if (!itemsAndSetters.containsKey(is))
throw new IllegalArgumentException("The provided ItemStack is not registered in this GUIHotBar");
if (!currentPlayers.contains(p))
@@ -135,7 +158,7 @@ public class GUIHotBar implements Listener {
itemsAndSetters.get(is).accept(p.getInventory(), is.clone());
}
public void removeItemFromPlayer(Player p, ItemStack is) {
private void removeItemFromPlayer(Player p, ItemStack is) {
p.getInventory().remove(is);
}

View File

@@ -0,0 +1,232 @@
package fr.pandacube.lib.paper.inventory;
import com.google.common.base.Preconditions;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Objects;
/**
* Dummy implementation of a player inventory.
*/
public class DummyPlayerInventory extends InventoryWrapper implements PlayerInventory {
/**
* Total number of item slots in the player inventory.
*/
public static final int PLAYER_INVENTORY_SIZE = 43; // 36 base inventory + 4 armor slots + 1 off hand + 2 hidden slots (body and saddle)
private int heldItemSlot;
/**
* Creates a dummy player inventory.
* @param base the inventory itself.
* @param heldItemSlot the currently held item slot, from 0 to 8.
*/
public DummyPlayerInventory(Inventory base, int heldItemSlot) {
super(base);
if (base.getSize() < PLAYER_INVENTORY_SIZE)
throw new IllegalArgumentException("base inventory should have a size of " + PLAYER_INVENTORY_SIZE + " (" + base.getSize() + " given).");
if (heldItemSlot < 0 || heldItemSlot > 8)
throw new IllegalArgumentException("heldItemSlot should be between 0 and 8 inclusive.");
this.heldItemSlot = heldItemSlot;
}
@Override
public @Nullable ItemStack @NotNull [] getStorageContents() {
return Arrays.copyOfRange(getContents(), 0, 36);
}
@Override
public @Nullable ItemStack @NotNull [] getArmorContents() {
return Arrays.copyOfRange(getContents(), 36, 40);
}
@Override
public void setArmorContents(@Nullable ItemStack[] items) {
this.setSlots(items, 36, 4);
}
@Override
public @Nullable ItemStack @NotNull [] getExtraContents() {
return Arrays.copyOfRange(getContents(), 40, getSize());
}
@Override
public void setExtraContents(@Nullable ItemStack[] items) {
this.setSlots(items, 40, getSize() - 40);
}
private void setSlots(ItemStack[] items, int baseSlot, int length) {
if (items == null) {
items = new ItemStack[length];
}
Preconditions.checkArgument(items.length <= length, "items.length must be < %s", length);
for (int i = 0; i < length; i++) {
if (i >= items.length) {
this.setItem(baseSlot + i, null);
} else {
this.setItem(baseSlot + i, items[i]);
}
}
}
@Override
public ItemStack getHelmet() {
return getItem(39);
}
@Override
public void setHelmet(@Nullable ItemStack helmet) {
setItem(39, helmet);
}
@Override
public ItemStack getChestplate() {
return getItem(38);
}
@Override
public void setChestplate(@Nullable ItemStack chestplate) {
setItem(38, chestplate);
}
@Override
public ItemStack getLeggings() {
return getItem(37);
}
@Override
public void setLeggings(@Nullable ItemStack leggings) {
setItem(37, leggings);
}
@Override
public ItemStack getBoots() {
return getItem(36);
}
@Override
public void setBoots(@Nullable ItemStack boots) {
setItem(36, boots);
}
/**
* Gets the item stack in the SADDLE {@link EquipmentSlot}.
* @return the SADDLE item stack.
*/
public ItemStack getSaddle() {
return getItem(42);
}
/**
* Puts the provided item stack in the SADDLE {@link EquipmentSlot}.
* @param saddle the item.
*/
public void setSaddle(@Nullable ItemStack saddle) {
setItem(42, saddle);
}
/**
* Gets the item stack in the BODY {@link EquipmentSlot}.
* @return the BODY item stack.
*/
public ItemStack getBody() {
return getItem(41);
}
/**
* Puts the provided item stack in the BODY {@link EquipmentSlot}.
* @param body the item.
*/
public void setBody(@Nullable ItemStack body) {
setItem(41, body);
}
@Override
public void setItem(EquipmentSlot slot, ItemStack item) {
Preconditions.checkArgument(slot != null, "slot must not be null");
switch (slot) {
case HAND -> this.setItemInMainHand(item);
case OFF_HAND -> this.setItemInOffHand(item);
case FEET -> this.setBoots(item);
case LEGS -> this.setLeggings(item);
case CHEST -> this.setChestplate(item);
case HEAD -> this.setHelmet(item);
case BODY -> this.setBody(item);
case SADDLE -> this.setSaddle(item);
}
}
@Override
public @NotNull ItemStack getItem(@NotNull EquipmentSlot slot) {
return switch (slot) {
case HAND -> this.getItemInMainHand();
case OFF_HAND -> this.getItemInOffHand();
case FEET -> Objects.requireNonNullElseGet(this.getBoots(), () -> new ItemStack(Material.AIR));
case LEGS -> Objects.requireNonNullElseGet(this.getLeggings(), () -> new ItemStack(Material.AIR));
case CHEST -> Objects.requireNonNullElseGet(this.getChestplate(), () -> new ItemStack(Material.AIR));
case HEAD -> Objects.requireNonNullElseGet(this.getHelmet(), () -> new ItemStack(Material.AIR));
case BODY -> Objects.requireNonNullElseGet(this.getBody(), () -> new ItemStack(Material.AIR)); // for horses/wolves armor
case SADDLE -> Objects.requireNonNullElseGet(this.getSaddle(), () -> new ItemStack(Material.AIR));
};
}
@Override
public @NotNull ItemStack getItemInMainHand() {
return Objects.requireNonNullElse(getItem(heldItemSlot), new ItemStack(Material.AIR));
}
@Override
public void setItemInMainHand(@Nullable ItemStack item) {
setItem(heldItemSlot, item);
}
@Override
public @NotNull ItemStack getItemInOffHand() {
return Objects.requireNonNullElse(getItem(40), new ItemStack(Material.AIR));
}
@Override
public void setItemInOffHand(@Nullable ItemStack item) {
setItem(40, item);
}
@Override
public @NotNull ItemStack getItemInHand() {
return getItemInMainHand();
}
@Override
public void setItemInHand(@Nullable ItemStack stack) {
setItemInMainHand(stack);
}
@Override
public int getHeldItemSlot() {
return heldItemSlot;
}
@Override
public void setHeldItemSlot(int slot) {
if (slot < 0 || slot > 8)
throw new IllegalArgumentException("Slot is not between 0 and 8 inclusive");
heldItemSlot = slot;
}
@Override
public @Nullable HumanEntity getHolder() {
return null;
}
}

View File

@@ -1,4 +1,4 @@
package fr.pandacube.lib.paper.util;
package fr.pandacube.lib.paper.inventory;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -13,12 +13,22 @@ import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
/**
* Wrapper for an {@link Inventory}.
* Can be overridden to add specific behaviour to some methods.
*/
public class InventoryWrapper implements Inventory {
private final Inventory base;
/**
* Creates a wrapper for the provided inventory.
* @param base the wrapped inventory. Cannot be null.
* @throws NullPointerException if the base inventory is null.
*/
public InventoryWrapper(Inventory base) {
this.base = base;
this.base = Objects.requireNonNull(base, "base inventory cannot be null.");
}

View File

@@ -0,0 +1,413 @@
package fr.pandacube.lib.paper.inventory;
import com.google.common.collect.Streams;
import fr.pandacube.lib.chat.Chat;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.datacomponent.DataComponentType.Valued;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ResolvableProfile;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import static fr.pandacube.lib.chat.ChatStatic.chatComponent;
/**
* A builder for {@link ItemStack}.
*/
public class ItemStackBuilder {
/**
* Create a builder with a clone of the provided ItemStack.
* <p>
* The returned builder will not alter the provided ItemStack.
* If you want to modify the ItemStack with the builder, please use {@link #wrap(ItemStack)}.
* @param base the original ItemStack.
* @return the builder
*/
public static ItemStackBuilder of(ItemStack base) {
return wrap(base.clone());
}
/**
* Create a builder of a new ItemStack with the specified Material.
* @param mat the material of the new built ItemStack
* @return the builder
*/
public static ItemStackBuilder of(Material mat) {
return wrap(new ItemStack(mat));
}
/**
* Create a builder that will alter the data of the provided ItemStack.
* <p>
* The {@link #build()} method of the returned builder will return the same instance
* of ItemStack as the parameter of this method.
* <p>
* To create a builder that doesn't modify the provided ItemStack, use {@link #of(ItemStack)}.
* @param stack the wrapped item stack.
* @return the builder
*/
public static ItemStackBuilder wrap(ItemStack stack) {
return new ItemStackBuilder(stack);
}
private final ItemStack stack;
private ItemStackBuilder(ItemStack base) {
stack = base;
}
/**
* Runs the provided updater on the {@link ItemMeta} instance of the built stack.
* @param metaUpdater the updater that will modify the meta.
* @return itself.
*/
public ItemStackBuilder meta(Consumer<ItemMeta> metaUpdater) {
return meta(metaUpdater, ItemMeta.class);
}
/**
* Runs the provided updater on the {@link ItemMeta} instance of the built stack.
* @param metaUpdater the updater that will modify the meta.
* @param metaType the type of the meta instance.
* @param <T> the type of item meta.
* @return itself.
*/
public <T extends ItemMeta> ItemStackBuilder meta(Consumer<T> metaUpdater, Class<T> metaType) {
stack.editMeta(metaType, metaUpdater);
return this;
}
/**
* Sets the amount of the built stack.
* @param a the new amount.
* @return itself.
*/
public ItemStackBuilder amount(int a) {
stack.setAmount(a);
return this;
}
/**
* Sets the display name of the item, directly passing to {@link ItemMeta#displayName(Component)}.
* @param displayName the new display name. Can be null to unset.
* @return itself.
*/
public ItemStackBuilder rawDisplayName(Component displayName) {
return meta(m -> m.displayName(displayName));
}
/**
* Sets the display name of the item, filtering to make default italic to false.
* @param displayName the new display name. Can be null to unset.
* @return itself.
*/
public ItemStackBuilder displayName(ComponentLike displayName) {
return rawDisplayName(displayName != null
? Chat.italicFalseIfNotSet(chatComponent(displayName)).asComponent()
: null);
}
/**
* Sets the lore of the item, directly passing to {@link ItemMeta#lore(List)}.
* @param lore the new lore. Can be null to unset.
* @return itself.
*/
public ItemStackBuilder rawLore(List<Component> lore) {
return meta(m -> m.lore(lore));
}
/**
* Sets the lore of the item, filtering to make default italic to false.
* @param lore the new lore. Can be null to unset.
* @return itself.
*/
public ItemStackBuilder lore(List<? extends ComponentLike> lore) {
if (lore != null) {
return rawLore(lore.stream()
.map(line -> Chat.italicFalseIfNotSet(chatComponent(line)).get())
.toList());
}
else
return rawLore(Collections.emptyList());
}
/**
* Adds new lore lines to the existing lore of the item.
* @param lores the added lore lines.
* @return itself.
*/
public ItemStackBuilder addLoreAfter(List<? extends ComponentLike> lores) {
if (lores != null) {
List<Component> baseLore = stack.getItemMeta().lore();
if (baseLore == null) baseLore = Collections.emptyList();
return rawLore(
Streams.concat(
baseLore.stream(),
lores.stream()
.map(line -> Chat.italicFalseIfNotSet(chatComponent(line)).get())
)
.toList());
}
else
return this;
}
/**
* Adds new lore lines to the existing lore of the item.
* @param lores the added lore lines.
* @return itself.
*/
public ItemStackBuilder addLoreAfter(ComponentLike... lores) {
if (lores == null || lores.length == 0)
return this;
return addLoreAfter(Arrays.asList(lores));
}
/**
* Enchant the item.
* Supports unsafe enchants.
* @param enchantment the enchantment.
* @param level the enchant level.
* @return itself.
*/
public ItemStackBuilder enchant(Enchantment enchantment, int level) {
return meta(m -> m.addEnchant(enchantment, level, true));
}
/**
* Adds the provided flags to the item.
* @param flags he flags to add.
* @return itself.
*/
public ItemStackBuilder flags(ItemFlag... flags) {
return flags(true, flags);
}
/**
* Adds or removes the provided flags to the item.
* @param add true to add, false to remove.
* @param flags he flags to add.
* @return itself.
*/
public ItemStackBuilder flags(boolean add, ItemFlag... flags) {
return add ? meta(m -> m.addItemFlags(flags))
: meta(m -> m.removeItemFlags(flags));
}
/**
* Hides the enchants from the tooltip of the item.
* Will set the {@link ItemFlag#HIDE_ENCHANTS} flag of the item.
* @return itself.
*/
public ItemStackBuilder hideEnchants() {
return hideEnchants(true);
}
/**
* Sets or unsets the {@link ItemFlag#HIDE_ENCHANTS} flag of the item.
* @param hide true to hide, false to show.
* @return itself.
*/
public ItemStackBuilder hideEnchants(boolean hide) {
return flags(hide, ItemFlag.HIDE_ENCHANTS);
}
/**
* Hides the attributes from the tooltip of the item.
* Will set the {@link ItemFlag#HIDE_ATTRIBUTES} flag of the item.
* @return itself.
*/
public ItemStackBuilder hideAttributes() {
return hideAttributes(true);
}
/**
* Sets or unsets the {@link ItemFlag#HIDE_ATTRIBUTES} flag of the item.
* @param hide true to hide, false to show.
* @return itself.
*/
public ItemStackBuilder hideAttributes(boolean hide) {
return flags(hide, ItemFlag.HIDE_ATTRIBUTES);
}
/**
* Apply the enchantment glint to the item, event if it's not enchant.
* @return itself.
*/
public ItemStackBuilder fakeEnchant() {
return fakeEnchant(true);
}
/**
* Sets the enchantment glint override to the item.
* @param apply true to enforce the enchantment glint, false to set to default.
* @return itself.
*/
public ItemStackBuilder fakeEnchant(boolean apply) {
return meta(m -> m.setEnchantmentGlintOverride(apply ? true : null));
}
/**
* Sets this item as unbreakable.
* @return itself.
*/
public ItemStackBuilder unbreakable() {
return unbreakable(true);
}
/**
* Sets the unbreakable status of this item.
* @param unbreakable the unbreakable status.
* @return itself.
*/
public ItemStackBuilder unbreakable(boolean unbreakable) {
return meta(m -> m.setUnbreakable(unbreakable));
}
/**
* Sets the damage value of this item.
* @param d the new damage value.
* @return itself.
*/
public ItemStackBuilder damage(int d) {
return meta(m -> m.setDamage(d), Damageable.class);
}
/**
* Sets a value for a data component of this item.
* @param dataType the data component type.
* @param dataValue the data component value.
* @return itself.
* @param <T> the data component API type.
*/
public <T> ItemStackBuilder data(Valued<T> dataType, T dataValue) {
stack.setData(dataType, dataValue);
return this;
}
/**
* Unset (set to empty) a value for a data component of this item.
* @param dataType the data component type.
* @return itself.
*/
public ItemStackBuilder unsetData(DataComponentType dataType) {
stack.unsetData(dataType);
return this;
}
/**
* Reset (act as default) a value for a data component of this item.
* @param dataType the data component type.
* @return itself.
*/
public ItemStackBuilder resetData(DataComponentType dataType) {
stack.resetData(dataType);
return this;
}
/**
* Sets the {@code can_break} data component to the provided list of {@link Material}.
* @param canBreak a list of {@link Material}.
* @return itself.
*/
public ItemStackBuilder canBreakMaterials(Collection<Material> canBreak) {
return canBreak(canBreak.stream().map(Material::getKey).toList());
}
/**
* Sets the {@code can_break} data component to the provided list of {@link NamespacedKey}.
* @param canBreak a list of block predicate. If empty, unsets the data component. If null, reset to default.
* @return itself.
*/
@SuppressWarnings("removal")
public ItemStackBuilder canBreak(Collection<NamespacedKey> canBreak) {
@SuppressWarnings("unchecked")
Collection<com.destroystokyo.paper.Namespaced> nsCanBreak = (Collection<com.destroystokyo.paper.Namespaced>) (Collection<?>) canBreak;
return meta(m -> m.setDestroyableKeys(nsCanBreak));
/*
if (canBreak == null)
return resetData(DataComponentTypes.CAN_BREAK);
else if (canBreak.isEmpty())
return unsetData(DataComponentTypes.CAN_BREAK);
else
return data(DataComponentTypes.CAN_BREAK, ItemAdventurePredicate.itemAdventurePredicate(canBreak));*/
}
/**
* Sets the {@code can_place_on} data component to the provided list of {@link Material}.
* @param canPlaceOn a list of {@link Material}.
* @return itself.
*/
public ItemStackBuilder canPlaceOnMaterials(Collection<Material> canPlaceOn) {
return canPlaceOn(canPlaceOn.stream().map(Material::getKey).toList());
}
/**
* Sets the {@code can_place_on} data component to the provided list of {@link NamespacedKey}.
* @param canPlaceOn a list of block predicate. If empty, unsets the data component. If null, reset to default.
* @return itself.
*/
@SuppressWarnings("removal")
public ItemStackBuilder canPlaceOn(Collection<NamespacedKey> canPlaceOn) {
@SuppressWarnings("unchecked")
Collection<com.destroystokyo.paper.Namespaced> nsCanPlaceOn = (Collection<com.destroystokyo.paper.Namespaced>) (Collection<?>) canPlaceOn;
return meta(m -> m.setPlaceableKeys(nsCanPlaceOn));
/* if (canPlaceOn == null)
return resetData(DataComponentTypes.CAN_PLACE_ON);
else if (canPlaceOn.isEmpty())
return unsetData(DataComponentTypes.CAN_PLACE_ON);
else
return data(DataComponentTypes.CAN_PLACE_ON, ItemAdventurePredicate.itemAdventurePredicate(canPlaceOn)); */
}
/**
* Sets the {@code profile} data component to the provided profile.
* @param profile the profile to use as the component value.
* @return itself.
*/
public ItemStackBuilder profile(ResolvableProfile profile) {
return data(DataComponentTypes.PROFILE, profile);
}
/**
* Build the {@link ItemStack}.
* @return the build item stack.
*/
public ItemStack build() {
return stack;
}
}

View File

@@ -0,0 +1,126 @@
package fr.pandacube.lib.paper.inventory;
import com.destroystokyo.paper.profile.ProfileProperty;
import io.papermc.paper.datacomponent.item.ResolvableProfile;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.Base64;
import java.util.regex.Pattern;
/**
* Represents some special mob heads, also support creating player skulls and custom skulls.
*/
public enum Skull {
/** Jungle wood arrow left. */
ARROW_LEFT("http://textures.minecraft.net/texture/3625902b389ed6c147574e422da8f8f361c8eb57e7631676a72777e7b1d"),
/** Jungle wood arrow right. */
ARROW_RIGHT("http://textures.minecraft.net/texture/d4be8aeec11849697adc6fd1f189b16642dff19f2955c05deaba68c9dff1be"),
/** Jungle wood arrow up. */
ARROW_UP("http://textures.minecraft.net/texture/88c0f37dec764d6e26b57aa8212572fbace5ee8f27f7b61c1fdaa47dd4c893"),
/** Jungle wood arrow down. */
ARROW_DOWN("http://textures.minecraft.net/texture/751ced2e647366f8f3ad2dfe415cca85651bfaf9739a95cd57b6f21cba053"),
/** Jungle wood question mark. */
QUESTION("http://textures.minecraft.net/texture/b4d7cc4dca986a53f1d6b52aaf376dc6acc73b8b287f42dc8fef5808bb5d76"),
/** Jungle wood exclamation mark. */
EXCLAMATION("http://textures.minecraft.net/texture/e869dc405a3155f281c16a3e8d9ff54afc1599153b4d9385c9b7bab88680f0");
private final String skinUrl;
Skull(String skinUrl) {
this.skinUrl = skinUrl;
}
/**
* Return the item based on this Skull enum.
* @return the item stack.
*/
public ItemStack get() {
return getFromSkinURL(skinUrl);
}
/**
* Return an item stack builder already containing the skull.
* @return an item stack builder already containing the skull.
*/
public ItemStackBuilder builder() {
return ItemStackBuilder.wrap(get());
}
/**
* Return a skull of a player based on their name.
*
* @param name player's name
* @return item stack
*/
public static ItemStack getFromPlayerName(String name) {
return getFromProfile(ResolvableProfile.resolvableProfile().name(name).build());
}
/**
* Return a skull that has a custom texture specified by url.
* @param url skin url.
* @return item stack
*/
public static ItemStack getFromSkinURL(String url) {
return getFromProfile(ResolvableProfile.resolvableProfile().addProperty(getTexturesProperty(url)).build());
}
private static ItemStack getFromProfile(ResolvableProfile profile) {
return ItemStackBuilder.of(Material.PLAYER_HEAD).profile(profile).build();
}
/**
* The URL prefix for all the player related textures (skin, cape)
*/
public static final String TEXTURE_URL_PREFIX = "http://textures.minecraft.net/texture/";
private static final Pattern textureIdMatcher = Pattern.compile("^[0-9a-fA-F]+$");
/**
* Generate the base64 value of the "textures" profile property, based on the provided skin url!
* @param skinURL the URL of the skin. The "https" will be replaced by "http" because this is the protocol used in
* the profile property url. If only the texture id part is provided, {@link #TEXTURE_URL_PREFIX} is
* prepended.
* @return the base64 encoded texture data.
*/
private static String encodeTextureBase64String(String skinURL) {
if (skinURL.startsWith("https://")) // secure url is not the url found in texture data (even if it actually works in the browser)
skinURL = "http://" + skinURL.substring("https://".length());
if (!skinURL.startsWith(TEXTURE_URL_PREFIX)) { // accept taking only the texture id part ()
if (textureIdMatcher.matcher(skinURL).matches())
skinURL = TEXTURE_URL_PREFIX + skinURL;
else
throw new IllegalArgumentException("Invalid skin URL. Must be from " + TEXTURE_URL_PREFIX + ".");
}
return Base64.getEncoder().encodeToString(String.format("{\"textures\":{\"SKIN\":{\"url\":\"%s\"}}}", skinURL).getBytes());
}
private static ProfileProperty getTexturesProperty(String skinURL) {
return new ProfileProperty("textures", encodeTextureBase64String(skinURL));
}
}

View File

@@ -23,7 +23,7 @@ import java.util.Map;
/**
* Gson adapter for ConfigurationSerializable, an interface implemented by several classes in the Bukkit API to ease
* serialization to YAML.
*
* <p>
* To not reinvent the wheel, this class uses the Bukkits Yaml API to convert the objects from/to json.
*/
/* package */ class ConfigurationSerializableAdapter implements JsonSerializer<ConfigurationSerializable>, JsonDeserializer<ConfigurationSerializable> {

View File

@@ -9,10 +9,13 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.Strictness;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.bind.TreeTypeAdapter;
import com.google.gson.reflect.TypeToken;
import net.kyori.adventure.key.Key;
import org.bukkit.Bukkit;
import org.bukkit.Registry;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.inventory.ItemStack;
@@ -28,16 +31,25 @@ import java.util.Map;
private static final TypeToken<Map<String, Object>> MAP_STR_OBJ_TYPE = new TypeToken<>() { };
/** Gson instance with no custom type adapter */
private static final Gson vanillaGson = new GsonBuilder().setLenient().create();
private static final Gson vanillaGson = new GsonBuilder().setStrictness(Strictness.LENIENT).create();
@Override
public ItemStack deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!(json instanceof JsonObject jsonObj))
throw new JsonParseException("Unable to deserialize a ConfigurationSerializable from the provided json structure.");
// if it contains both old and new data, delete the old one introduced for compatibility
if (jsonObj.has("DataVersion") || jsonObj.has("id")) {
jsonObj.remove("v");
jsonObj.remove("type");
}
// format used when using ConfigurationSerialization
if (jsonObj.has(ConfigurationSerialization.SERIALIZED_TYPE_KEY))
return context.deserialize(jsonObj, ConfigurationSerializable.class);
if (jsonObj.has("meta")
&& jsonObj.get("meta") instanceof JsonObject metaJson
&& !metaJson.has(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
@@ -47,6 +59,8 @@ import java.util.Map;
Map<String, Object> map = context.deserialize(jsonObj, MAP_STR_OBJ_TYPE.getType());
fixDeserializationVersion(map);
map.remove("meta");
ItemStack is = ItemStack.deserialize(map);
Class<? extends ItemMeta> metaClass = is.getItemMeta().getClass();

View File

@@ -14,4 +14,7 @@ public class PaperJson {
Json.registerTypeAdapterFactory(ItemStackAdapter.FACTORY);
Json.registerTypeAdapterFactory(ConfigurationSerializableAdapter.FACTORY);
}
private PaperJson() {}
}

View File

@@ -21,7 +21,7 @@ import net.kyori.adventure.bossbar.BossBar.Color;
import net.kyori.adventure.bossbar.BossBar.Overlay;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@@ -37,6 +37,7 @@ import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static fr.pandacube.lib.chat.ChatStatic.chat;
import static fr.pandacube.lib.chat.ChatStatic.failureText;
@@ -44,10 +45,17 @@ import static fr.pandacube.lib.chat.ChatStatic.infoText;
import static fr.pandacube.lib.chat.ChatStatic.successText;
import static fr.pandacube.lib.chat.ChatStatic.text;
/**
* Various tools to supervise the JVM RAM and the CPU usage of the main server thread.
*/
public class PerformanceAnalysisManager implements Listener {
private static PerformanceAnalysisManager instance;
/**
* Gets the instance of {@link PerformanceAnalysisManager}.
* @return the instance of {@link PerformanceAnalysisManager}.
*/
public static synchronized PerformanceAnalysisManager getInstance() {
if (instance == null)
instance = new PerformanceAnalysisManager();
@@ -76,14 +84,22 @@ public class PerformanceAnalysisManager implements Listener {
private final LinkedList<Long> interTPSDurations = new LinkedList<>();
/**
* The boss bar that shows in real time the CPU performance of the main server thread.
*/
public final AutoUpdatedBossBar tpsBar;
/**
* The boss bar that shows in real time the JVM RAM usage.
*/
public final AutoUpdatedBossBar memoryBar;
private final List<Player> barPlayers = new ArrayList<>();
private final List<BossBar> relatedBossBars = new ArrayList<>();
/**
* The gradient of color covering the common range of TPS values.
*/
public final ChatColorGradient tps1sGradient = new ChatColorGradient()
.add(0, NamedTextColor.BLACK)
.add(1, NamedTextColor.DARK_RED)
@@ -94,23 +110,7 @@ public class PerformanceAnalysisManager implements Listener {
.add(21, PandaTheme.CHAT_GREEN_1_NORMAL)
.add(26, NamedTextColor.BLUE);
public final ChatColorGradient tps10sGradient = new ChatColorGradient()
.add(0, NamedTextColor.DARK_RED)
.add(5, NamedTextColor.RED)
.add(10, NamedTextColor.GOLD)
.add(14, NamedTextColor.YELLOW)
.add(19, PandaTheme.CHAT_GREEN_1_NORMAL);
public final ChatColorGradient tps1mGradient = new ChatColorGradient()
.add(0, NamedTextColor.DARK_RED)
.add(8, NamedTextColor.RED)
.add(14, NamedTextColor.GOLD)
.add(17, NamedTextColor.YELLOW)
.add(19, PandaTheme.CHAT_GREEN_1_NORMAL);
public final ChatColorGradient memoryUsageGradient = new ChatColorGradient()
private final ChatColorGradient memoryUsageGradient = new ChatColorGradient()
.add(.60f, PandaTheme.CHAT_GREEN_1_NORMAL)
.add(.70f, NamedTextColor.YELLOW)
.add(.80f, NamedTextColor.GOLD)
@@ -132,10 +132,19 @@ public class PerformanceAnalysisManager implements Listener {
}
/**
* Tells if the provided players is seeing the performance boss bars.
* @param p the player to verify.
* @return true if the provided players is seeing the performance boss bars, false otherwise.
*/
public boolean barsContainsPlayer(Player p) {
return barPlayers.contains(p);
}
/**
* Shows the performance boss bars to the provided player.
* @param p the player.
*/
public synchronized void addPlayerToBars(Player p) {
barPlayers.add(p);
p.showBossBar(tpsBar.bar);
@@ -143,7 +152,11 @@ public class PerformanceAnalysisManager implements Listener {
for (BossBar bar : relatedBossBars)
p.showBossBar(bar);
}
/**
* Hides the performance boss bars from the provided player.
* @param p the player.
*/
public synchronized void removePlayerToBars(Player p) {
p.hideBossBar(tpsBar.bar);
p.hideBossBar(memoryBar.bar);
@@ -151,7 +164,11 @@ public class PerformanceAnalysisManager implements Listener {
p.hideBossBar(bar);
barPlayers.remove(p);
}
/**
* Show an additional boss bar to the players currently seeing the performance ones.
* @param bar the new bar to show.
*/
public synchronized void addBossBar(BossBar bar) {
if (relatedBossBars.contains(bar))
return;
@@ -159,7 +176,11 @@ public class PerformanceAnalysisManager implements Listener {
for (Player p : barPlayers)
p.showBossBar(bar);
}
/**
* Hides an additional boss bar from the players currently seeing the performance ones.
* @param bar the additional bar to hide.
*/
public synchronized void removeBossBar(BossBar bar) {
if (!relatedBossBars.contains(bar))
return;
@@ -167,8 +188,11 @@ public class PerformanceAnalysisManager implements Listener {
for (Player p : barPlayers)
p.hideBossBar(bar);
}
public synchronized void cancelInternalBossBar() {
/**
* De-initialize the performance analyzer.
*/
public synchronized void deinit() {
tpsBar.cancel();
memoryBar.cancel();
}
@@ -178,7 +202,7 @@ public class PerformanceAnalysisManager implements Listener {
@EventHandler
public synchronized void onTickStart(ServerTickStartEvent event) {
synchronized void onTickStart(ServerTickStartEvent event) {
tickStartNanoTime = System.nanoTime();
tickStartCPUTime = threadMXBean.isThreadCpuTimeSupported() ? threadMXBean.getCurrentThreadCpuTime() : 0;
@@ -186,7 +210,7 @@ public class PerformanceAnalysisManager implements Listener {
}
@EventHandler
public synchronized void onTickEnd(ServerTickEndEvent event) {
synchronized void onTickEnd(ServerTickEndEvent event) {
tickEndNanoTime = System.nanoTime();
long tickEndCPUTime = threadMXBean.isThreadCpuTimeSupported() ? threadMXBean.getCurrentThreadCpuTime() : 0;
@@ -213,7 +237,7 @@ public class PerformanceAnalysisManager implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
void onPlayerJoin(PlayerJoinEvent event) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
@SuppressWarnings("unchecked")
AbstractPlayerManager<PaperOnlinePlayer, PaperOffPlayer> playerManager = (AbstractPlayerManager<PaperOnlinePlayer, PaperOffPlayer>) AbstractPlayerManager.getInstance();
@@ -233,7 +257,7 @@ public class PerformanceAnalysisManager implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
void onPlayerQuit(PlayerQuitEvent event) {
removePlayerToBars(event.getPlayer());
}
@@ -295,22 +319,25 @@ public class PerformanceAnalysisManager implements Listener {
int[] tpsHistory = getTPSHistory();
// keep the legacy text when generating the bar to save space when converting to component
StringBuilder s = new StringBuilder();
TextColor prevC = null;
List<Pair<TextColor, AtomicInteger>> barComponents = new ArrayList<>(60);
for (int i = 58; i >= 0; i--) {
int t = tpsHistory[i];
TextColor newC = tps1sGradient.pickColorAt(t);
if (!newC.equals(prevC)) {
s.append(text("|").color(newC).getLegacyText());
prevC = newC;
if (barComponents.isEmpty() || !newC.equals(barComponents.get(barComponents.size() - 1).getKey())) {
barComponents.add(Pair.of(newC, new AtomicInteger(1)));
}
else {
s.append("|");
barComponents.get(barComponents.size() - 1).getValue().incrementAndGet();
}
}
Chat history = chat();
barComponents.forEach(p -> {
history.then(text("|".repeat(p.getValue().get()))
.color(p.getKey())
);
});
// tick time measurement
@@ -333,7 +360,7 @@ public class PerformanceAnalysisManager implements Listener {
: (avgTickCPUTime1s < 50) ? NamedTextColor.RED
: NamedTextColor.DARK_RED;
float avgTickWaitingTime1s = avgTickDuration1s - avgTickCPUTime1s;
float avgTickWaitingTime1s = Math.max(0, avgTickDuration1s - avgTickCPUTime1s);
TextColor avgTickWaitingTime1sColor = (avgTickDuration1s < 46 || avgTickWaitingTime1s < 20) ? PandaTheme.CHAT_GREEN_1_NORMAL
: (avgTickWaitingTime1s < 30) ? NamedTextColor.YELLOW
: (avgTickWaitingTime1s < 40) ? NamedTextColor.GOLD
@@ -349,16 +376,16 @@ public class PerformanceAnalysisManager implements Listener {
: NamedTextColor.RED;
timings = text("(R/W/S:")
.then(text(Math.round(avgTickCPUTime1s)).color(avgTickCPUTime1sColor))
.then(text("%02d".formatted(Math.round(avgTickCPUTime1s))).color(avgTickCPUTime1sColor))
.thenText("/")
.then(text(Math.round(avgTickWaitingTime1s)).color(avgTickWaitingTime1sColor))
.then(text("%02d".formatted(Math.round(avgTickWaitingTime1s))).color(avgTickWaitingTime1sColor))
.thenText("/")
.then(text(Math.round(avgInterTickDuration1s)).color(avgInterTickDuration1sColor))
.then(text("%02d".formatted(Math.round(avgInterTickDuration1s))).color(avgInterTickDuration1sColor))
.thenText("ms)");
}
title = infoText("TPS [")
.thenLegacyText(s.toString())
.then(history)
.thenText("] ")
.then(text(tps1sDisplay + "/" + getTargetTickRate() + " ").color(tps1sGradient.pickColorAt(tps1s)))
.then(timings);
@@ -375,28 +402,34 @@ public class PerformanceAnalysisManager implements Listener {
}
private Chat alteredTPSTitle = null;
/**
* Temporary change the title of the TPS boss bar.
* @param title the title override. null to restore to the normal TPS title.
*/
public synchronized void setAlteredTPSTitle(Chat title) {
alteredTPSTitle = title;
}
// special case where the getTPS method always returns a whole number when retrieving the TPS for 1 sec
/**
* Gets the number of tick in the last second.
* @return the number of tick in the last second.
*/
public int getTPS1s() {
return (int) getTPS(1_000);
}
/**
*
* @param nbTicks number of ticks when the avg value is computed from history
* @return the avg number of TPS in the interval
*/
public synchronized float getAvgNano(List<Long> data, int nbTicks) {
private synchronized float getAvgNano(List<Long> data, int nbTicks) {
if (data.isEmpty())
return 0;
@@ -410,7 +443,7 @@ public class PerformanceAnalysisManager implements Listener {
}
/**
*
* Gets the average number of tick per second in the n last milliseconds.
* @param nbMillis number of milliseconds when the avg TPS is computed from history
* @return the avg number of TPS in the interval
*/
@@ -429,8 +462,12 @@ public class PerformanceAnalysisManager implements Listener {
return count * (1000 / (float) nbMillis);
}
/**
* Gets the history of TPS performance.
* @return an array of TPS values from the last minute. The value at 0 is in the last second (current second on the clock - 1), the value at index 1 is now - 2, ...
*/
public synchronized int[] getTPSHistory() {
int[] history = new int[60];
@@ -448,15 +485,22 @@ public class PerformanceAnalysisManager implements Listener {
}
/**
* Gets the current server's target tick rate.
* Usually 20 but the server can be configured to tick at a different rate.
* @return the current server's target tick rate.
*/
public static int getTargetTickRate() {
return Math.round(Bukkit.getServerTickManager().getTickRate());
}
/**
* Runs the garbage collector on the server.
* Depending on the server load and the used memory, this can freeze the server for a second.
* @param sender the command sender that triggers the garbage collector. Can be null (the report will be sent to the
* console)
*/
public static void gc(CommandSender sender) {
long t1 = System.currentTimeMillis();
long alloc1 = Runtime.getRuntime().totalMemory();
@@ -478,7 +522,7 @@ public class PerformanceAnalysisManager implements Listener {
Log.info(finalMessage.getLegacyText());
}
public static String displayRound10(double val) {
private static String displayRound10(double val) {
long v = (long) Math.ceil(val * 10);
return "" + (v / 10f);
}

View File

@@ -1,11 +1,11 @@
package fr.pandacube.lib.paper.players;
import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProblemReporter;
import fr.pandacube.lib.paper.world.PrimaryWorlds;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.NbtIo;
import fr.pandacube.lib.paper.util.PlayerDataWrapper;
import fr.pandacube.lib.paper.util.PlayerDataWrapper.PlayerDataLoadException;
import fr.pandacube.lib.paper.players.PlayerDataWrapper.PlayerDataLoadException;
import fr.pandacube.lib.paper.world.WorldUtil;
import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
@@ -116,26 +116,31 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
* Player config
*/
@SuppressWarnings("RedundantThrows") // may be thrown by concrete implementation
@Override
default String getConfig(String key) throws Exception {
return PaperPlayerConfigStorage.get(getUniqueId(), key);
}
@SuppressWarnings("RedundantThrows") // may be thrown by concrete implementation
@Override
default String getConfig(String key, String deflt) throws Exception {
return PaperPlayerConfigStorage.get(getUniqueId(), key, deflt);
}
@SuppressWarnings("RedundantThrows") // may be thrown by concrete implementation
@Override
default void setConfig(String key, String value) throws Exception {
PaperPlayerConfigStorage.set(getUniqueId(), key, value);
}
@SuppressWarnings("RedundantThrows") // may be thrown by concrete implementation
@Override
default void updateConfig(String key, String deflt, UnaryOperator<String> updater) throws Exception {
PaperPlayerConfigStorage.update(getUniqueId(), key, deflt, updater);
}
@SuppressWarnings("RedundantThrows") // may be thrown by concrete implementation
@Override
default void unsetConfig(String key) throws Exception {
PaperPlayerConfigStorage.unset(getUniqueId(), key);
@@ -162,7 +167,7 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
.getServer()
.getPlayerList()
.playerIo()
.load(getName(), getUniqueId().toString()).orElse(null);
.load(getName(), getUniqueId().toString(), ProblemReporter.DISCARDING()).orElse(null);
} catch (Exception|LinkageError e) {
throw new PlayerDataLoadException(getName(), getUniqueId(), e);
}

View File

@@ -3,7 +3,7 @@ package fr.pandacube.lib.paper.players;
import com.destroystokyo.paper.ClientOption;
import com.destroystokyo.paper.ClientOption.ChatVisibility;
import com.destroystokyo.paper.SkinParts;
import fr.pandacube.lib.paper.players.PlayerNonPersistentConfig.Expiration;
import fr.pandacube.lib.paper.players.PlayerNonPersistentConfig.ExpirationPolicy;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftPlayer;
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
@@ -170,7 +170,7 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
@Override
public boolean isChatFullyVisible() {
ChatVisibility v = getChatVisibility();
return v == ChatVisibility.FULL || v == ChatVisibility.UNKNOWN;
return v == ChatVisibility.FULL;
}
@Override
@@ -283,7 +283,7 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
* @param relZ the relative z coordinate.
*/
default void teleportRelatively(float relX, float relY, float relZ) {
getBukkitPlayer().teleport(getBukkitPlayer().getLocation().add(relX, relY, relZ), Relative.X, Relative.Y, Relative.Z, Relative.YAW, Relative.PITCH);
getBukkitPlayer().teleport(getBukkitPlayer().getLocation().add(relX, relY, relZ), Relative.VELOCITY_X, Relative.VELOCITY_Y, Relative.VELOCITY_Z, Relative.VELOCITY_ROTATION);
}
/**
@@ -291,7 +291,7 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
* @param destination the destination.
*/
default void teleportRelatively(Location destination) {
getBukkitPlayer().teleport(destination, Relative.X, Relative.Y, Relative.Z, Relative.YAW, Relative.PITCH);
getBukkitPlayer().teleport(destination, Relative.VELOCITY_X, Relative.VELOCITY_Y, Relative.VELOCITY_Z, Relative.VELOCITY_ROTATION);
}
@@ -304,18 +304,39 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
* Player config
*/
/**
* Gets the non-persistent value of the provided configuration key of this player.
* @param key the configuration key.
* @return the value of the configuration, or null if the configuration is not set.
*/
default String getNonPersistentConfig(String key) {
return PlayerNonPersistentConfig.getData(getUniqueId(), key);
}
/**
* Gets the non-persistent value of the provided configuration key of this player.
* @param key the configuration key.
* @param deflt the default value if the configuration is not set.
* @return the value of the configuration, or {@code deflt} if the configuration is not set.
*/
default String getNonPersistentConfig(String key, String deflt) {
return PlayerNonPersistentConfig.getData(getUniqueId(), key);
}
default void setNonPersistentConfig(String key, String value, Expiration expiration) {
PlayerNonPersistentConfig.setData(getUniqueId(), key, value, expiration);
/**
* Sets the non-persistent value of the provided configuration key for this player.
* @param key the configuration key to set.
* @param value the new value.
* @param expirationPolicy the expiration policy.
*/
default void setNonPersistentConfig(String key, String value, ExpirationPolicy expirationPolicy) {
PlayerNonPersistentConfig.setData(getUniqueId(), key, value, expirationPolicy);
}
/**
* Unsets the non-persistent value of the provided configuration key for this player.
* @param key the configuration key to update.
*/
default void unsetNonPersistentConfig(String key) {
PlayerNonPersistentConfig.unsetData(getUniqueId(), key);
}

View File

@@ -16,6 +16,10 @@ import java.util.UUID;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
/**
* Provides rudimentary player data storage using a file in the plugin configuration.
* The file is loaded on the first access, and is auto-saved if needed every 30 seconds.
*/
public class PaperPlayerConfigStorage {
static final File storageFile = new File(PandaLibPaper.getPlugin().getDataFolder(), "playerdata.yml");
@@ -77,6 +81,8 @@ public class PaperPlayerConfigStorage {
private static synchronized void save() {
if (!changed)
return;
YamlConfiguration config = new YamlConfiguration();
for (UUID pId : playerSortedData.keySet()) {
String pIdStr = pId.toString();
@@ -109,11 +115,17 @@ public class PaperPlayerConfigStorage {
}
public static synchronized void set(UUID player, String key, String newValue) {
/**
* Sets the value of the provided configuration key for the player.
* @param player the player.
* @param key the configuration key to set.
* @param value the new value.
*/
public static synchronized void set(UUID player, String key, String value) {
initIfNeeded();
ConfigKey cKey = new ConfigKey(player, key);
ConfigEntry e = data.get(cKey);
if (e != null && newValue == null) { // delete
if (e != null && value == null) { // delete
data.remove(cKey);
if (playerSortedData.containsKey(player))
playerSortedData.get(player).remove(e);
@@ -121,50 +133,91 @@ public class PaperPlayerConfigStorage {
keySortedData.get(key).remove(e);
changed = true;
}
else if (e == null && newValue != null) { // create
create(player, key, newValue);
else if (e == null && value != null) { // create
create(player, key, value);
changed = true;
}
else if (e != null && !newValue.equals(e.value)) { // update
e.value = newValue;
else if (e != null && !value.equals(e.value)) { // update
e.value = value;
changed = true;
}
}
/**
* Gets the value of the provided configuration key of the player.
* @param player the player.
* @param key the configuration key.
* @return the value of the configuration, or null if the configuration is not set.
*/
public static synchronized String get(UUID player, String key) {
initIfNeeded();
ConfigEntry e = data.get(new ConfigKey(player, key));
return e != null ? e.value : null;
}
public static String get(UUID p, String k, String deflt) {
String value = get(p, k);
/**
* Gets the value of the provided configuration key of the player.
* @param player the player.
* @param key the configuration key.
* @param deflt the default value if the configuration is not set.
* @return the value of the configuration, or {@code deflt} if the configuration is not set.
*/
public static String get(UUID player, String key, String deflt) {
String value = get(player, key);
return value == null ? deflt : value;
}
public static synchronized void update(UUID p, String k, String deflt, UnaryOperator<String> updater) {
String oldValue = get(p, k, deflt);
set(p, k, updater.apply(oldValue));
/**
* Updates the value of the provided configuration key for the player, using the provided updater.
* @param player the player.
* @param key the configuration key to update.
* @param deflt the default value to use if the configuration is not already set.
* @param updater the unary operator to use to update th value. The old value is used as the parameter of the updater,
* and it returns the new value of the configuration.
*/
public static synchronized void update(UUID player, String key, String deflt, UnaryOperator<String> updater) {
String oldValue = get(player, key, deflt);
set(player, key, updater.apply(oldValue));
}
public static void unset(UUID p, String k) {
set(p, k, null);
/**
* Unsets the value of the provided configuration key for the player.
* @param player the player.
* @param key the configuration key to update.
*/
public static void unset(UUID player, String key) {
set(player, key, null);
}
public static LinkedHashSet<ConfigEntry> getAllFromPlayer(UUID p) {
/**
* Gets all the config key-value pairs of the provided player.
* @param player the player.
* @return all the config key-value pairs of the provided player.
*/
public static LinkedHashSet<ConfigEntry> getAllFromPlayer(UUID player) {
initIfNeeded();
return new LinkedHashSet<>(playerSortedData.getOrDefault(p, new LinkedHashSet<>()));
return new LinkedHashSet<>(playerSortedData.getOrDefault(player, new LinkedHashSet<>()));
}
/**
* Gets all the config key-value pairs of all players that have the provided key.
* @param key the key.
* @return all the config key-value pairs of all players that have the provided key.
*/
public static LinkedHashSet<ConfigEntry> getAllWithKeys(String key) {
initIfNeeded();
return new LinkedHashSet<>(keySortedData.getOrDefault(key, new LinkedHashSet<>()));
}
public static LinkedHashSet<ConfigEntry> getAllWithKeyValue(String k, String v) {
/**
* Gets all the config key-value pairs of all players that have the provided key AND value.
* @param key the key.
* @param v the value.
* @return all the config key-value pairs of all players that have the provided key AND value.
*/
public static LinkedHashSet<ConfigEntry> getAllWithKeyValue(String key, String v) {
initIfNeeded();
return getAllWithKeys(k).stream()
return getAllWithKeys(key).stream()
.filter(c -> c.value.equals(v))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
@@ -173,25 +226,46 @@ public class PaperPlayerConfigStorage {
private record ConfigKey(UUID playerId, String key) { }
/**
* Class holding the playerId-key-value triplet.
*/
public static class ConfigEntry {
private final UUID playerId;
private final String key;
private String value;
/**
* Creates a new {@link ConfigEntry}.
* @param playerId the player id.
* @param key the key.
* @param value the value.
*/
private ConfigEntry(UUID playerId, String key, String value) {
this.playerId = playerId;
this.key = key;
this.value = value;
}
/**
* Gets the player id.
* @return the player id.
*/
public UUID getPlayerId() {
return playerId;
}
/**
* Gets the config key.
* @return the config key.
*/
public String getKey() {
return key;
}
/**
* Gets the config value.
* @return the config value.
*/
public String getValue() {
return value;
}
@@ -208,4 +282,7 @@ public class PaperPlayerConfigStorage {
&& Objects.equals(key, o.key);
}
}
private PaperPlayerConfigStorage() {}
}

View File

@@ -0,0 +1,237 @@
package fr.pandacube.lib.paper.players;
import fr.pandacube.lib.paper.inventory.DummyPlayerInventory;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftItemStack;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProblemReporter;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStackWithSlot;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.TagValueInput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.TagValueOutput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ValueInput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ValueOutputTypedOutputList;
import fr.pandacube.lib.paper.util.ExperienceUtil;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import org.bukkit.Bukkit;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.TreeMap;
import java.util.UUID;
import java.util.function.IntUnaryOperator;
/**
* A wrapper to easily manipulate the player data file.
*
* @param data The NBT data structure as it is stored in the player file.
*/
public record PlayerDataWrapper(CompoundTag data) {
/**
* Creates a new wrapper for the provided player data.
* @param data the NBT data to wrap.
*/
public PlayerDataWrapper(CompoundTag data) {
this.data = data == null ? new CompoundTag() : data;
}
/**
* Gets a snapshot of the inventory of this player.
* If the inventory is modified, the {@link #setInventory(PlayerInventory)} method should be called to update the
* data in this wrapper.
* @return the player inventory.
*/
public PlayerInventory getInventory() {
return new DummyPlayerInventory(
getBukkitInventory("Inventory", InventoryType.PLAYER, this::fromNBTtoBukkitInventorySlot),
getHeldItemSlot());
}
private int fromNBTtoBukkitInventorySlot(int nbtSlot) {
// cat nbEl NBTSlot bukkitSlot NBT->Bukkit
// items 36 0-35 ==
// armor 4 starts at 100 36-39 -100 + 36
// offhand 1 starts at 150 40 -150 + 40
if (nbtSlot >= 0 && nbtSlot < 36) { // regular inventory slots
return nbtSlot;
}
if (nbtSlot >= 100 && nbtSlot < 104) { // armor slots
return nbtSlot - 100 + 36;
}
if (nbtSlot == 150) { // second hand
return 40;
}
throw new IllegalArgumentException("Unrecognized NBT player inventory slot " + nbtSlot);
}
/**
* Sets the player inventory to the content of the provided one.
* The internal data of this wrapper will be updated.
* @param inv the inventory to store in this player data in place of the old one.
*/
public void setInventory(PlayerInventory inv) {
setBukkitInventory("Inventory", inv, this::fromBukkitToNBTInventorySlot);
setHeldItemSlot(inv.getHeldItemSlot());
}
private int fromBukkitToNBTInventorySlot(int bukkitSlot) {
if (bukkitSlot >= 0 && bukkitSlot < 36) { // regular inventory slots
return bukkitSlot;
}
if (bukkitSlot >= 36 && bukkitSlot < 40) { // armor slots
return bukkitSlot + 100 - 36;
}
if (bukkitSlot == 40) { // second hand
return 150;
}
throw new IllegalArgumentException("Unrecognized Bukkit player inventory slot " + bukkitSlot);
}
/**
* Gets a snapshot of the enderchest of this player.
* If the enderchest is modified, the {@link #setEnderChest(Inventory)} method should be called to update the
* data in this wrapper.
* @return the player enderchest.
*/
public Inventory getEnderChest() {
return getBukkitInventory("EnderItems", InventoryType.ENDER_CHEST, IntUnaryOperator.identity());
}
/**
* Sets the player enderchest to the content of the provided one.
* The internal data of this wrapper will be updated.
* @param inv the enderchest content to store in this player data in place of the old enderchest.
*/
public void setEnderChest(Inventory inv) {
setBukkitInventory("EnderItems", inv, IntUnaryOperator.identity());
}
private Inventory getBukkitInventory(String nbtKey, InventoryType bukkitType, IntUnaryOperator nbtToBukkitSlotConverter) {
Map<Integer, ItemStack> stacks = getRawInventoryContent(nbtKey);
Inventory inv = Bukkit.createInventory(null, bukkitType);
if (stacks.isEmpty())
return inv;
for (Entry<Integer, ItemStack> is : stacks.entrySet()) {
inv.setItem(nbtToBukkitSlotConverter.applyAsInt(is.getKey()), is.getValue());
}
return inv;
}
private Map<Integer, ItemStack> getRawInventoryContent(String key) {
ValueInput vi = TagValueInput.createGlobal(ProblemReporter.DISCARDING(), data);
Iterable<?> listNMSItemStackWithSlot = ReflectWrapper.unwrap(vi.listOrEmpty(key, ItemStackWithSlot.CODEC()));
Map<Integer, ItemStack> stacks = new TreeMap<>();
for (Object nmsISWS : listNMSItemStackWithSlot) {
ItemStackWithSlot isws = ReflectWrapper.wrap(nmsISWS, ItemStackWithSlot.class);
int nbtSlot = isws.slot() & 255;
Optional.of(isws.stack())
.map(nms -> filterStack(CraftItemStack.asCraftMirror(nms)))
.ifPresent(is -> stacks.put(nbtSlot, is));
}
return stacks;
}
private void setBukkitInventory(String nbtKey, Inventory inv, IntUnaryOperator bukkitToNBTSlotConverter) {
Map<Integer, ItemStack> stacks = new TreeMap<>();
if (inv == null) {
setRawInventoryContent(nbtKey, stacks);
return;
}
for (int bukkitSlot = 0; bukkitSlot < inv.getSize(); bukkitSlot++) {
ItemStack is = filterStack(inv.getItem(bukkitSlot));
if (is == null)
continue;
int nbtSlot = bukkitToNBTSlotConverter.applyAsInt(bukkitSlot);
stacks.put(nbtSlot, is);
}
setRawInventoryContent(nbtKey, stacks);
}
private void setRawInventoryContent(String key, Map<Integer, ItemStack> stacks) {
TagValueOutput vo = TagValueOutput.createWrappingGlobal(ProblemReporter.DISCARDING(), data);
ValueOutputTypedOutputList listNMSItemStackWithSlot = vo.list(key, ItemStackWithSlot.CODEC());
for (Entry<Integer, ItemStack> is : stacks.entrySet()) {
ItemStack stack = filterStack(is.getValue());
if (stack == null)
continue;
listNMSItemStackWithSlot.add(ReflectWrapper.unwrap(new ItemStackWithSlot(is.getKey(), CraftItemStack.asNMSCopy(is.getValue()))));
}
}
private ItemStack filterStack(ItemStack is) {
return is == null || is.isEmpty() || is.getAmount() <= 0 ? null : is;
}
private int getHeldItemSlot() {
return data.getInt("SelectedItemSlot").orElse(0);
}
private void setHeldItemSlot(int slot) {
data.putInt("SelectedItemSlot", slot);
}
/**
* Gets the score of the player, as stored in the data with the key {@code Score}.
* @return the value of Score.
*/
public int getScore() {
return data.getInt("Score").orElse(0);
}
/**
* Sets the score of the player, as stored in the data with the key {@code Score}.
* @param score the value of Score to set.
*/
public void setScore(int score) {
data.putInt("Score", score);
}
/**
* Gets the total experience of the player, as stored in the data with the key {@code XpTotal}.
* @return the value of XpTotal.
*/
public int getTotalExperience() {
return data.getInt("XpTotal").orElse(0);
}
/**
* Sets the total experience of the player, as stored in the data with the key {@code XpTotal}.
* @param xp the value of XpTotal to set.
*/
public void setTotalExperience(int xp) {
data.putInt("XpTotal", xp);
double levelAndExp = ExperienceUtil.getLevelFromExp(xp);
int level = (int) levelAndExp;
double expProgress = levelAndExp - level;
data.putInt("XpLevel", level);
data.putFloat("XpP", (float) expProgress);
}
/**
* Thrown to indicate that an error occurred while loading the data of the player from the file.
*/
public static class PlayerDataLoadException extends RuntimeException {
/* package */ PlayerDataLoadException(String playerName, UUID playerId, Throwable cause) {
super("Unable to load data of player " + playerName + " (" + playerId + ")", cause);
}
}
}

View File

@@ -12,6 +12,9 @@ import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/**
* Handles the player related configuration that is not persisted to disk.
*/
public class PlayerNonPersistentConfig {
private static final Map<UUID, Map<String, ConfigEntry>> data = new HashMap<>();
@@ -22,34 +25,58 @@ public class PlayerNonPersistentConfig {
}
public static void setData(UUID playerId, String key, String value, Expiration expiration) {
data.computeIfAbsent(Objects.requireNonNull(playerId, "playerId"), pp -> new HashMap<>())
/**
* Sets the value of the provided configuration key for the player.
* @param player the player.
* @param key the configuration key to set.
* @param value the new value.
* @param expirationPolicy the expiration policy for this config. If the config key already exists for this player. the expiration will be overridden.
*/
public static void setData(UUID player, String key, String value, ExpirationPolicy expirationPolicy) {
data.computeIfAbsent(Objects.requireNonNull(player, "playerId"), pp -> new HashMap<>())
.put(Objects.requireNonNull(key, "key"),
new ConfigEntry(Objects.requireNonNull(value, "value"),
Objects.requireNonNull(expiration, "expiration")
Objects.requireNonNull(expirationPolicy, "expiration")
)
);
}
public static void unsetData(UUID playerId, String key) {
data.getOrDefault(Objects.requireNonNull(playerId, "playerId"), new HashMap<>())
/**
* Unsets the value of the provided configuration key for the player.
* @param player the player.
* @param key the configuration key to update.
*/
public static void unsetData(UUID player, String key) {
data.getOrDefault(Objects.requireNonNull(player, "playerId"), new HashMap<>())
.remove(Objects.requireNonNull(key, "key"));
}
public static String getData(UUID playerId, String key) {
Map<String, ConfigEntry> playerData = data.getOrDefault(Objects.requireNonNull(playerId, "playerId"), new HashMap<>());
/**
* Gets the value of the provided configuration key of the player.
* @param player the player.
* @param key the configuration key.
* @return the value of the configuration, or {@code deflt} if the configuration is not set.
*/
public static String getData(UUID player, String key) {
Map<String, ConfigEntry> playerData = data.getOrDefault(Objects.requireNonNull(player, "playerId"), new HashMap<>());
ConfigEntry ce = playerData.get(Objects.requireNonNull(key, "key"));
if (ce == null)
return null;
if (!ce.expiration.valid(playerId, key)) {
if (!ce.expirationPolicy.valid(player, key)) {
playerData.remove(key);
return null;
}
return ce.value;
}
public static boolean isDataSet(UUID playerId, String key) {
return getData(playerId, key) != null;
/**
* Tells if the provided config key is set for the player.
* @param player the player.
* @param key the configuration key.
* @return true if the value is set, false otherwise.
*/
public static boolean isDataSet(UUID player, String key) {
return getData(player, key) != null;
}
@@ -63,34 +90,76 @@ public class PlayerNonPersistentConfig {
private record ConfigEntry(String value, Expiration expiration) { }
private record ConfigEntry(String value, ExpirationPolicy expirationPolicy) { }
/**
* Super class for all expiration policies.
*/
public static abstract class ExpirationPolicy {
/**
* Creates an expiration policy.
*/
public ExpirationPolicy() {}
public static abstract class Expiration {
/**
* Tests if the associated configuration is still valid (not expired).
* @param player the player.
* @param key the configuration key.
* @return true if the associated configuration is still valid, false otherwise.
*/
abstract boolean valid(UUID player, String key);
}
public static class ExpiresLogout extends Expiration {
/**
* Expiration policy for a config that expires when the player logs out.
*/
public static class ExpiresLogout extends ExpirationPolicy {
/**
* Creates a logout expiration policy.
*/
public ExpiresLogout() {
super();
}
@Override
protected boolean valid(UUID player, String key) {
return Bukkit.getPlayer(player) != null; // should not be call if player reconnects because it is removed on player quit
}
}
public static class ExpiresTick extends Expiration {
/**
* Expiration policy for a config that expires after a certain amount of game tick.
*/
public static class ExpiresTick extends ExpirationPolicy {
final long expirationTick;
/**
* Creates a delay expiration policy.
* @param expirationDelayTick the number of tick after which the config will expire. If 0, will expire immediately ; 1 to expire on the next tick.
*/
public ExpiresTick(long expirationDelayTick) {
expirationTick = tick + expirationDelayTick;
}
@Override
protected boolean valid(UUID player, String key) {
return tick < expirationTick;
}
}
public static class ExpiresServerStop extends Expiration {
/**
* Expiration policy for a config that expires when the server stops.
*/
public static class ExpiresServerStop extends ExpirationPolicy {
/**
* Creates a server stop expiration policy.
*/
public ExpiresServerStop() {
super();
}
@Override
protected boolean valid(UUID player, String key) {
return true;
}
@@ -103,7 +172,7 @@ public class PlayerNonPersistentConfig {
private static class ConfigListeners implements Listener {
public ConfigListeners() {
private ConfigListeners() {
Bukkit.getPluginManager().registerEvents(this, PandaLibPaper.getPlugin());
}
@@ -111,7 +180,7 @@ public class PlayerNonPersistentConfig {
public void onPlayerQuit(PlayerQuitEvent event) {
data.getOrDefault(event.getPlayer().getUniqueId(), new HashMap<>())
.entrySet()
.removeIf(e -> e.getValue().expiration instanceof ExpiresLogout);
.removeIf(e -> e.getValue().expirationPolicy instanceof ExpiresLogout);
}
@EventHandler
@@ -119,4 +188,9 @@ public class PlayerNonPersistentConfig {
tick++;
}
}
private PlayerNonPersistentConfig() {}
}

View File

@@ -23,4 +23,6 @@ public class OBCReflect {
return Reflect.ofClass(CRAFTBUKKIT_PACKAGE + "." + obcClass);
}
private OBCReflect() { }
}

View File

@@ -14,9 +14,7 @@ import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.VanillaCommandWrapper;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCDataConverter;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCDataType;
import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCTypeRegistry;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.DetectedVersion;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.SharedConstants;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.WorldVersion;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.CommandSourceStack;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Commands;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Coordinates;
@@ -54,17 +52,25 @@ import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerGamePacketL
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerLevel;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerPlayer;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.Settings;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProblemReporter;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProgressListener;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.AABB;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ChunkPos;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ChunkStorage;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.DataVersion;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Entity;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStackWithSlot;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Level;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.MapItemSavedData;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.PlayerDataStorage;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.RegionFileStorage;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.SavedData;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.TagValueInput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.TagValueOutput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ValueInput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ValueInputTypedInputList;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ValueOutput;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ValueOutputTypedOutputList;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Vec3;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.VoxelShape;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.block.BambooStalkBlock;
@@ -72,13 +78,13 @@ import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.block.Block;
import fr.pandacube.lib.paper.reflect.wrapper.netty.ByteBuf;
import fr.pandacube.lib.paper.reflect.wrapper.netty.Unpooled;
import fr.pandacube.lib.paper.reflect.wrapper.paper.PaperAdventure;
import fr.pandacube.lib.paper.reflect.wrapper.paper.QueuedChangesMapLong2Object;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.APICommandMeta;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.BukkitCommandNode;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.PaperBrigadier;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.PluginCommandNode;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.ShadowBrigNode;
import fr.pandacube.lib.paper.reflect.wrapper.paper.configuration.FallbackValue_Int;
import fr.pandacube.lib.paper.reflect.wrapper.paper.configuration.WorldConfiguration;
import fr.pandacube.lib.paper.reflect.wrapper.spottedleaf.moonrise.ChunkSystemChunkStorage;
import fr.pandacube.lib.reflect.ReflectionWrapperBypass;
import fr.pandacube.lib.util.ThrowableAccumulator;
@@ -179,6 +185,7 @@ public class PandalibPaperReflect {
thAcc.catchThrowable(() -> initWrapper(ServerPlayer.class, ServerPlayer.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(Settings.class, Settings.REFLECT.get()));
// minecraft.util
thAcc.catchThrowable(() -> initWrapper(ProblemReporter.class, ProblemReporter.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ProgressListener.class, ProgressListener.REFLECT.get()));
// minecraft.world.block
thAcc.catchThrowable(() -> initWrapper(Block.class, Block.REFLECT.get()));
@@ -187,28 +194,33 @@ public class PandalibPaperReflect {
thAcc.catchThrowable(() -> initWrapper(AABB.class, AABB.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ChunkPos.class, ChunkPos.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ChunkStorage.class, ChunkStorage.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(DataVersion.class, DataVersion.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(Entity.class, Entity.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ItemStack.class, ItemStack.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ItemStackWithSlot.class, ItemStackWithSlot.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(Level.class, Level.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(MapItemSavedData.class, MapItemSavedData.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(PlayerDataStorage.class, PlayerDataStorage.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(RegionFileStorage.class, RegionFileStorage.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(SavedData.class, SavedData.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(TagValueInput.class, TagValueInput.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(TagValueOutput.class, TagValueOutput.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ValueInput.class, ValueInput.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ValueInputTypedInputList.class, ValueInputTypedInputList.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ValueOutput.class, ValueOutput.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ValueOutputTypedOutputList.class, ValueOutputTypedOutputList.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(Vec3.class, Vec3.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(VoxelShape.class, VoxelShape.REFLECT.get()));
// minecraft
thAcc.catchThrowable(() -> initWrapper(DetectedVersion.class, DetectedVersion.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(SharedConstants.class, SharedConstants.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(WorldVersion.class, WorldVersion.REFLECT.get()));
// netty
thAcc.catchThrowable(() -> initWrapper(ByteBuf.class, ByteBuf.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(Unpooled.class, Unpooled.REFLECT.get()));
// paper.commands
thAcc.catchThrowable(() -> initWrapper(APICommandMeta.class, APICommandMeta.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(BukkitCommandNode.class, BukkitCommandNode.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(PaperBrigadier.class, PaperBrigadier.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(PluginCommandNode.class, PluginCommandNode.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(ShadowBrigNode.class, ShadowBrigNode.REFLECT.get()));
// paper.configuration
thAcc.catchThrowable(() -> initWrapper(FallbackValue_Int.class, FallbackValue_Int.REFLECT.get()));
@@ -216,10 +228,14 @@ public class PandalibPaperReflect {
thAcc.catchThrowable(() -> initWrapper(WorldConfiguration.Chunks.class, WorldConfiguration.Chunks.REFLECT.get()));
// paper
thAcc.catchThrowable(() -> initWrapper(PaperAdventure.class, PaperAdventure.REFLECT.get()));
thAcc.catchThrowable(() -> initWrapper(QueuedChangesMapLong2Object.class, QueuedChangesMapLong2Object.REFLECT.get()));
// spottedleaf
thAcc.catchThrowable(() -> initWrapper(ChunkSystemChunkStorage.class, ChunkSystemChunkStorage.REFLECT.get()));
thAcc.throwCaught();
}
private PandalibPaperReflect() {}
}

View File

@@ -34,7 +34,7 @@ public final class BedrockBambooCollisionFixer implements Listener {
public BedrockBambooCollisionFixer() {
// Make the bamboo block have zero collision.
try {
BambooStalkBlock.COLLISION_SHAPE(Block.box(8, 0, 8, 8, 0, 8));
BambooStalkBlock.SHAPE_COLLISION(Block.box(8, 0, 8, 8, 0, 8));
Log.info("Bamboo block collision box removed successfully.");
} catch (Exception e) {
Log.severe("Unable to remove the collision box of the Bamboo block.", e);

View File

@@ -1,21 +1,24 @@
package fr.pandacube.lib.paper.reflect.util;
import org.bukkit.Bukkit;
import org.bukkit.World;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.chat.ChatConfig.PandaTheme;
import fr.pandacube.lib.paper.modules.PerformanceAnalysisManager;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftWorld;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ChunkMap;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerLevel;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProgressListener;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import org.bukkit.Bukkit;
import org.bukkit.World;
/**
* Provides static methods to save worlds presumably in a better way than Bukkit provides (better flushing, more released RAM).
*/
public class WorldSaveUtil {
private static ChunkMap getChunkMap(World w) {
return ReflectWrapper.wrapTyped(w, CraftWorld.class).getHandle().getChunkSource().chunkMap;
}
/**
* Save the provided world using the NMS {@link ServerLevel#save(ProgressListener, boolean, boolean)} method.
* @param w the world to save.
*/
public static void nmsSaveFlush(World w) {
PerformanceAnalysisManager.getInstance().setAlteredTPSTitle(
Chat.text("Sauvegarde map ").color(PandaTheme.CHAT_BROWN_2_SAT).thenData(w.getName()).thenText(" ...")
@@ -27,9 +30,14 @@ public class WorldSaveUtil {
PerformanceAnalysisManager.getInstance().setAlteredTPSTitle(null);
}
}
/**
* Save all the loaded worlds, using {@link #nmsSaveFlush(World)}.
*/
public static void nmsSaveAllFlush() {
Bukkit.getWorlds().forEach(WorldSaveUtil::nmsSaveFlush);
}
private WorldSaveUtil() {}
}

View File

@@ -1,6 +1,8 @@
package fr.pandacube.lib.paper.reflect.wrapper.brigadier;
import fr.pandacube.lib.paper.reflect.wrapper.paper.commands.APICommandMeta;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectField;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
@@ -11,11 +13,20 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class CommandNode<S> extends ReflectWrapperTyped<com.mojang.brigadier.tree.CommandNode<S>> {
public static final ReflectClass<?> REFLECT = Reflect.ofClass(com.mojang.brigadier.tree.CommandNode.class);
private static final ReflectMethod<?> removeCommand = wrapEx(() -> REFLECT.method("removeCommand", String.class));
private static final ReflectField<?> apiCommandMeta = wrapEx(() -> REFLECT.field("apiCommandMeta"));
public void removeCommand(String cmd) {
wrapReflectEx(() -> removeCommand.invoke(__getRuntimeInstance(), cmd));
}
public APICommandMeta apiCommandMeta() {
return wrap(wrapReflectEx(() -> apiCommandMeta.getValue(__getRuntimeInstance())), APICommandMeta.class);
}
public void apiCommandMeta(APICommandMeta meta) {
wrapReflectEx(() -> apiCommandMeta.setValue(__getRuntimeInstance(), unwrap(meta)));
}
protected CommandNode(Object obj) {
super(obj);
}

View File

@@ -1,12 +1,10 @@
package fr.pandacube.lib.paper.reflect.wrapper.craftbukkit;
import fr.pandacube.lib.paper.reflect.OBCReflect;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.BlockPos;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Vec3;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.util.ThrowableUtil;
import org.bukkit.util.Vector;
@@ -16,26 +14,11 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class CraftVector extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> OBCReflect.ofClass("util.CraftVector"));
public static final ReflectMethod<?> toBukkit_Vec3 = ThrowableUtil.wrapEx(() -> REFLECT.method("toBukkit", Vec3.REFLECT.get()));
public static final ReflectMethod<?> toBukkit_BlockPos = ThrowableUtil.wrapEx(() -> REFLECT.method("toBukkit", BlockPos.REFLECT.get()));
public static final ReflectMethod<?> toNMS = wrapEx(() -> REFLECT.method("toNMS", Vector.class));
public static final ReflectMethod<?> toBlockPos = wrapEx(() -> REFLECT.method("toNMS", Vector.class));
public static Vector toBukkit(Vec3 nms) {
return (Vector) wrapReflectEx(() -> toBukkit_Vec3.invokeStatic(unwrap(nms)));
}
public static Vector toBukkit(BlockPos blockPos) {
return (Vector) wrapReflectEx(() -> toBukkit_BlockPos.invokeStatic(unwrap(blockPos)));
}
public static Vec3 toNMS(Vector bukkit) {
return wrap(wrapReflectEx(() -> toNMS.invokeStatic(bukkit)), Vec3.class);
}
public static BlockPos toBlockPos(Vector bukkit) {
return wrap(wrapReflectEx(() -> toBlockPos.invokeStatic(bukkit)), BlockPos.class);
}
protected CraftVector(Object obj) {
super(obj);

View File

@@ -17,13 +17,9 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class VanillaCommandWrapper extends ReflectWrapperTyped<BukkitCommand> {
public static final ReflectClass<?> REFLECT = wrapEx(() -> OBCReflect.ofClass("command.VanillaCommandWrapper"));
public static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(Commands.REFLECT.get(), CommandNode.class));
public static final ReflectField<?> vanillaCommand = wrapEx(() -> REFLECT.field("vanillaCommand"));
public static final ReflectMethod<?> getListener = wrapEx(() -> REFLECT.method("getListener", CommandSender.class));
public VanillaCommandWrapper(Commands dispatcher, CommandNode<CommandSourceStack> vanillaCommand) {
this(wrapReflectEx(() -> CONSTRUCTOR.instantiate(unwrap(dispatcher), vanillaCommand)));
}
@SuppressWarnings("unchecked")
public CommandNode<CommandSourceStack> vanillaCommand() {

View File

@@ -1,15 +0,0 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
public class DetectedVersion extends ReflectWrapper implements WorldVersion {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.DetectedVersion"));
protected DetectedVersion(Object obj) {
super(obj);
}
}

View File

@@ -10,15 +10,10 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class SharedConstants extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.SharedConstants"));
private static final ReflectMethod<?> getCurrentVersion = wrapEx(() -> REFLECT.method("getCurrentVersion"));
private static final ReflectMethod<?> getProtocolVersion = wrapEx(() -> REFLECT.method("getProtocolVersion"));
public static WorldVersion getCurrentVersion() {
return wrap(wrapReflectEx(() -> getCurrentVersion.invokeStatic()), WorldVersion.class);
}
public static int getProtocolVersion() {
return (int) wrapReflectEx(() -> getProtocolVersion.invokeStatic());
}

View File

@@ -14,7 +14,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(Coordinates.__concrete.class)
public interface Coordinates extends ReflectWrapperI {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.arguments.coordinates.Coordinates"));
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.arguments.coordinates.Coordinates"));
ReflectMethod<?> getPosition = wrapEx(() -> REFLECT.method("getPosition", CommandSourceStack.REFLECT.get()));
default Vec3 getPosition(io.papermc.paper.command.brigadier.CommandSourceStack source) {

View File

@@ -6,10 +6,9 @@ import fr.pandacube.lib.reflect.ReflectConstructor;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@@ -20,21 +19,16 @@ public class CompoundTag extends ReflectWrapper implements Tag {
private static final ReflectMethod<?> putBoolean = wrapEx(() -> REFLECT.method("putBoolean", String.class, boolean.class));
private static final ReflectMethod<?> putByte = wrapEx(() -> REFLECT.method("putByte", String.class, byte.class));
private static final ReflectMethod<?> putByteArray = wrapEx(() -> REFLECT.method("putByteArray", String.class, byte[].class));
private static final ReflectMethod<?> putByteArray_List = wrapEx(() -> REFLECT.method("putByteArray", String.class, List.class));
private static final ReflectMethod<?> putDouble = wrapEx(() -> REFLECT.method("putDouble", String.class, double.class));
private static final ReflectMethod<?> putFloat = wrapEx(() -> REFLECT.method("putFloat", String.class, float.class));
private static final ReflectMethod<?> putInt = wrapEx(() -> REFLECT.method("putInt", String.class, int.class));
private static final ReflectMethod<?> putIntArray = wrapEx(() -> REFLECT.method("putIntArray", String.class, int[].class));
private static final ReflectMethod<?> putIntArray_List = wrapEx(() -> REFLECT.method("putIntArray", String.class, List.class));
private static final ReflectMethod<?> putString = wrapEx(() -> REFLECT.method("putString", String.class, String.class));
private static final ReflectMethod<?> putUUID = wrapEx(() -> REFLECT.method("putUUID", String.class, UUID.class));
private static final ReflectMethod<?> putLong = wrapEx(() -> REFLECT.method("putLong", String.class, long.class));
private static final ReflectMethod<?> putLongArray = wrapEx(() -> REFLECT.method("putLongArray", String.class, long[].class));
private static final ReflectMethod<?> putLongArray_List = wrapEx(() -> REFLECT.method("putLongArray", String.class, List.class));
private static final ReflectMethod<?> putShort = wrapEx(() -> REFLECT.method("putShort", String.class, short.class));
private static final ReflectMethod<?> put = wrapEx(() -> REFLECT.method("put", String.class, Tag.REFLECT.get()));
private static final ReflectMethod<?> getTagType = wrapEx(() -> REFLECT.method("getTagType", String.class));
private static final ReflectMethod<?> getByte = wrapEx(() -> REFLECT.method("getByte", String.class));
private static final ReflectMethod<?> getShort = wrapEx(() -> REFLECT.method("getShort", String.class));
private static final ReflectMethod<?> getInt = wrapEx(() -> REFLECT.method("getInt", String.class));
@@ -47,14 +41,13 @@ public class CompoundTag extends ReflectWrapper implements Tag {
private static final ReflectMethod<?> getLongArray = wrapEx(() -> REFLECT.method("getLongArray", String.class));
private static final ReflectMethod<?> getCompound = wrapEx(() -> REFLECT.method("getCompound", String.class));
private static final ReflectMethod<?> getBoolean = wrapEx(() -> REFLECT.method("getBoolean", String.class));
private static final ReflectMethod<?> getList = wrapEx(() -> REFLECT.method("getList", String.class, int.class));
private static final ReflectMethod<?> getList = wrapEx(() -> REFLECT.method("getList", String.class));
private static final ReflectMethod<?> get = wrapEx(() -> REFLECT.method("get", String.class));
private static final ReflectMethod<?> getAllKeys = wrapEx(() -> REFLECT.method("getAllKeys"));
private static final ReflectMethod<?> keySet = wrapEx(() -> REFLECT.method("keySet"));
private static final ReflectMethod<?> entrySet = wrapEx(() -> REFLECT.method("entrySet"));
private static final ReflectMethod<?> size = wrapEx(() -> REFLECT.method("size"));
private static final ReflectMethod<?> contains = wrapEx(() -> REFLECT.method("contains", String.class));
private static final ReflectMethod<?> containsStringInt = wrapEx(() -> REFLECT.method("contains", String.class, int.class));
public CompoundTag() {
this(wrapReflectEx(() -> CONSTRUCTOR.instantiate()));
@@ -73,9 +66,6 @@ public class CompoundTag extends ReflectWrapper implements Tag {
public void putByteArray(String key, byte[] value) {
wrapReflectEx(() -> putByteArray.invoke(__getRuntimeInstance(), key, value));
}
public void putByteArray(String key, List<Byte> value) {
wrapReflectEx(() -> putByteArray_List.invoke(__getRuntimeInstance(), key, value));
}
public void putDouble(String key, double value) {
wrapReflectEx(() -> putDouble.invoke(__getRuntimeInstance(), key, value));
}
@@ -88,78 +78,79 @@ public class CompoundTag extends ReflectWrapper implements Tag {
public void putIntArray(String key, int[] value) {
wrapReflectEx(() -> putIntArray.invoke(__getRuntimeInstance(), key, value));
}
public void putIntArray(String key, List<Integer> value) {
wrapReflectEx(() -> putIntArray_List.invoke(__getRuntimeInstance(), key, value));
}
public void putString(String key, String value) {
wrapReflectEx(() -> putString.invoke(__getRuntimeInstance(), key, value));
}
public void putUUID(String key, UUID value) {
wrapReflectEx(() -> putUUID.invoke(__getRuntimeInstance(), key, value));
}
public void putLong(String key, long value) {
wrapReflectEx(() -> putLong.invoke(__getRuntimeInstance(), key, value));
}
public void putLongArray(String key, long[] value) {
wrapReflectEx(() -> putLongArray.invoke(__getRuntimeInstance(), key, value));
}
public void putLongArray(String key, List<Long> value) {
wrapReflectEx(() -> putLongArray_List.invoke(__getRuntimeInstance(), key, value));
}
public void putShort(String key, short value) {
wrapReflectEx(() -> putShort.invoke(__getRuntimeInstance(), key, value));
}
public void put(String key, Tag value) {
wrapReflectEx(() -> put.invoke(__getRuntimeInstance(), key, unwrap(value)));
}
public byte getTagType(String key) {
return (byte) wrapReflectEx(() -> getTagType.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<Byte> getByte(String key) {
return (Optional<Byte>) wrapReflectEx(() -> getByte.invoke(__getRuntimeInstance(), key));
}
public byte getByte(String key) {
return (byte) wrapReflectEx(() -> getByte.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<Short> getShort(String key) {
return (Optional<Short>) wrapReflectEx(() -> getShort.invoke(__getRuntimeInstance(), key));
}
public short getShort(String key) {
return (short) wrapReflectEx(() -> getShort.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<Integer> getInt(String key) {
return (Optional<Integer>) wrapReflectEx(() -> getInt.invoke(__getRuntimeInstance(), key));
}
public int getInt(String key) {
return (int) wrapReflectEx(() -> getInt.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<Long> getLong(String key) {
return (Optional<Long>) wrapReflectEx(() -> getLong.invoke(__getRuntimeInstance(), key));
}
public long getLong(String key) {
return (long) wrapReflectEx(() -> getLong.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<Float> getFloat(String key) {
return (Optional<Float>) wrapReflectEx(() -> getFloat.invoke(__getRuntimeInstance(), key));
}
public float getFloat(String key) {
return (float) wrapReflectEx(() -> getFloat.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<Double> getDouble(String key) {
return (Optional<Double>) wrapReflectEx(() -> getDouble.invoke(__getRuntimeInstance(), key));
}
public double getDouble(String key) {
return (double) wrapReflectEx(() -> getDouble.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<String> getString(String key) {
return (Optional<String>) wrapReflectEx(() -> getString.invoke(__getRuntimeInstance(), key));
}
public String getString(String key) {
return (String) wrapReflectEx(() -> getString.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<byte[]> getByteArray(String key) {
return (Optional<byte[]>) wrapReflectEx(() -> getByteArray.invoke(__getRuntimeInstance(), key));
}
public byte[] getByteArray(String key) {
return (byte[]) wrapReflectEx(() -> getByteArray.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<int[]> getIntArray(String key) {
return (Optional<int[]>) wrapReflectEx(() -> getIntArray.invoke(__getRuntimeInstance(), key));
}
public int[] getIntArray(String key) {
return (int[]) wrapReflectEx(() -> getIntArray.invoke(__getRuntimeInstance(), key));
@SuppressWarnings("unchecked")
public Optional<long[]> getLongArray(String key) {
return (Optional<long[]>) wrapReflectEx(() -> getLongArray.invoke(__getRuntimeInstance(), key));
}
public long[] getLongArray(String key) {
return (long[]) wrapReflectEx(() -> getLongArray.invoke(__getRuntimeInstance(), key));
public Optional<CompoundTag> getCompound(String key) {
return ((Optional<?>) wrapReflectEx(() -> getCompound.invoke(__getRuntimeInstance(), key)))
.map(u -> wrap(u, CompoundTag.class));
}
public CompoundTag getCompound(String key) {
return wrap(wrapReflectEx(() -> getCompound.invoke(__getRuntimeInstance(), key)), CompoundTag.class);
@SuppressWarnings("unchecked")
public Optional<Boolean> getBoolean(String key) {
return (Optional<Boolean>) wrapReflectEx(() -> getBoolean.invoke(__getRuntimeInstance(), key));
}
public boolean getBoolean(String key) {
return (boolean) wrapReflectEx(() -> getBoolean.invoke(__getRuntimeInstance(), key));
}
public ListTag getList(String key, int type) {
return wrap(wrapReflectEx(() -> getList.invoke(__getRuntimeInstance(), key, type)), ListTag.class);
public Optional<ListTag> getList(String key) {
return ((Optional<?>) wrapReflectEx(() -> getList.invoke(__getRuntimeInstance(), key)))
.map(u -> wrap(u, ListTag.class));
}
public Tag get(String key) {
return wrap(wrapReflectEx(() -> get.invoke(__getRuntimeInstance(), key)), Tag.class);
}
@SuppressWarnings("unchecked")
public Set<String> getAllKeys() {
return (Set<String>) wrapReflectEx(() -> getAllKeys.invoke(__getRuntimeInstance()));
public Set<String> keySet() {
return (Set<String>) wrapReflectEx(() -> keySet.invoke(__getRuntimeInstance()));
}
/**
@@ -176,8 +167,5 @@ public class CompoundTag extends ReflectWrapper implements Tag {
public boolean contains(String key) {
return (boolean) wrapReflectEx(() -> contains.invoke(__getRuntimeInstance(), key));
}
public boolean contains(String key, int type) {
return (boolean) wrapReflectEx(() -> containsStringInt.invoke(__getRuntimeInstance(), key, type));
}
}

View File

@@ -8,20 +8,22 @@ import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import java.util.Optional;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(Tag.__concrete.class)
public interface Tag extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.Tag"));
ReflectMethod<?> getAsString = wrapEx(() -> REFLECT.method("getAsString"));
ReflectMethod<?> asString = wrapEx(() -> REFLECT.method("asString"));
ReflectField<?> TAG_LIST = wrapEx(() -> REFLECT.field("TAG_LIST"));
ReflectField<?> TAG_COMPOUND = wrapEx(() -> REFLECT.field("TAG_COMPOUND"));
ReflectField<?> TAG_ANY_NUMERIC = wrapEx(() -> REFLECT.field("TAG_ANY_NUMERIC"));
default String getAsString() {
return wrapReflectEx(() -> (String) getAsString.invoke(__getRuntimeInstance()));
@SuppressWarnings("unchecked")
default Optional<String> asString() {
return wrapReflectEx(() -> (Optional<String>) asString.invoke(__getRuntimeInstance()));
}
static byte TAG_LIST() {
@@ -32,10 +34,6 @@ public interface Tag extends ReflectWrapperI {
return wrapReflectEx(() -> (byte) TAG_COMPOUND.getStaticValue());
}
static byte TAG_ANY_NUMERIC() {
return wrapReflectEx(() -> (byte) TAG_ANY_NUMERIC.getStaticValue());
}
class __concrete extends ReflectWrapper implements Tag {
private __concrete(Object obj) {

View File

@@ -0,0 +1,31 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.util;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectField;
import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(ProblemReporter.__concrete.class)
public interface ProblemReporter extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.util.ProblemReporter"));
ReflectField<?> DISCARDING = wrapEx(() -> REFLECT.field("DISCARDING"));
static ProblemReporter DISCARDING() {
return wrap(wrapReflectEx(DISCARDING::getStaticValue), ProblemReporter.class);
}
class __concrete extends ReflectWrapper implements ProblemReporter {
protected __concrete(Object obj) {
super(obj);
}
}
}

View File

@@ -1,6 +1,7 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.spottedleaf.moonrise.ChunkSystemChunkStorage;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
@@ -12,17 +13,13 @@ import java.util.concurrent.CompletableFuture;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class ChunkStorage extends ReflectWrapper {
public class ChunkStorage extends ReflectWrapper implements ChunkSystemChunkStorage {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.chunk.storage.ChunkStorage"));
private static final ReflectMethod<?> readSync = wrapEx(() -> REFLECT.method("readSync", ChunkPos.REFLECT.get())); // spigot/paper method
public CompoundTag readSync(ChunkPos pos) {
return wrap(wrapReflectEx(() -> readSync.invoke(__getRuntimeInstance(), unwrap(pos))), CompoundTag.class);
}
private static final ReflectMethod<?> read = wrapEx(() -> REFLECT.method("read", ChunkPos.REFLECT.get())); // spigot/paper method
public CompletableFuture<Optional<CompoundTag>> read(ChunkPos pos) {
@SuppressWarnings("unchecked")
CompletableFuture<Optional<?>> nmsFuture = (CompletableFuture<Optional<?>>) wrapReflectEx(() -> readSync.invoke(__getRuntimeInstance(), unwrap(pos)));
CompletableFuture<Optional<?>> nmsFuture = ((CompletableFuture<Optional<?>>) wrapReflectEx(() -> read.invoke(__getRuntimeInstance(), unwrap(pos))));
return nmsFuture.thenApply(o -> o.map(c -> wrap(c, CompoundTag.class)));
}

View File

@@ -1,32 +0,0 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class DataVersion extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.DataVersion"));
private static final ReflectMethod<?> getVersion = wrapEx(() -> REFLECT.method("getVersion"));
private static final ReflectMethod<?> getSeries = wrapEx(() -> REFLECT.method("getSeries"));
public int getVersion() {
return (int) wrapReflectEx(() -> getVersion.invoke(__getRuntimeInstance()));
}
public String getSeries() {
return (String) wrapReflectEx(() -> getSeries.invoke(__getRuntimeInstance()));
}
protected DataVersion(Object obj) {
super(obj);
}
}

View File

@@ -1,6 +1,5 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
@@ -12,16 +11,11 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class Entity extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.entity.Entity"));
public static final ReflectMethod<?> getBukkitEntity = wrapEx(() -> REFLECT.method("getBukkitEntity")); // spigot method
public static final ReflectMethod<?> serializeEntity = wrapEx(() -> REFLECT.method("serializeEntity", CompoundTag.REFLECT.get())); // paper method
public org.bukkit.entity.Entity getBukkitEntity() {
return (org.bukkit.entity.Entity) wrapReflectEx(() -> getBukkitEntity.invoke(__getRuntimeInstance()));
}
public boolean serializeEntity(CompoundTag nbt) {
return wrapReflectEx(() -> (Boolean) serializeEntity.invoke(__getRuntimeInstance(), unwrap(nbt)));
}
protected Entity(Object obj) {
super(obj);
}

View File

@@ -1,63 +1,15 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.HolderLookupProvider;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.RegistryAccess;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.Tag;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import org.bukkit.Bukkit;
import java.util.Optional;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class ItemStack extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.item.ItemStack"));
private static final ReflectMethod<?> parse = wrapEx(() -> REFLECT.method("parse", HolderLookupProvider.REFLECT.get(), Tag.REFLECT.get()));
private static final ReflectMethod<?> saveWithPrefix = wrapEx(() -> REFLECT.method("save", HolderLookupProvider.REFLECT.get(), Tag.REFLECT.get()));
private static final ReflectMethod<?> save = wrapEx(() -> REFLECT.method("save", HolderLookupProvider.REFLECT.get()));
@SuppressWarnings("unchecked")
public static Optional<ItemStack> parse(HolderLookupProvider registries, Tag nbt) {
return ((Optional<Object>) wrapReflectEx(() -> parse.invokeStatic(unwrap(registries), unwrap(nbt))))
.map(o -> wrap(o, ItemStack.class));
}
public static Optional<ItemStack> parse(Tag nbt) {
return parse(getRegistries(), nbt);
}
protected ItemStack(Object obj) {
super(obj);
}
public Tag save(HolderLookupProvider registries, Tag prefix) {
return wrap(wrapReflectEx(() -> saveWithPrefix.invoke(__getRuntimeInstance(), unwrap(registries), unwrap(prefix))), Tag.class);
}
public Tag save(HolderLookupProvider registries) {
return wrap(wrapReflectEx(() -> save.invoke(__getRuntimeInstance(), unwrap(registries))), Tag.class);
}
public Tag save(Tag prefix) {
return save(getRegistries(), prefix);
}
public Tag save() {
return save(getRegistries());
}
private static RegistryAccess getRegistries() {
return wrap(Bukkit.getServer(), CraftServer.class).getServer().registryAccess();
}
}

View File

@@ -0,0 +1,44 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import com.mojang.serialization.Codec;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectConstructor;
import fr.pandacube.lib.reflect.ReflectField;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class ItemStackWithSlot extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.ItemStackWithSlot"));
private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(int.class, ItemStack.REFLECT.get()));
private static final ReflectField<?> CODEC = wrapEx(() -> REFLECT.field("CODEC"));
private static final ReflectMethod<?> slot = wrapEx(() -> REFLECT.method("slot"));
private static final ReflectMethod<?> stack = wrapEx(() -> REFLECT.method("stack"));
public static Codec<?> CODEC() {
return (Codec<?>) wrapReflectEx(CODEC::getStaticValue);
}
protected ItemStackWithSlot(Object obj) {
super(obj);
}
public ItemStackWithSlot(int slot, ItemStack stack) {
super(wrapReflectEx(() -> CONSTRUCTOR.instantiate(slot, unwrap(stack))));
}
public int slot() {
return (int) wrapReflectEx(() -> slot.invoke(__getRuntimeInstance()));
}
public ItemStack stack() {
return wrap(wrapReflectEx(() -> stack.invoke(__getRuntimeInstance())), ItemStack.class);
}
}

View File

@@ -12,17 +12,12 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class Level extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.Level"));
public static final ReflectMethod<?> getGameTime = wrapEx(() -> REFLECT.method("getGameTime"));
public static final ReflectMethod<?> getFreeMapId = wrapEx(() -> REFLECT.method("getFreeMapId"));
public static final ReflectMethod<?> paperConfig = wrapEx(() -> REFLECT.method("paperConfig")); // paper method
public long getGameTime() {
return (long) wrapReflectEx(() -> getGameTime.invoke(__getRuntimeInstance()));
}
public int getFreeMapId() {
return (int) wrapReflectEx(() -> getFreeMapId.invoke(__getRuntimeInstance()));
}
public WorldConfiguration paperConfig() {
return wrap(wrapReflectEx(() -> paperConfig.invoke(__getRuntimeInstance())), WorldConfiguration.class);
}

View File

@@ -1,6 +1,7 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProblemReporter;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
@@ -13,15 +14,15 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class PlayerDataStorage extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.PlayerDataStorage"));
public static final ReflectMethod<?> load = wrapEx(() -> REFLECT.method("load", String.class, String.class));
public static final ReflectMethod<?> load = wrapEx(() -> REFLECT.method("load", String.class, String.class, ProblemReporter.REFLECT.get()));
/**
* @param playerName the name of the player: used for loading error message and for offline UUID generation.
* @param playerId UUID of a player as it is used to name the player data file (UUID.toString()).
*/
@SuppressWarnings("unchecked")
public Optional<CompoundTag> load(String playerName, String playerId) {
return wrapOptional((Optional<Object>) wrapReflectEx(() -> load.invoke(__getRuntimeInstance(), playerName, playerId)), CompoundTag.class);
public Optional<CompoundTag> load(String playerName, String playerId, ProblemReporter problemReporter) {
return wrapOptional((Optional<Object>) wrapReflectEx(() -> load.invoke(__getRuntimeInstance(), playerName, playerId, unwrap(problemReporter))), CompoundTag.class);
}

View File

@@ -0,0 +1,23 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class RegionFileStorage extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.chunk.storage.RegionFileStorage"));
private static final ReflectMethod<?> read = wrapEx(() -> REFLECT.method("read", ChunkPos.REFLECT.get())); // spigot/paper method
public CompoundTag read(ChunkPos pos) {
return wrap(wrapReflectEx(() -> read.invoke(__getRuntimeInstance(), unwrap(pos))), CompoundTag.class);
}
protected RegionFileStorage(Object obj) {
super(obj);
}
}

View File

@@ -0,0 +1,25 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProblemReporter;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class TagValueInput extends ReflectWrapper implements ValueInput {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.TagValueInput"));
private static final ReflectMethod<?> createGlobal = wrapEx(() -> REFLECT.method("createGlobal", ProblemReporter.REFLECT.get(), CompoundTag.REFLECT.get()));
public static ValueInput createGlobal(ProblemReporter problemReporter, CompoundTag nbt) {
return wrap(wrapReflectEx(() -> createGlobal.invokeStatic(unwrap(problemReporter), unwrap(nbt))), ValueInput.class);
}
protected TagValueInput(Object obj) {
super(obj);
}
}

View File

@@ -0,0 +1,25 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.util.ProblemReporter;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class TagValueOutput extends ReflectWrapper implements ValueOutput {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.TagValueOutput"));
private static final ReflectMethod<?> createWrappingGlobal = wrapEx(() -> REFLECT.method("createWrappingGlobal", ProblemReporter.REFLECT.get(), CompoundTag.REFLECT.get()));
public static TagValueOutput createWrappingGlobal(ProblemReporter problemReporter, CompoundTag nbt) {
return wrap(wrapReflectEx(() -> createWrappingGlobal.invokeStatic(unwrap(problemReporter), unwrap(nbt))), TagValueOutput.class);
}
protected TagValueOutput(Object obj) {
super(obj);
}
}

View File

@@ -0,0 +1,31 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import com.mojang.serialization.Codec;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(ValueInput.__concrete.class)
public interface ValueInput extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.ValueInput"));
ReflectMethod<?> listOrEmpty = wrapEx(() -> REFLECT.method("listOrEmpty", String.class, Codec.class));
default ValueInputTypedInputList listOrEmpty(String key, Codec<?> elementCodec) {
return wrap(wrapReflectEx(() -> listOrEmpty.invoke(__getRuntimeInstance(), key, elementCodec)), ValueInputTypedInputList.class);
}
class __concrete extends ReflectWrapper implements ValueInput {
private __concrete(Object obj) {
super(obj);
}
}
}

View File

@@ -0,0 +1,21 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTypedI;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
@ConcreteWrapper(ValueInputTypedInputList.__concrete.class)
public interface ValueInputTypedInputList extends ReflectWrapperTypedI<Iterable<?>> {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.ValueInput$TypedInputList"));
class __concrete extends ReflectWrapperTyped<Iterable<?>> implements ValueInputTypedInputList {
private __concrete(Object obj) {
super(obj);
}
}
}

View File

@@ -1,6 +1,6 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft;
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.DataVersion;
import com.mojang.serialization.Codec;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
@@ -12,18 +12,18 @@ import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(WorldVersion.__concrete.class)
public interface WorldVersion extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.WorldVersion"));
ReflectMethod<?> getDataVersion = wrapEx(() -> REFLECT.method("getDataVersion"));
@ConcreteWrapper(ValueOutput.__concrete.class)
public interface ValueOutput extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.ValueOutput"));
ReflectMethod<?> list = wrapEx(() -> REFLECT.method("list", String.class, Codec.class));
default DataVersion getDataVersion() {
return wrap(wrapReflectEx(() -> getDataVersion.invoke(__getRuntimeInstance())), DataVersion.class);
default ValueOutputTypedOutputList list(String key, Codec<?> elementCodec) {
return wrap(wrapReflectEx(() -> list.invoke(__getRuntimeInstance(), key, elementCodec)), ValueOutputTypedOutputList.class);
}
class __concrete extends ReflectWrapper implements WorldVersion {
class __concrete extends ReflectWrapper implements ValueOutput {
private __concrete(Object obj) {
super(obj);
}

View File

@@ -0,0 +1,28 @@
package fr.pandacube.lib.paper.reflect.wrapper.minecraft.world;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(ValueOutputTypedOutputList.__concrete.class)
public interface ValueOutputTypedOutputList extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.storage.ValueOutput$TypedOutputList"));
ReflectMethod<?> add = wrapEx(() -> REFLECT.method("add", Object.class));
default void add(Object rawElement) {
wrapReflectEx(() -> add.invoke(__getRuntimeInstance(), rawElement));
}
class __concrete extends ReflectWrapper implements ValueOutputTypedOutputList {
private __concrete(Object obj) {
super(obj);
}
}
}

View File

@@ -10,14 +10,14 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class BambooStalkBlock extends Block {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.world.level.block.BambooStalkBlock"));
public static final ReflectField<?> COLLISION_SHAPE = wrapEx(() -> REFLECT.field("COLLISION_SHAPE"));
public static final ReflectField<?> SHAPE_COLLISION = wrapEx(() -> REFLECT.field("SHAPE_COLLISION"));
public static VoxelShape COLLISION_SHAPE() {
return wrap(wrapReflectEx(COLLISION_SHAPE::getStaticValue), VoxelShape.class);
public static VoxelShape SHAPE_COLLISION() {
return wrap(wrapReflectEx(SHAPE_COLLISION::getStaticValue), VoxelShape.class);
}
public static void COLLISION_SHAPE(VoxelShape shape) {
wrapReflectEx(() -> COLLISION_SHAPE.setStaticValue(unwrap(shape)));
public static void SHAPE_COLLISION(VoxelShape shape) {
wrapReflectEx(() -> SHAPE_COLLISION.setStaticValue(unwrap(shape)));
}
protected BambooStalkBlock(Object obj) {

View File

@@ -1,25 +0,0 @@
package fr.pandacube.lib.paper.reflect.wrapper.paper;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class QueuedChangesMapLong2Object extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object"));
public static final ReflectMethod<?> getVisibleMap = wrapEx(() -> REFLECT.method("getVisibleMap"));
/** The entries in the returned value are not mapped */
public Long2ObjectLinkedOpenHashMap<?> getVisibleMap() {
return (Long2ObjectLinkedOpenHashMap<?>) wrapReflectEx(() -> getVisibleMap.invoke(__getRuntimeInstance()));
}
protected QueuedChangesMapLong2Object(Object obj) {
super(obj);
}
}

View File

@@ -0,0 +1,39 @@
package fr.pandacube.lib.paper.reflect.wrapper.paper.commands;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import org.bukkit.plugin.Plugin;
import java.util.List;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class APICommandMeta extends ReflectWrapper {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("io.papermc.paper.command.brigadier.APICommandMeta"));
private static final ReflectMethod<?> plugin = wrapEx(() -> REFLECT.method("plugin"));
private static final ReflectMethod<?> description = wrapEx(() -> REFLECT.method("description"));
private static final ReflectMethod<?> aliases = wrapEx(() -> REFLECT.method("aliases"));
public Plugin plugin() {
return (Plugin) wrapReflectEx(() -> plugin.invoke(__getRuntimeInstance()));
}
public String description() {
return (String) wrapReflectEx(() -> description.invoke(__getRuntimeInstance()));
}
@SuppressWarnings("unchecked")
public List<String> aliases() {
return (List<String>) wrapReflectEx(() -> aliases.invoke(__getRuntimeInstance()));
}
protected APICommandMeta(Object obj) {
super(obj);
}
}

View File

@@ -1,18 +1,15 @@
package fr.pandacube.lib.paper.reflect.wrapper.paper.commands;
import com.mojang.brigadier.tree.LiteralCommandNode;
import fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.Command;
import org.bukkit.plugin.Plugin;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class BukkitCommandNode extends ReflectWrapperTyped<LiteralCommandNode<CommandSourceStack>> {
public class BukkitCommandNode<S> extends CommandNode<S> {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode"));
private static final ReflectMethod<?> getBukkitCommand = wrapEx(() -> REFLECT.method("getBukkitCommand"));

View File

@@ -1,43 +0,0 @@
package fr.pandacube.lib.paper.reflect.wrapper.paper.commands;
import com.mojang.brigadier.tree.LiteralCommandNode;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectConstructor;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.plugin.configuration.PluginMeta;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
public class PluginCommandNode extends ReflectWrapperTyped<LiteralCommandNode<CommandSourceStack>> {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("io.papermc.paper.command.brigadier.PluginCommandNode"));
private static final ReflectMethod<?> getPlugin = wrapEx(() -> REFLECT.method("getPlugin"));
private static final ReflectMethod<?> getDescription = wrapEx(() -> REFLECT.method("getDescription"));
private static final ReflectConstructor CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(String.class, PluginMeta.class, LiteralCommandNode.class, String.class));
public PluginCommandNode(@NotNull String literal, @NotNull PluginMeta plugin, @NotNull LiteralCommandNode<CommandSourceStack> rootLiteral, @Nullable String description) {
this(wrapReflectEx(() -> CONSTRUCTOR.instantiate(literal, plugin, rootLiteral, description)));
}
public Plugin getPlugin() {
return (Plugin) wrapReflectEx(() -> getPlugin.invoke(__getRuntimeInstance()));
}
public String getDescription() {
return (String) wrapReflectEx(() -> getDescription.invoke(__getRuntimeInstance()));
}
protected PluginCommandNode(Object obj) {
super(obj);
}
}

View File

@@ -1,14 +1,12 @@
package fr.pandacube.lib.paper.reflect.wrapper.paper.commands;
import com.mojang.brigadier.tree.LiteralCommandNode;
import fr.pandacube.lib.paper.reflect.wrapper.brigadier.CommandNode;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
public class ShadowBrigNode extends ReflectWrapperTyped<LiteralCommandNode<CommandSourceStack>> {
public class ShadowBrigNode<S> extends CommandNode<S> {
public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("io.papermc.paper.command.brigadier.ShadowBrigNode"));

View File

@@ -0,0 +1,31 @@
package fr.pandacube.lib.paper.reflect.wrapper.spottedleaf.moonrise;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.RegionFileStorage;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.reflect.ReflectClass;
import fr.pandacube.lib.reflect.ReflectMethod;
import fr.pandacube.lib.reflect.wrapper.ConcreteWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI;
import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap;
import static fr.pandacube.lib.util.ThrowableUtil.wrapEx;
import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx;
@ConcreteWrapper(ChunkSystemChunkStorage.__concrete.class)
public interface ChunkSystemChunkStorage extends ReflectWrapperI {
ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkStorage"));
ReflectMethod<?> moonrise$getRegionStorage = wrapEx(() -> REFLECT.method("moonrise$getRegionStorage"));
default RegionFileStorage moonrise$getRegionStorage() {
return wrap(wrapReflectEx(() -> moonrise$getRegionStorage.invoke(__getRuntimeInstance())), RegionFileStorage.class);
}
class __concrete extends ReflectWrapper implements ChunkSystemChunkStorage {
private __concrete(Object obj) {
super(obj);
}
}
}

View File

@@ -69,6 +69,6 @@ public class SchedulerUtil {
}
private SchedulerUtil() {}
}

View File

@@ -1,9 +1,11 @@
package fr.pandacube.lib.paper.util;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Predicate;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.paper.PandaLibPaper;
import fr.pandacube.lib.util.log.Log;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.bossbar.BossBar.Color;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -14,26 +16,37 @@ import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.util.log.Log;
import fr.pandacube.lib.paper.PandaLibPaper;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.bossbar.BossBar.Color;
import net.kyori.adventure.text.Component;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Predicate;
/**
* A {@link BossBar} capable of automatically updating itself by registering a {@link BukkitTask} or using a {@link Timer}.
*/
public class AutoUpdatedBossBar implements Listener {
/**
* The boss bar itself.
*/
public final BossBar bar;
/**
* the function executed to update the boss bar.
*/
public final BarUpdater updater;
private Timer timer = null;
private BukkitTask bukkitTask = null;
private boolean scheduled = false;
private boolean followPlayerList = false;
private LoginLogoutListener followPlayerList = null;
private Predicate<Player> playerCondition = null;
/**
* Wraps the provided boss bar into a new instance of {@link AutoUpdatedBossBar}.
* @param bar the boss bar itself.
* @param updater the updater that will be run to update the boss bar.
*/
public AutoUpdatedBossBar(BossBar bar, BarUpdater updater) {
this.bar = bar;
this.updater = updater;
@@ -44,8 +57,8 @@ public class AutoUpdatedBossBar implements Listener {
* Schedule the update of this boss bar with synchronisation with the system clock.
* The underlying method called is {@link Timer#schedule(TimerTask, long, long)}.
* The updater is executed in a separate Thread.
* @param msDelay ms before running the first update of this bossbar
* @param msPeriod ms between each call of the updater
* @param msDelay ms before running the first update of this boss bar.
* @param msPeriod ms between each call of the updater.
*/
public synchronized void scheduleUpdateTimeSyncThreadAsync(long msDelay, long msPeriod) {
if (scheduled)
@@ -66,12 +79,11 @@ public class AutoUpdatedBossBar implements Listener {
}
/**
* Schedule the update of this boss bar with synchronisation with the main Thread of the
* current Minecraft server (follow the tick count progress).
* Schedule the update of this boss bar with synchronisation with the ticking of this Minecraft server.
* The underlying method called is {@link BukkitScheduler#runTaskTimer(org.bukkit.plugin.Plugin, Runnable, long, long)}.
* The updater is executed by the Server Thread.
* @param tickDelay number of server tick before running the first update of this boss bar
* @param tickPeriod number of server tick between each call of the updater
* The updater is executed by the main Server Thread.
* @param tickDelay number of server tick before running the first update of this boss bar.
* @param tickPeriod number of server tick between each call of the updater.
*/
public synchronized void scheduleUpdateTickSyncThreadSync(long tickDelay, long tickPeriod) {
if (scheduled)
@@ -90,55 +102,65 @@ public class AutoUpdatedBossBar implements Listener {
}, tickDelay, tickPeriod);
}
/**
* Auto-update the boss bar on player join and quit.
* @param condition an additional test that if it's true on player join/quit, will actually run the update. Can be null.
*/
public synchronized void followLoginLogout(Predicate<Player> condition) {
playerCondition = condition;
if (followPlayerList)
if (followPlayerList != null)
return;
followPlayerList = true;
BukkitEvent.register(this);
Bukkit.getServer().getOnlinePlayers().forEach(p -> onPlayerJoin(new PlayerJoinEvent(p, Component.text(""))));
followPlayerList = new LoginLogoutListener();
BukkitEvent.register(followPlayerList);
Bukkit.getServer().getOnlinePlayers().forEach(p -> followPlayerList.onPlayerJoin(new PlayerJoinEvent(p, Component.text(""))));
}
/**
* Cancel the auto-update on player join and quit.
*/
public synchronized void unfollowPlayerList() {
if (!followPlayerList)
if (followPlayerList == null)
return;
followPlayerList = false;
playerCondition = null;
PlayerJoinEvent.getHandlerList().unregister(this);
PlayerQuitEvent.getHandlerList().unregister(this);
BukkitEvent.unregister(followPlayerList);
followPlayerList = null;
}
@EventHandler(priority=EventPriority.MONITOR)
public synchronized void onPlayerJoin(PlayerJoinEvent event) {
if (!followPlayerList)
return;
if (playerCondition != null && !playerCondition.test(event.getPlayer()))
return;
synchronized (bar) {
event.getPlayer().showBossBar(bar);
private class LoginLogoutListener implements Listener {
@EventHandler(priority=EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
if (playerCondition != null && !playerCondition.test(event.getPlayer()))
return;
synchronized (bar) {
event.getPlayer().showBossBar(bar);
}
}
}
@EventHandler(priority=EventPriority.HIGH)
public synchronized void onPlayerQuit(PlayerQuitEvent event) {
if (!followPlayerList)
return;
synchronized (bar) {
event.getPlayer().hideBossBar(bar);
@EventHandler(priority=EventPriority.HIGH)
public void onPlayerQuit(PlayerQuitEvent event) {
synchronized (bar) {
event.getPlayer().hideBossBar(bar);
}
}
}
/**
* Hides this boss bar from all players.
*/
public void removeAll() {
synchronized (bar) {
for (Player p : Bukkit.getOnlinePlayers())
p.hideBossBar(bar);
}
}
/**
* Cancel any auto-updating of this boss bar.
*/
public synchronized void cancel() {
if (!scheduled)
return;
@@ -151,13 +173,19 @@ public class AutoUpdatedBossBar implements Listener {
bukkitTask.cancel();
bukkitTask = null;
}
unfollowPlayerList();
}
/**
* Functional interface taking an instance of {@link AutoUpdatedBossBar} to update it. Returns nothing.
*/
@FunctionalInterface
public interface BarUpdater {
/**
* Updates the boss bar.
* @param bar the auto-updated boss bar instance.
*/
void update(AutoUpdatedBossBar bar);
}
@@ -165,6 +193,7 @@ public class AutoUpdatedBossBar implements Listener {
/**
* Utility method to update the title of the boss bar without unnecessary packet.
* @param title the new title.
*/
public void setTitle(Chat title) {
synchronized (bar) {
@@ -174,6 +203,7 @@ public class AutoUpdatedBossBar implements Listener {
/**
* Utility method to update the color of the boss bar without unnecessary packet.
* @param color the new color.
*/
public void setColor(Color color) {
synchronized (bar) {
@@ -183,6 +213,7 @@ public class AutoUpdatedBossBar implements Listener {
/**
* Utility method to update the progress of the boss bar without unnecessary packet.
* @param progress the new progress value.
*/
public void setProgress(double progress) {
synchronized (bar) {

View File

@@ -1,72 +0,0 @@
package fr.pandacube.lib.paper.util;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
/**
* Utility class around chat coloring.
*/
public class BukkitChatColorUtil {
/**
* Returns the {@link TextColor} that is visually the closest from the provided {@link DyeColor} when used on a sign.
* @param dye the provided dye color
* @return the closest chat color from {@code dye}
*/
public static TextColor fromDyeToSignColor(DyeColor dye) {
// following code invalid due to text color on sign does not use rgb value of dye color.
//org.bukkit.Color col = dye.getColor();
//return ChatColor.of(new Color(col.asRGB()));
// the color values are extracted from IG screenshot of dyed text signs.
return switch (dye) {
case BLACK -> TextColor.fromHexString("#000000");
case RED -> TextColor.fromHexString("#650000");
case GREEN -> TextColor.fromHexString("#006500");
case BROWN -> TextColor.fromHexString("#361B07");
case BLUE -> TextColor.fromHexString("#000065");
case PURPLE -> TextColor.fromHexString("#3F0C5F");
case CYAN -> TextColor.fromHexString("#006565");
case LIGHT_GRAY -> TextColor.fromHexString("#535353");
case GRAY -> TextColor.fromHexString("#323232");
case PINK -> TextColor.fromHexString("#652947");
case LIME -> TextColor.fromHexString("#4B6500");
case YELLOW -> TextColor.fromHexString("#656500");
case LIGHT_BLUE -> TextColor.fromHexString("#3C4B51");
case MAGENTA -> TextColor.fromHexString("#650065");
case ORANGE -> TextColor.fromHexString("#65280C");
case WHITE -> TextColor.fromHexString("#656565");
};
}
@SuppressWarnings("deprecation")
public static ChatColor toBukkit(net.md_5.bungee.api.ChatColor color) {
return ChatColor.valueOf(color.getName().toUpperCase());
}
@SuppressWarnings("deprecation")
public static ChatColor toBukkit(TextColor color) {
return toBukkit(NamedTextColor.nearestTo(color));
}
@SuppressWarnings("deprecation")
public static ChatColor toBukkit(NamedTextColor color) {
return ChatColor.valueOf(color.toString().toUpperCase());
}
@SuppressWarnings("deprecation")
public static NamedTextColor toAdventure(ChatColor color) {
return NamedTextColor.NAMES.value(color.name().toLowerCase());
}
public static NamedTextColor toAdventure(net.md_5.bungee.api.ChatColor color) {
return NamedTextColor.NAMES.value(color.getName());
}
}

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