Compare commits
	
		
			47 Commits
		
	
	
		
			e7b528718c
			...
			medrias
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 27c444f3b4 | |||
| c589da2a14 | |||
| 3fe4a1b244 | |||
| 1925dd9b36 | |||
| d637b92f6c | |||
| af4bab0d12 | |||
| 44dc690736 | |||
| c9af5ad308 | |||
| 27403a6e20 | |||
| 38a42dcca0 | |||
| 5782046b7a | |||
| 2b407d7f27 | |||
| 8f5f880754 | |||
| 3d92c3afb6 | |||
| 5e1f98ab70 | |||
| 276f5b2dc1 | |||
| 9c72b8cda4 | |||
| ee023b5d2c | |||
| 974347cbde | |||
| e6b77bcad6 | |||
| 36eb1227cf | |||
| 4be37945cb | |||
| 3e6cf96040 | |||
| d1a04a7a66 | |||
| fcac9af7d1 | |||
| e16487431d | |||
| 50f5ab671a | |||
| 5a04052f8e | |||
| c86855ac16 | |||
| 001239fe57 | |||
| 0c7fb9b370 | |||
| f416e30d45 | |||
| e271ac2964 | |||
| 2bb09ad42b | |||
| 4f55890092 | |||
| 76470b963e | |||
| 76fc673e98 | |||
| 307b5132df | |||
| ac52e024f3 | |||
| bb6d221ced | |||
| 2acfa53b63 | |||
| 640b255e1d | |||
| ed0db5391d | |||
| cef4af80f0 | |||
| 7f56645ba5 | |||
| 827c13887c | |||
| 0ff2ae1296 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,5 @@ | ||||
| /.idea | ||||
| /*/target | ||||
| dependency-reduced-pom.xml | ||||
| dependency-reduced-pom.xml | ||||
|  | ||||
| *.iml | ||||
| @@ -18,8 +18,9 @@ that are detailed in their respective Readme file (if any). | ||||
| - `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 | ||||
|   | ||||
							
								
								
									
										1
									
								
								pandalib-bungee-chat/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pandalib-bungee-chat/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| /target/ | ||||
							
								
								
									
										49
									
								
								pandalib-bungee-chat/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								pandalib-bungee-chat/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| <?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 https://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-bungee-chat</artifactId> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <repositories> | ||||
|         <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-util</artifactId> | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>fr.pandacube.lib</groupId> | ||||
|             <artifactId>pandalib-chat</artifactId> | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>net.md-5</groupId> | ||||
|             <artifactId>bungeecord-chat</artifactId> | ||||
|             <version>${bungeecord.version}</version> | ||||
|             <scope>provided</scope> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>net.kyori</groupId> | ||||
|             <artifactId>adventure-platform-bungeecord</artifactId> | ||||
|             <version>4.3.0</version> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
| </project> | ||||
| @@ -0,0 +1,77 @@ | ||||
| package fr.pandacube.lib.bungee.chat; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat; | ||||
| import fr.pandacube.lib.chat.Chat.FormatableChat; | ||||
| import net.kyori.adventure.text.Component; | ||||
| import net.kyori.adventure.text.ComponentLike; | ||||
| import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; | ||||
| import net.md_5.bungee.api.chat.BaseComponent; | ||||
|  | ||||
| /** | ||||
|  * Utility class to ease conversion between our Adventure backed Chat API and BungeeCord chat API. | ||||
|  */ | ||||
| public class ChatBungee { | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent}. | ||||
|      * @param c the {@link BaseComponent}. | ||||
|      * @return a new {@link FormatableChat}. | ||||
|      */ | ||||
|     public static FormatableChat chatComponent(BaseComponent c) { | ||||
|         return Chat.chatComponent(toAdventure(c)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent BaseComponent[]}. | ||||
|      * @param c the array of {@link BaseComponent}. | ||||
|      * @return a new {@link FormatableChat}. | ||||
|      */ | ||||
|     public static FormatableChat chatComponent(BaseComponent[] c) { | ||||
|         return Chat.chatComponent(toAdventure(c)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Converts the Bungee {@link BaseComponent} array into Adventure {@link Component}. | ||||
|      * @param components the Bungee {@link BaseComponent} array. | ||||
|      * @return a {@link Component}. | ||||
|      */ | ||||
|     public static Component toAdventure(BaseComponent[] components) { | ||||
|         return BungeeComponentSerializer.get().deserialize(components); | ||||
|     } | ||||
|     /** | ||||
|      * Converts the Bungee {@link BaseComponent} into Adventure {@link Component}. | ||||
|      * @param component the Bungee {@link BaseComponent}. | ||||
|      * @return a {@link Component}. | ||||
|      */ | ||||
|     public static Component toAdventure(BaseComponent component) { | ||||
|         return toAdventure(new BaseComponent[] { component }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts the Adventure {@link Component} into Bungee {@link BaseComponent} array. | ||||
|      * @param component the Adventure {@link Component}. | ||||
|      * @return a {@link BaseComponent} array. | ||||
|      */ | ||||
|     public static BaseComponent[] toBungeeArray(ComponentLike component) { | ||||
|         return BungeeComponentSerializer.get().serialize(component.asComponent()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts the Adventure {@link Component} into Bungee {@link BaseComponent}. | ||||
|      * @param component the Adventure {@link Component}. | ||||
|      * @return a {@link BaseComponent}. | ||||
|      */ | ||||
|     public static BaseComponent toBungee(ComponentLike component) { | ||||
|         BaseComponent[] arr = toBungeeArray(component); | ||||
|         return arr.length == 1 ? arr[0] : new net.md_5.bungee.api.chat.TextComponent(arr); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private ChatBungee() {} | ||||
| } | ||||
| @@ -33,7 +33,7 @@ | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>fr.pandacube.lib</groupId> | ||||
|             <artifactId>pandalib-chat</artifactId> | ||||
|             <artifactId>pandalib-bungee-chat</artifactId> | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package fr.pandacube.lib.bungee.commands; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat; | ||||
| import fr.pandacube.lib.bungee.chat.ChatBungee; | ||||
| import fr.pandacube.lib.commands.BrigadierDispatcher; | ||||
| import net.kyori.adventure.text.ComponentLike; | ||||
| import net.md_5.bungee.api.CommandSender; | ||||
| @@ -71,6 +71,6 @@ public class BungeeBrigadierDispatcher extends BrigadierDispatcher<CommandSender | ||||
|  | ||||
| 	@Override | ||||
| 	protected void sendSenderMessage(CommandSender sender, ComponentLike message) { | ||||
| 		sender.sendMessage(Chat.toBungee(message.asComponent())); | ||||
| 		sender.sendMessage(ChatBungee.toBungee(message.asComponent())); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package fr.pandacube.lib.bungee.players; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat; | ||||
| import fr.pandacube.lib.bungee.chat.ChatBungee; | ||||
| import fr.pandacube.lib.core.mc_version.ProtocolVersion; | ||||
| import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| @@ -84,13 +84,13 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye | ||||
|  | ||||
|     @Override | ||||
|     default void sendMessage(Component message) { | ||||
|         getBungeeProxiedPlayer().sendMessage(Chat.toBungee(message)); | ||||
|         getBungeeProxiedPlayer().sendMessage(ChatBungee.toBungee(message)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     default void sendTitle(Component title, Component subtitle, int fadeIn, int stay, int fadeOut) { | ||||
|         ProxyServer.getInstance().createTitle() | ||||
|                 .title(Chat.toBungee(title)).subTitle(Chat.toBungee(subtitle)) | ||||
|                 .title(ChatBungee.toBungee(title)).subTitle(ChatBungee.toBungee(subtitle)) | ||||
|                 .fadeIn(fadeIn).stay(stay).fadeOut(fadeOut) | ||||
|                 .send(getBungeeProxiedPlayer()); | ||||
|     } | ||||
|   | ||||
| @@ -30,8 +30,13 @@ | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>net.kyori</groupId> | ||||
|             <artifactId>adventure-platform-bungeecord</artifactId> | ||||
|             <version>4.3.0</version> | ||||
|             <artifactId>adventure-text-serializer-gson</artifactId> | ||||
|             <version>4.13.0</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>net.kyori</groupId> | ||||
|             <artifactId>adventure-text-serializer-legacy</artifactId> | ||||
|             <version>4.13.0</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>net.kyori</groupId> | ||||
| @@ -46,13 +51,6 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>net.md-5</groupId> | ||||
|             <artifactId>bungeecord-chat</artifactId> | ||||
|             <version>${bungeecord.version}</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>com.google.code.gson</groupId> | ||||
|             <artifactId>gson</artifactId> | ||||
|   | ||||
| @@ -16,15 +16,13 @@ import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.text.format.TextDecoration; | ||||
| import net.kyori.adventure.text.format.TextDecoration.State; | ||||
| import net.kyori.adventure.text.minimessage.MiniMessage; | ||||
| import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; | ||||
| import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; | ||||
| import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||
| import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; | ||||
| import net.md_5.bungee.api.ChatColor; | ||||
| import net.md_5.bungee.api.chat.BaseComponent; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| import java.awt.*; | ||||
| import java.util.Locale; | ||||
| import java.util.Objects; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.function.UnaryOperator; | ||||
| @@ -67,26 +65,10 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      * Builds the component into Adventure Component instance. | ||||
|      * @return the {@link Component} built from this {@link Chat} component. | ||||
|      */ | ||||
|     public Component getAdv() { | ||||
|     public Component get() { | ||||
|         return builder.build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Builds the component into BungeeCord {@link BaseComponent} instance. | ||||
|      * @return the {@link BaseComponent} built from this {@link Chat} component. | ||||
|      */ | ||||
|     public BaseComponent get() { | ||||
|         return toBungee(getAdv()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Builds the component into BungeeCord {@link BaseComponent} array. | ||||
|      * @return the {@link BaseComponent} array built from this {@link Chat} component. | ||||
|      */ | ||||
|     public BaseComponent[] getAsArray() { | ||||
|         return toBungeeArray(getAdv()); | ||||
|     } | ||||
|  | ||||
|     private static final LegacyComponentSerializer LEGACY_SERIALIZER_BUNGEE_FRIENDLY = LegacyComponentSerializer.builder() | ||||
|             .hexColors() | ||||
|             .useUnusualXRepeatedCharacterHexFormat() | ||||
| @@ -97,7 +79,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      * @return the legacy text. RGB colors are in BungeeCord format. | ||||
|      */ | ||||
|     public String getLegacyText() { | ||||
|         return LEGACY_SERIALIZER_BUNGEE_FRIENDLY.serialize(getAdv()); | ||||
|         return LEGACY_SERIALIZER_BUNGEE_FRIENDLY.serialize(get()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -105,12 +87,12 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      * @return the plain text of this component. | ||||
|      */ | ||||
|     public String getPlainText() { | ||||
|         return PlainTextComponentSerializer.plainText().serializeOr(getAdv(), ""); | ||||
|         return PlainTextComponentSerializer.plainText().serializeOr(get(), ""); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public @NotNull HoverEvent<Component> asHoverEvent(@NotNull UnaryOperator<Component> op) { | ||||
|         return HoverEvent.showText(op.apply(getAdv())); | ||||
|         return HoverEvent.showText(op.apply(get())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -119,7 +101,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      */ | ||||
|     @Override | ||||
|     public @NotNull Component asComponent() { | ||||
|         return getAdv(); | ||||
|         return get(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -127,7 +109,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      * @return the {@link Component} built from this {@link Chat} component, with down-sampled colors. | ||||
|      */ | ||||
|     public Component getAsDownSampledColorsComponent() { | ||||
|         String json = GsonComponentSerializer.colorDownsamplingGson().serialize(getAdv()); | ||||
|         String json = GsonComponentSerializer.colorDownsamplingGson().serialize(get()); | ||||
|         return GsonComponentSerializer.gson().deserialize(json); | ||||
|     } | ||||
|  | ||||
| @@ -144,7 +126,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      * @return the MiniMessage representation if this {@link Chat} component. | ||||
|      */ | ||||
|     public String getMiniMessage() { | ||||
|         return MiniMessage.miniMessage().serialize(getAdv()); | ||||
|         return MiniMessage.miniMessage().serialize(get()); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -184,15 +166,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Appends a BungeeCord {@link BaseComponent} to this component. | ||||
|      * @param comp the component to append. | ||||
|      * @return this. | ||||
|      */ | ||||
|     public Chat then(BaseComponent comp) { | ||||
|         return then(toAdventure(comp)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Appends a component to this component. | ||||
|      * @param comp the component to append. | ||||
| @@ -207,15 +180,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|         return then(comp.asComponent()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Appends a BungeeCord {@link BaseComponent} array to this component. | ||||
|      * @param comp the components to append. | ||||
|      * @return this. | ||||
|      */ | ||||
|     public Chat then(BaseComponent[] comp) { | ||||
|         return then(toAdventure(comp)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -495,17 +459,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      */ | ||||
|     public Chat thenLeftText(ComponentLike leftText) { return then(leftText(leftText, console)); } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      *         and color and a left-aligned text. | ||||
|      * @deprecated uses Bungeecord chat API. | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public Chat thenLeftText(BaseComponent leftText) { return thenLeftText(chatComponent(leftText)); } | ||||
|  | ||||
|     /** | ||||
|      * Appends a component filling a chat line with the configured decoration character and | ||||
|      * color and a right-aligned text. | ||||
| @@ -515,17 +468,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|      */ | ||||
|     public Chat thenRightText(ComponentLike rightText) { return then(rightText(rightText, console)); } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      *         and color and a right-aligned text. | ||||
|      * @deprecated uses Bungeecord chat API. | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public Chat thenRightText(BaseComponent rightText) { return thenRightText(chatComponent(rightText)); } | ||||
|  | ||||
|     /** | ||||
|      * Appends a component filling a chat line with the configured decoration character and | ||||
|      * color and a centered text. | ||||
| @@ -537,19 +479,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|         return then(centerText(centerText, console)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      *         and color and a centered text. | ||||
|      * @deprecated uses Bungeecord chat API. | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public Chat thenCenterText(BaseComponent centerText) { | ||||
|         return thenCenterText(chatComponent(centerText)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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. | ||||
| @@ -629,12 +558,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|          * @return this. | ||||
|          */ | ||||
|         public FormatableChat color(TextColor c) { builder.color(c); return this; } | ||||
|         /** | ||||
|          * Sets the color of this component. | ||||
|          * @param c the color. | ||||
|          * @return this. | ||||
|          */ | ||||
|         public FormatableChat color(ChatColor c) { return color(c == null ? null : TextColor.color(c.getColor().getRGB())); } | ||||
|         /** | ||||
|          * Sets the color of this component. | ||||
|          * @param c the color. | ||||
| @@ -646,7 +569,16 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|          * @param c the color. | ||||
|          * @return this. | ||||
|          */ | ||||
|         public FormatableChat color(String c) { return color(c == null ? null : ChatColor.of(c)); } | ||||
|         public FormatableChat color(String c) { | ||||
|             if (c == null) | ||||
|                 return color((TextColor) null); | ||||
|             TextColor tc = c.startsWith("#") | ||||
|                     ? TextColor.fromCSSHexString(c) | ||||
|                     : NamedTextColor.NAMES.value(c.toLowerCase(Locale.ROOT)); | ||||
|             if (tc == null) | ||||
|                 throw new IllegalArgumentException("Invalid color string '" + c + "'."); | ||||
|             return color(tc); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /** | ||||
| @@ -924,18 +856,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|          * @return this. | ||||
|          */ | ||||
|         public FormatableChat hover(ComponentLike v) { return hover(v.asComponent()); } | ||||
|         /** | ||||
|          * Configure this component to show the provided component when hovered. | ||||
|          * @param v the component to show. | ||||
|          * @return this. | ||||
|          */ | ||||
|         public FormatableChat hover(BaseComponent v) { return hover(toAdventure(v)); } | ||||
|         /** | ||||
|          * Configure this component to show the provided component when hovered. | ||||
|          * @param v the component to show. | ||||
|          * @return this. | ||||
|          */ | ||||
|         public FormatableChat hover(BaseComponent[] v) { return hover(toAdventure(v)); } | ||||
|         /** | ||||
|          * Configure this component to show the provided legacy text when hovered. | ||||
|          * @param legacyText the legacy text to show. | ||||
| @@ -963,7 +883,7 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|  | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         return getAdv().hashCode(); | ||||
|         return get().hashCode(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -976,8 +896,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|  | ||||
|     /* package */ static ComponentLike filterObjToComponentLike(Object v) { | ||||
|         return switch (v) { | ||||
|             case BaseComponent[] baseComponents -> toAdventure(baseComponents); | ||||
|             case BaseComponent baseComponent -> toAdventure(baseComponent); | ||||
|             case ComponentLike componentLike -> componentLike; | ||||
|             case null, default -> Component.text(Objects.toString(v)); | ||||
|         }; | ||||
| @@ -1011,40 +929,6 @@ public abstract sealed class Chat extends ChatStatic implements HoverEventSource | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Converts the Bungee {@link BaseComponent} array into Adventure {@link Component}. | ||||
|      * @param components the Bungee {@link BaseComponent} array. | ||||
|      * @return a {@link Component}. | ||||
|      */ | ||||
|     public static Component toAdventure(BaseComponent[] components) { | ||||
|         return BungeeComponentSerializer.get().deserialize(components); | ||||
|     } | ||||
|     /** | ||||
|      * Converts the Bungee {@link BaseComponent} into Adventure {@link Component}. | ||||
|      * @param component the Bungee {@link BaseComponent}. | ||||
|      * @return a {@link Component}. | ||||
|      */ | ||||
|     public static Component toAdventure(BaseComponent component) { | ||||
|         return toAdventure(new BaseComponent[] { component }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts the Adventure {@link Component} into Bungee {@link BaseComponent} array. | ||||
|      * @param component the Adventure {@link Component}. | ||||
|      * @return a {@link BaseComponent} array. | ||||
|      */ | ||||
|     public static BaseComponent[] toBungeeArray(Component component) { | ||||
|         return BungeeComponentSerializer.get().serialize(component); | ||||
|     } | ||||
|     /** | ||||
|      * Converts the Adventure {@link Component} into Bungee {@link BaseComponent}. | ||||
|      * @param component the Adventure {@link Component}. | ||||
|      * @return a {@link BaseComponent}. | ||||
|      */ | ||||
|     public static BaseComponent toBungee(Component component) { | ||||
|         BaseComponent[] arr = toBungeeArray(component); | ||||
|         return arr.length == 1 ? arr[0] : new net.md_5.bungee.api.chat.TextComponent(arr); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Force the italic formatting to be set to false if it is not explicitly set in the component. | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| package fr.pandacube.lib.chat; | ||||
|  | ||||
| import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.text.format.TextDecoration; | ||||
| import net.kyori.adventure.text.format.TextFormat; | ||||
| import net.kyori.adventure.util.RGBLike; | ||||
|  | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| import net.kyori.adventure.text.format.NamedTextColor; | ||||
| import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.util.RGBLike; | ||||
| import net.md_5.bungee.api.ChatColor; | ||||
|  | ||||
| /** | ||||
|  * Provides methods to manipulate legacy colors and {@link ChatColor} class. | ||||
|  * Provides methods to manipulate legacy colors. | ||||
|  */ | ||||
| public class ChatColorUtil { | ||||
|  | ||||
| @@ -38,12 +38,12 @@ public class ChatColorUtil { | ||||
|         int length = legacyText.length(); | ||||
|  | ||||
|         for (int index = length - 2; index >= 0; index--) { | ||||
|             if (legacyText.charAt(index) == ChatColor.COLOR_CHAR) { | ||||
|             if (legacyText.charAt(index) == LegacyChatFormat.COLOR_CHAR) { | ||||
|  | ||||
|                 // detection of rgb color §x§0§1§2§3§4§5 | ||||
|                 String rgb; | ||||
|                 if (index > 11 | ||||
|                         && legacyText.charAt(index - 12) == ChatColor.COLOR_CHAR | ||||
|                         && legacyText.charAt(index - 12) == LegacyChatFormat.COLOR_CHAR | ||||
|                         && (legacyText.charAt(index - 11) == 'x' | ||||
|                         || legacyText.charAt(index - 11) == 'X') | ||||
|                         && HEX_COLOR_PATTERN.matcher(rgb = legacyText.substring(index - 12, index + 2)).matches()) { | ||||
| @@ -64,7 +64,7 @@ public class ChatColorUtil { | ||||
|  | ||||
|                 // try detect non-rgb format | ||||
|                 char colorChar = legacyText.charAt(index + 1); | ||||
|                 ChatColor legacyColor = getChatColorByChar(colorChar); | ||||
|                 LegacyChatFormat legacyColor = LegacyChatFormat.of(colorChar); | ||||
|  | ||||
|                 if (legacyColor != null) { | ||||
|                     result.insert(0, legacyColor); | ||||
| @@ -83,15 +83,6 @@ public class ChatColorUtil { | ||||
|         return result.toString(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the {@link ChatColor} associated with the provided char, case-insensitive. | ||||
|      * @param code the case-insensitive char code. | ||||
|      * @return the corresponding {@link ChatColor}. | ||||
|      */ | ||||
|     public static ChatColor getChatColorByChar(char code) { | ||||
|         return ChatColor.getByChar(Character.toLowerCase(code)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -99,7 +90,7 @@ public class ChatColorUtil { | ||||
|      * Translate the color code of the provided string, that uses the alt color char, to the {@code §} color code | ||||
|      * format. | ||||
|      * <p> | ||||
|      * This method is the improved version of {@link ChatColor#translateAlternateColorCodes(char, String)}, | ||||
|      * This method is the improved version of Bukkit’s {@code ChatColor.translateAlternateColorCodes(char, String)}, | ||||
|      * because it takes into account essentials RGB color code, and {@code altColorChar} escaping (by doubling it). | ||||
|      * Essentials RGB color code are converted to Bungee chat RGB format, so the returned string can be converted | ||||
|      * to component (see {@link Chat#legacyText(Object)}). | ||||
| @@ -112,7 +103,7 @@ public class ChatColorUtil { | ||||
|      */ | ||||
|     public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) | ||||
|     { | ||||
|         char colorChar = ChatColor.COLOR_CHAR; | ||||
|         char colorChar = LegacyChatFormat.COLOR_CHAR; | ||||
|         StringBuilder acc = new StringBuilder(); | ||||
|         char[] b = textToTranslate.toCharArray(); | ||||
|         for ( int i = 0; i < b.length; i++ ) | ||||
| @@ -180,7 +171,7 @@ public class ChatColorUtil { | ||||
|      * @return the text fully italic. | ||||
|      */ | ||||
|     public static String forceItalic(String legacyText) { | ||||
|         return forceFormat(legacyText, ChatColor.ITALIC); | ||||
|         return forceFormat(legacyText, TextDecoration.ITALIC); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -190,7 +181,7 @@ public class ChatColorUtil { | ||||
|      * @return the text fully bold. | ||||
|      */ | ||||
|     public static String forceBold(String legacyText) { | ||||
|         return forceFormat(legacyText, ChatColor.BOLD); | ||||
|         return forceFormat(legacyText, TextDecoration.BOLD); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -200,7 +191,7 @@ public class ChatColorUtil { | ||||
|      * @return the text fully underlined. | ||||
|      */ | ||||
|     public static String forceUnderline(String legacyText) { | ||||
|         return forceFormat(legacyText, ChatColor.UNDERLINE); | ||||
|         return forceFormat(legacyText, TextDecoration.UNDERLINED); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -210,7 +201,7 @@ public class ChatColorUtil { | ||||
|      * @return the text fully stroked through. | ||||
|      */ | ||||
|     public static String forceStrikethrough(String legacyText) { | ||||
|         return forceFormat(legacyText, ChatColor.STRIKETHROUGH); | ||||
|         return forceFormat(legacyText, TextDecoration.STRIKETHROUGH); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -220,15 +211,16 @@ public class ChatColorUtil { | ||||
|      * @return the text fully obfuscated. | ||||
|      */ | ||||
|     public static String forceObfuscated(String legacyText) { | ||||
|         return forceFormat(legacyText, ChatColor.MAGIC); | ||||
|         return forceFormat(legacyText, TextDecoration.OBFUSCATED); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     private static String forceFormat(String legacyText, ChatColor format) { | ||||
|     private static String forceFormat(String legacyText, TextFormat format) { | ||||
|         String formatStr = LegacyChatFormat.of(format).toString(); | ||||
|         return format + legacyText | ||||
|                 .replace(format.toString(), "") // remove previous tag to make the result cleaner | ||||
|                 .replaceAll("§([a-frA-FR\\d])", "§$1" + format); | ||||
|                 .replace(formatStr, "") // remove previous tag to make the result cleaner | ||||
|                 .replaceAll("§([a-frA-FR\\d])", "§$1" + formatStr); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -243,40 +235,12 @@ public class ChatColorUtil { | ||||
|      * @return the resulting text. | ||||
|      */ | ||||
|     public static String resetToColor(String legacyText, String color) { | ||||
|         return legacyText.replace(ChatColor.RESET.toString(), color); | ||||
|         return legacyText.replace(LegacyChatFormat.RESET.toString(), color); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Converts the provided {@link ChatColor} to its Adventure counterpart. | ||||
|      * @param bungee a BungeeCord {@link ChatColor} instance. | ||||
|      * @return the {@link TextColor} equivalent to the provided {@link ChatColor}. | ||||
|      */ | ||||
|     public static TextColor toAdventure(ChatColor bungee) { | ||||
|         if (bungee == null) | ||||
|             return null; | ||||
|         if (bungee.getColor() == null) | ||||
|             throw new IllegalArgumentException("The provided Bungee ChatColor must be an actual color (not format nor reset)."); | ||||
|         return TextColor.color(bungee.getColor().getRGB()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts the provided {@link TextColor} to its BungeeCord counterpart. | ||||
|      * @param col a Adventure {@link TextColor} instance. | ||||
|      * @return the {@link ChatColor} equivalent to the provided {@link TextColor}. | ||||
|      */ | ||||
|     public static ChatColor toBungee(TextColor col) { | ||||
|         if (col == null) | ||||
|             return null; | ||||
|         if (col instanceof NamedTextColor) { | ||||
|             return ChatColor.of(((NamedTextColor) col).toString()); | ||||
|         } | ||||
|         return ChatColor.of(col.asHexString()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Create a color, interpolating between 2 colors. | ||||
|      * @param v0 the value corresponding to color {@code cc0}. | ||||
|   | ||||
| @@ -87,7 +87,7 @@ public class ChatConfig { | ||||
|      */ | ||||
|     public static int getPrefixWidth(boolean console) { | ||||
|         Chat c; | ||||
|         return prefix == null ? 0 : (c = prefix.get()) == null ? 0 : ChatUtil.componentWidth(c.getAdv(), console); | ||||
|         return prefix == null ? 0 : (c = prefix.get()) == null ? 0 : ChatUtil.componentWidth(c.get(), console); | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package fr.pandacube.lib.chat; | ||||
|  | ||||
| import java.util.Objects; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat.FormatableChat; | ||||
| import net.kyori.adventure.text.BlockNBTComponent; | ||||
| import net.kyori.adventure.text.Component; | ||||
| import net.kyori.adventure.text.ComponentBuilder; | ||||
| @@ -18,9 +17,8 @@ import net.kyori.adventure.text.format.NamedTextColor; | ||||
| import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.text.minimessage.MiniMessage; | ||||
| import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||
| import net.md_5.bungee.api.chat.BaseComponent; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat.FormatableChat; | ||||
| import java.util.Objects; | ||||
|  | ||||
| /** | ||||
|  * Abstract class holding the publicly accessible methods to create an instance of {@link Chat} component. | ||||
| @@ -33,15 +31,6 @@ public abstract class ChatStatic { | ||||
|         return new FormatableChat(componentToBuilder(c)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent}. | ||||
|      * @param c the {@link BaseComponent}. | ||||
|      * @return a new {@link FormatableChat}. | ||||
|      */ | ||||
|     public static FormatableChat chatComponent(BaseComponent c) { | ||||
|         return new FormatableChat(componentToBuilder(Chat.toAdventure(c))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a {@link FormatableChat} from the provided {@link ComponentLike}. | ||||
|      * If the provided component is an instance of {@link Chat}, its content will be duplicated, and the provided one | ||||
| @@ -61,15 +50,6 @@ public abstract class ChatStatic { | ||||
|         return new FormatableChat(Component.text()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent BaseComponent[]}. | ||||
|      * @param c the array of {@link BaseComponent}. | ||||
|      * @return a new {@link FormatableChat}. | ||||
|      */ | ||||
|     public static FormatableChat chatComponent(BaseComponent[] c) { | ||||
|         return chatComponent(Chat.toAdventure(c)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,17 @@ | ||||
| package fr.pandacube.lib.chat; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat.FormatableChat; | ||||
| import net.kyori.adventure.text.Component; | ||||
| import net.kyori.adventure.text.ComponentLike; | ||||
| import net.kyori.adventure.text.TextComponent; | ||||
| import net.kyori.adventure.text.TranslatableComponent; | ||||
| import net.kyori.adventure.text.TranslationArgument; | ||||
| import net.kyori.adventure.text.format.NamedTextColor; | ||||
| import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.text.format.TextDecoration; | ||||
| import net.kyori.adventure.text.format.TextDecoration.State; | ||||
| import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| @@ -10,19 +22,6 @@ import java.util.Set; | ||||
| import java.util.TreeSet; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import net.kyori.adventure.text.Component; | ||||
| import net.kyori.adventure.text.ComponentLike; | ||||
| import net.kyori.adventure.text.TextComponent; | ||||
| import net.kyori.adventure.text.TranslatableComponent; | ||||
| import net.kyori.adventure.text.TranslationArgument; | ||||
| import net.kyori.adventure.text.format.NamedTextColor; | ||||
| import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.text.format.TextDecoration; | ||||
| import net.kyori.adventure.text.format.TextDecoration.State; | ||||
| import net.md_5.bungee.api.ChatColor; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat.FormatableChat; | ||||
|  | ||||
| import static fr.pandacube.lib.chat.ChatStatic.chat; | ||||
|  | ||||
| /** | ||||
| @@ -351,7 +350,7 @@ public class ChatUtil { | ||||
|  | ||||
|         do { | ||||
|             char c = legacyText.charAt(index); | ||||
|             if (c == ChatColor.COLOR_CHAR && index < legacyText.length() - 1) { | ||||
|             if (c == LegacyComponentSerializer.SECTION_CHAR && index < legacyText.length() - 1) { | ||||
|                 currentWord.append(c); | ||||
|                 c = legacyText.charAt(++index); | ||||
|                 currentWord.append(c); | ||||
| @@ -482,7 +481,7 @@ public class ChatUtil { | ||||
|             } | ||||
|             if (!row.isEmpty()) | ||||
|                 spacedRow.then(row.getLast()); | ||||
|             spacedRows.add(spacedRow.getAdv()); | ||||
|             spacedRows.add(spacedRow.get()); | ||||
|         } | ||||
|  | ||||
|         return spacedRows; | ||||
| @@ -504,14 +503,14 @@ public class ChatUtil { | ||||
|      */ | ||||
|     public static Component customWidthSpace(int width, boolean console) { | ||||
|         if (console) | ||||
|             return Chat.text(" ".repeat(width)).getAdv(); | ||||
|             return Chat.text(" ".repeat(width)).get(); | ||||
|         return switch (width) { | ||||
|             case 0, 1 -> Component.empty(); | ||||
|             case 2 -> Chat.text(".").black().getAdv(); | ||||
|             case 3 -> Chat.text("`").black().getAdv(); | ||||
|             case 6 -> Chat.text(". ").black().getAdv(); | ||||
|             case 7 -> Chat.text("` ").black().getAdv(); | ||||
|             case 11 -> Chat.text("`  ").black().getAdv(); | ||||
|             case 2 -> Chat.text(".").black().get(); | ||||
|             case 3 -> Chat.text("`").black().get(); | ||||
|             case 6 -> Chat.text(". ").black().get(); | ||||
|             case 7 -> Chat.text("` ").black().get(); | ||||
|             case 11 -> Chat.text("`  ").black().get(); | ||||
|             default -> { | ||||
|                 int nbSpace = width / 4; | ||||
|                 int nbBold = width % 4; | ||||
| @@ -520,13 +519,13 @@ public class ChatUtil { | ||||
|                     if (nbBold > 0) { | ||||
|                         yield Chat.text(" ".repeat(nbNotBold)).bold(false) | ||||
|                                 .then(Chat.text(" ".repeat(nbBold)).bold(true)) | ||||
|                                 .getAdv(); | ||||
|                                 .get(); | ||||
|                     } | ||||
|                     else | ||||
|                         yield Chat.text(" ".repeat(nbNotBold)).bold(false).getAdv(); | ||||
|                         yield Chat.text(" ".repeat(nbNotBold)).bold(false).get(); | ||||
|                 } | ||||
|                 else if (nbBold > 0) { | ||||
|                     yield Chat.text(" ".repeat(nbBold)).bold(true).getAdv(); | ||||
|                     yield Chat.text(" ".repeat(nbBold)).bold(true).get(); | ||||
|                 } | ||||
|                 throw new IllegalStateException("Should not be here (width=" + width + "; nbSpace=" + nbSpace + "; nbBold=" + nbBold + "; nbNotBold=" + nbNotBold + ")"); | ||||
|             } | ||||
|   | ||||
| @@ -0,0 +1,230 @@ | ||||
| package fr.pandacube.lib.chat; | ||||
|  | ||||
| import net.kyori.adventure.text.format.TextColor; | ||||
| import net.kyori.adventure.text.format.TextDecoration; | ||||
| import net.kyori.adventure.text.format.TextFormat; | ||||
| import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||
| import net.kyori.adventure.text.serializer.legacy.LegacyFormat; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| /** | ||||
|  * Convenient enum to uses legacy format while keeping compatibility with modern chat format and API (Adventure, ...) | ||||
|  */ | ||||
| public enum LegacyChatFormat { | ||||
|  | ||||
|     /** | ||||
|      * Black (0) color format code. | ||||
|      */ | ||||
|     BLACK('0'), | ||||
|     /** | ||||
|      * Dark blue (1) color format code. | ||||
|      */ | ||||
|     DARK_BLUE('1'), | ||||
|     /** | ||||
|      * Dark green (2) color format code. | ||||
|      */ | ||||
|     DARK_GREEN('2'), | ||||
|     /** | ||||
|      * Dark aqua (3) color format code. | ||||
|      */ | ||||
|     DARK_AQUA('3'), | ||||
|     /** | ||||
|      * Dark red (4) color format code. | ||||
|      */ | ||||
|     DARK_RED('4'), | ||||
|     /** | ||||
|      * Dark purple (5) color format code. | ||||
|      */ | ||||
|     DARK_PURPLE('5'), | ||||
|     /** | ||||
|      * Gold (6) color format code. | ||||
|      */ | ||||
|     GOLD('6'), | ||||
|     /** | ||||
|      * Gray (7) color format code. | ||||
|      */ | ||||
|     GRAY('7'), | ||||
|     /** | ||||
|      * Dark gray (8) color format code. | ||||
|      */ | ||||
|     DARK_GRAY('8'), | ||||
|     /** | ||||
|      * Blue (9) color format code. | ||||
|      */ | ||||
|     BLUE('9'), | ||||
|     /** | ||||
|      * Green (A) color format code. | ||||
|      */ | ||||
|     GREEN('a'), | ||||
|     /** | ||||
|      * Aqua (B) color format code. | ||||
|      */ | ||||
|     AQUA('b'), | ||||
|     /** | ||||
|      * Red (C) color format code. | ||||
|      */ | ||||
|     RED('c'), | ||||
|     /** | ||||
|      * Light purple (D) color format code. | ||||
|      */ | ||||
|     LIGHT_PURPLE('d'), | ||||
|     /** | ||||
|      * Yellow (E) color format code. | ||||
|      */ | ||||
|     YELLOW('e'), | ||||
|     /** | ||||
|      * White (F) color format code. | ||||
|      */ | ||||
|     WHITE('f'), | ||||
|     /** | ||||
|      * Obfuscated (K) decoration format code. | ||||
|      */ | ||||
|     OBFUSCATED('k'), | ||||
|     /** | ||||
|      * Bold (L) decoration format code. | ||||
|      */ | ||||
|     BOLD('l'), | ||||
|     /** | ||||
|      * Strikethrough (M) decoration format code. | ||||
|      */ | ||||
|     STRIKETHROUGH('m'), | ||||
|     /** | ||||
|      * Underlined (N) decoration format code. | ||||
|      */ | ||||
|     UNDERLINED('n'), | ||||
|     /** | ||||
|      * Italic (O) decoration format code. | ||||
|      */ | ||||
|     ITALIC('o'), | ||||
|     /** | ||||
|      * Reset (R) format code. | ||||
|      */ | ||||
|     RESET('r'); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * The character used by Minecraft for legacy chat format. | ||||
|      */ | ||||
|     public static final char COLOR_CHAR = LegacyComponentSerializer.SECTION_CHAR; | ||||
|  | ||||
|     /** {@link #COLOR_CHAR} but as a String! */ | ||||
|     public static final String COLOR_STR_PREFIX = Character.toString(COLOR_CHAR); | ||||
|  | ||||
|     private static final Map<Character, LegacyChatFormat> BY_CHAR; | ||||
|     private static final Map<TextFormat, LegacyChatFormat> BY_FORMAT; | ||||
|     private static final Map<LegacyFormat, LegacyChatFormat> BY_LEGACY; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Gets the {@link LegacyChatFormat} from the provided chat color code. | ||||
|      * @param code the character code from [0-9A-Fa-fK-Ok-oRr]. | ||||
|      * @return the {@link LegacyChatFormat} related to the provided code. | ||||
|      */ | ||||
|     public static LegacyChatFormat of(char code) { | ||||
|         return BY_CHAR.get(Character.toLowerCase(code)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the {@link LegacyChatFormat} from the provided {@link TextFormat} instance. | ||||
|      * @param format the {@link TextFormat} instance. | ||||
|      * @return the {@link LegacyChatFormat} related to the provided format. | ||||
|      */ | ||||
|     public static LegacyChatFormat of(TextFormat format) { | ||||
|         LegacyChatFormat colorOrDecoration = BY_FORMAT.get(format); | ||||
|         if (colorOrDecoration != null) | ||||
|             return colorOrDecoration; | ||||
|         if (format.getClass().getSimpleName().equals("Reset")) // an internal class of legacy serializer library | ||||
|             return RESET; | ||||
|         throw new IllegalArgumentException("Unsupported format of type " + format.getClass()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the {@link LegacyChatFormat} from the provided {@link LegacyFormat} instance. | ||||
|      * @param advLegacy the {@link LegacyFormat} instance. | ||||
|      * @return the {@link LegacyChatFormat} related to the provided format. | ||||
|      */ | ||||
|     public static LegacyChatFormat of(LegacyFormat advLegacy) { | ||||
|         return BY_LEGACY.get(advLegacy); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * The format code of this chat format. | ||||
|      */ | ||||
|     public final char code; | ||||
|  | ||||
|     /** | ||||
|      * The Adventure legacy format instance related to this chat format. | ||||
|      */ | ||||
|     public final LegacyFormat advLegacyFormat; | ||||
|  | ||||
|     LegacyChatFormat(char code) { | ||||
|         this.code = code; | ||||
|         advLegacyFormat = LegacyComponentSerializer.parseChar(code); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the related {@link TextColor}, or null if it's not a color. | ||||
|      * @return the related {@link TextColor}, or null if it's not a color. | ||||
|      */ | ||||
|     public TextColor getTextColor() { | ||||
|         return advLegacyFormat.color(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tells if this format is a color. | ||||
|      * @return true if this format is a color, false otherwise. | ||||
|      */ | ||||
|     public boolean isColor() { | ||||
|         return getTextColor() != null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the related {@link TextDecoration}, or null if it's not a decoration. | ||||
|      * @return the related {@link TextDecoration}, or null if it's not a decoration. | ||||
|      */ | ||||
|     public TextDecoration getTextDecoration() { | ||||
|         return advLegacyFormat.decoration(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tells if this format is a decoration (bold, italic, ...). | ||||
|      * @return true if this format is a decoration, false otherwise. | ||||
|      */ | ||||
|     public boolean isDecoration() { | ||||
|         return getTextDecoration() != null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tells if this format is the reset. | ||||
|      * @return true if this format is the reset, false otherwise. | ||||
|      */ | ||||
|     public boolean isReset() { | ||||
|         return this == RESET; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return COLOR_STR_PREFIX + code; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     static { | ||||
|         BY_CHAR = Arrays.stream(values()).sequential() | ||||
|                 .collect(Collectors.toMap(e -> e.code, e -> e, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|         BY_FORMAT = Arrays.stream(values()).sequential() | ||||
|                 .filter(e -> e.isColor() || e.isDecoration()) | ||||
|                 .collect(Collectors.toMap(e -> { | ||||
|                     if (e.isColor()) | ||||
|                         return e.getTextColor(); | ||||
|                     return e.getTextDecoration(); | ||||
|                 }, e -> e, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|         BY_LEGACY = Arrays.stream(values()).sequential() | ||||
|                 .collect(Collectors.toMap(e -> e.advLegacyFormat, e -> e, (e1, e2) -> e1, LinkedHashMap::new)); | ||||
|     } | ||||
| } | ||||
| @@ -15,42 +15,36 @@ | ||||
|     <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-reflect</artifactId> | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>fr.pandacube.lib</groupId> | ||||
|             <artifactId>pandalib-commands</artifactId> | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>fr.pandacube.lib</groupId> | ||||
|             <artifactId>pandalib-config</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> | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ import fr.pandacube.lib.chat.Chat.FormatableChat; | ||||
| import fr.pandacube.lib.chat.ChatTreeNode; | ||||
| import fr.pandacube.lib.cli.CLIApplication; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import net.md_5.bungee.api.chat.BaseComponent; | ||||
| import net.kyori.adventure.text.Component; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| @@ -192,7 +192,7 @@ public class CommandAdmin extends CLIBrigadierCommand { | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	private BaseComponent displayCurrentNode(CommandNode<CLICommandSender> node, boolean redirectTarget, CLICommandSender sender) { | ||||
| 	private Component displayCurrentNode(CommandNode<CLICommandSender> node, boolean redirectTarget, CLICommandSender sender) { | ||||
| 		if (node == null) | ||||
| 			throw new IllegalArgumentException("node must not be null"); | ||||
| 		FormatableChat d; | ||||
|   | ||||
| @@ -11,27 +11,31 @@ import java.util.logging.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. | ||||
| 	 */ | ||||
|   | ||||
							
								
								
									
										33
									
								
								pandalib-config/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								pandalib-config/pom.xml
									
									
									
									
									
										Normal 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> | ||||
| @@ -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. | ||||
| 	 */ | ||||
| @@ -1,4 +1,4 @@ | ||||
| package fr.pandacube.lib.core.config; | ||||
| package fr.pandacube.lib.config; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| @@ -37,6 +37,12 @@ | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>com.google.guava</groupId> | ||||
|             <artifactId>guava</artifactId> | ||||
|             <version>${guava.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- Cron expression interpreter --> | ||||
|         <dependency> | ||||
|             <groupId>ch.eitchnet</groupId> | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| package fr.pandacube.lib.core.backup; | ||||
|  | ||||
| import fr.pandacube.lib.chat.Chat; | ||||
| import fr.pandacube.lib.chat.LegacyChatFormat; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import net.md_5.bungee.api.ChatColor; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.time.LocalDateTime; | ||||
| @@ -107,14 +107,14 @@ public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTi | ||||
|         if (files == null) | ||||
|             return; | ||||
|  | ||||
|         Log.info("[Backup] Cleaning up backup directory " + ChatColor.GRAY + compressDisplayName + ChatColor.RESET + "..."); | ||||
|         Log.info("[Backup] Cleaning up backup directory " + LegacyChatFormat.GRAY + compressDisplayName + LegacyChatFormat.RESET + "..."); | ||||
|  | ||||
|         TreeMap<LocalDateTime, File> datedFiles = new TreeMap<>(); | ||||
|  | ||||
|         for (String filename : files) { | ||||
|             File file = new File(archiveDir, filename); | ||||
|             if (!filename.matches("\\d{8}-\\d{6}\\.zip")) { | ||||
|                 Log.warning("[Backup] " + ChatColor.GRAY + compressDisplayName + ChatColor.RESET + " Invalid file in backup directory: " + filename); | ||||
|                 Log.warning("[Backup] " + LegacyChatFormat.GRAY + compressDisplayName + LegacyChatFormat.RESET + " Invalid file in backup directory: " + filename); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
| @@ -123,7 +123,7 @@ public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTi | ||||
|             try { | ||||
|                 ldt = LocalDateTime.parse(dateTimeStr, BackupProcess.dateFileNameFormatter); | ||||
|             } catch (DateTimeParseException e) { | ||||
|                 Log.warning("[Backup] " + ChatColor.GRAY + compressDisplayName + ChatColor.RESET + " Unable to parse file name to a date-time: " + filename, e); | ||||
|                 Log.warning("[Backup] " + LegacyChatFormat.GRAY + compressDisplayName + LegacyChatFormat.RESET + " Unable to parse file name to a date-time: " + filename, e); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
| @@ -156,7 +156,7 @@ public abstract class BackupCleaner implements UnaryOperator<TreeSet<LocalDateTi | ||||
|         if (testOnly || oneDeleted) | ||||
|             Log.warning(c.getLegacyText()); | ||||
|  | ||||
|         Log.info("[Backup] Backup directory " + ChatColor.GRAY + compressDisplayName + ChatColor.RESET + " cleaned."); | ||||
|         Log.info("[Backup] Backup directory " + LegacyChatFormat.GRAY + compressDisplayName + LegacyChatFormat.RESET + " cleaned."); | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| package fr.pandacube.lib.core.backup; | ||||
|  | ||||
| import fc.cron.CronExpression; | ||||
| import fr.pandacube.lib.chat.LegacyChatFormat; | ||||
| import fr.pandacube.lib.core.cron.CronScheduler; | ||||
| import fr.pandacube.lib.util.FileUtils; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import net.md_5.bungee.api.ChatColor; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.text.DateFormat; | ||||
| @@ -209,7 +209,7 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab | ||||
|             File sourceDir = getSourceDir(); | ||||
|  | ||||
|             if (!sourceDir.exists()) { | ||||
|                 Log.warning("[Backup] Unable to compress " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + ": source directory " + sourceDir + " doesn't exist"); | ||||
|                 Log.warning("[Backup] Unable to compress " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + ": source directory " + sourceDir + " doesn't exist"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @@ -219,7 +219,7 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab | ||||
|             onBackupStart(); | ||||
|  | ||||
|             new Thread(() -> { | ||||
|                 Log.info("[Backup] Starting for " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " ..."); | ||||
|                 Log.info("[Backup] Starting for " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + " ..."); | ||||
|  | ||||
|                 compressor = new ZipCompressor(sourceDir, target, 9, filter); | ||||
|  | ||||
| @@ -229,7 +229,7 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab | ||||
|  | ||||
|                     success = true; | ||||
|  | ||||
|                     Log.info("[Backup] Finished for " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET); | ||||
|                     Log.info("[Backup] Finished for " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET); | ||||
|  | ||||
|                     try { | ||||
|                         BackupCleaner cleaner = getBackupCleaner(); | ||||
| @@ -267,7 +267,7 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab | ||||
|      * Logs the scheduling status of this backup process. | ||||
|      */ | ||||
|     public void displayNextSchedule() { | ||||
|         Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " next backup on " | ||||
|         Log.info("[Backup] " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + " next backup on " | ||||
|                 + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(getNext()))); | ||||
|     } | ||||
|  | ||||
| @@ -297,7 +297,7 @@ public abstract class BackupProcess implements Comparable<BackupProcess>, Runnab | ||||
|     public void logProgress() { | ||||
|         if (compressor == null) | ||||
|             return; | ||||
|         Log.info("[Backup] " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + ": " + compressor.getState().getLegacyText()); | ||||
|         Log.info("[Backup] " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + ": " + compressor.getState().getLegacyText()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| package fr.pandacube.lib.core.backup; | ||||
|  | ||||
| import com.google.common.io.Files; | ||||
| import fr.pandacube.lib.chat.LegacyChatFormat; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import net.md_5.bungee.api.ChatColor; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| @@ -53,7 +53,7 @@ public class RotatedLogsBackupProcess extends BackupProcess { | ||||
|         if (!getSourceDir().isDirectory()) | ||||
|             return; | ||||
|  | ||||
|         Log.info("[Backup] Starting for " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET + " ..."); | ||||
|         Log.info("[Backup] Starting for " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET + " ..."); | ||||
|  | ||||
|         try { | ||||
|             // wait a little after the log message above, in case the log file rotation has to be performed. | ||||
| @@ -82,9 +82,9 @@ public class RotatedLogsBackupProcess extends BackupProcess { | ||||
|  | ||||
|             success = true; | ||||
|  | ||||
|             Log.info("[Backup] Finished for " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET); | ||||
|             Log.info("[Backup] Finished for " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET); | ||||
|         } catch (Exception e) { | ||||
|             Log.severe("[Backup] Failed for : " + ChatColor.GRAY + getDisplayName() + ChatColor.RESET, e); | ||||
|             Log.severe("[Backup] Failed for : " + LegacyChatFormat.GRAY + getDisplayName() + LegacyChatFormat.RESET, e); | ||||
|         } finally { | ||||
|             onBackupEnd(success); | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -66,7 +66,12 @@ | ||||
|     "1.20.3": 765, | ||||
|     "1.20.4": 765, | ||||
|     "1.20.5": 766, | ||||
|     "1.20.6": 766 | ||||
|     "1.20.6": 766, | ||||
|     "1.21": 767, | ||||
|     "1.21.1": 767, | ||||
|     "1.21.2": 768, | ||||
|     "1.21.3": 768, | ||||
|     "1.21.4": 769 | ||||
|   }, | ||||
|   "versionsOfProtocol": { | ||||
|     "4": [ | ||||
| @@ -217,6 +222,17 @@ | ||||
|     "766": [ | ||||
|       "1.20.5", | ||||
|       "1.20.6" | ||||
|     ], | ||||
|     "767": [ | ||||
|       "1.21", | ||||
|       "1.21.1" | ||||
|     ], | ||||
|     "768": [ | ||||
|       "1.21.2", | ||||
|       "1.21.3" | ||||
|     ], | ||||
|     "769": [ | ||||
|       "1.21.4" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -23,7 +23,8 @@ public class DBConnection { | ||||
|     public DBConnection(String host, int port, String dbname, String login, String password) { | ||||
|         this("jdbc:mysql://" + host + ":" + port + "/" + dbname | ||||
|                         + "?useUnicode=true" | ||||
|                         + "&useSSL=false" | ||||
|                         + "&sslMode=DISABLED" | ||||
|                         + "&allowPublicKeyRetrieval=true" | ||||
|                         + "&characterEncoding=utf8" | ||||
|                         + "&characterSetResults=utf8" | ||||
|                         + "&character_set_server=utf8mb4" | ||||
|   | ||||
| @@ -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 --> | ||||
|   | ||||
| @@ -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> | ||||
|         <repository> | ||||
|             <id>fabricmc</id> | ||||
| @@ -71,6 +71,12 @@ | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>fr.pandacube.lib</groupId> | ||||
|             <artifactId>pandalib-bungee-chat</artifactId> | ||||
|             <version>${project.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>fr.pandacube.lib</groupId> | ||||
|             <artifactId>pandalib-paper-permissions</artifactId> | ||||
| @@ -84,19 +90,6 @@ | ||||
|             <artifactId>paper-api</artifactId> | ||||
|             <version>${paper.version}-SNAPSHOT</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>io.papermc.paper</groupId> | ||||
|             <artifactId>paper-mojangapi</artifactId> | ||||
|             <version>${paper.version}-SNAPSHOT</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- Needed to read obfuscation mapping file. Already included in Paper --> | ||||
|         <dependency> | ||||
|             <groupId>net.fabricmc</groupId> | ||||
|             <artifactId>mapping-io</artifactId> | ||||
|             <version>0.5.0</version> | ||||
|             <scope>provided</scope> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -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() {} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -6,14 +6,65 @@ import java.io.File; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * A basic class holding configuration for {@link PaperBackupManager}. | ||||
|  */ | ||||
| @SuppressWarnings("CanBeFinal") | ||||
| public class PaperBackupConfig { | ||||
|  | ||||
|     /** | ||||
|      * Creates a new Paper backup config. | ||||
|      */ | ||||
|     public PaperBackupConfig() {} | ||||
|  | ||||
|     /** | ||||
|      * Set to true to enable worlds backup. | ||||
|      * Defaults to true. | ||||
|      */ | ||||
|     public boolean worldBackupEnabled = true; | ||||
|  | ||||
|     /** | ||||
|      * Set to true to enable the backup of the working directory. | ||||
|      * The workdir backup will already ignore the logs directory and any world folder (folder with a level.dat file in it). | ||||
|      * Defaults to true. | ||||
|      */ | ||||
|     public boolean workdirBackupEnabled = true; | ||||
|  | ||||
|     /** | ||||
|      * Set to true to enable the backup of logs. | ||||
|      * Defaults to true. | ||||
|      */ | ||||
|     public boolean logsBackupEnabled = true; | ||||
|  | ||||
|     /** | ||||
|      * The cron-formatted scheduling of the worlds and workdir backups. | ||||
|      * The default value is {@code "0 2 * * *"}, that is every day at 2am. | ||||
|      */ | ||||
|     public String scheduling = "0 2 * * *"; // cron format, here is every day at 2am | ||||
|  | ||||
|     /** | ||||
|      * The backup target directory. | ||||
|      * Must be set (defaults to null). | ||||
|      */ | ||||
|     public File backupDirectory = null; | ||||
|  | ||||
|     /** | ||||
|      * The backup cleaner for the worlds backup. | ||||
|      * Defaults to keep 1 backup every 3 month + the last 5 backups. | ||||
|      */ | ||||
|     public BackupCleaner worldBackupCleaner = BackupCleaner.KEEPING_1_EVERY_N_MONTH(3).merge(BackupCleaner.KEEPING_N_LAST(5)); | ||||
|  | ||||
|     /** | ||||
|      * The backup cleaner for the workdir backup. | ||||
|      * Defaults to keep 1 backup every 3 month + the last 5 backups. | ||||
|      */ | ||||
|     public BackupCleaner workdirBackupCleaner = BackupCleaner.KEEPING_1_EVERY_N_MONTH(3).merge(BackupCleaner.KEEPING_N_LAST(5)); | ||||
|  | ||||
|     /** | ||||
|      * The list of files or directory to ignore. | ||||
|      * Defaults to none. | ||||
|      * The workdir backup will already ignore the logs directory and any world folder (folder with a level.dat file in it). | ||||
|      */ | ||||
|     public List<String> workdirIgnoreList = new ArrayList<>(); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -23,12 +23,19 @@ import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.CancellationException; | ||||
|  | ||||
| /** | ||||
|  * The backup manager for Paper servers. | ||||
|  */ | ||||
| public class PaperBackupManager extends BackupManager implements Listener { | ||||
|  | ||||
| 	private final Map<String, PaperWorldProcess> compressWorlds = new HashMap<>(); | ||||
|  | ||||
| 	PaperBackupConfig config; | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Instantiate a new backup manager. | ||||
| 	 * @param config the configuration of the backups. | ||||
| 	 */ | ||||
| 	public PaperBackupManager(PaperBackupConfig config) { | ||||
| 		super(config.backupDirectory); | ||||
| 		setConfig(config); | ||||
| @@ -49,13 +56,17 @@ public class PaperBackupManager extends BackupManager implements Listener { | ||||
| 		super.addProcess(process); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Updates the backups config | ||||
| 	 * @param config the new config. | ||||
| 	 */ | ||||
| 	public void setConfig(PaperBackupConfig config) { | ||||
| 		this.config = config; | ||||
| 		backupQueue.forEach(this::updateProcessConfig); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	public void updateProcessConfig(BackupProcess process) { | ||||
| 	private void updateProcessConfig(BackupProcess process) { | ||||
| 		if (process instanceof PaperWorkdirProcess) { | ||||
| 			process.setEnabled(config.workdirBackupEnabled); | ||||
| 			process.setBackupCleaner(config.workdirBackupCleaner); | ||||
| @@ -119,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(); | ||||
| @@ -137,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()); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| 	} | ||||
|   | ||||
| @@ -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"); | ||||
| 	} | ||||
|   | ||||
| @@ -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())) | ||||
| 			); | ||||
|   | ||||
| @@ -1,45 +1,53 @@ | ||||
| package fr.pandacube.lib.paper.commands; | ||||
|  | ||||
| import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; | ||||
| import com.mojang.brigadier.CommandDispatcher; | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
| import com.mojang.brigadier.builder.LiteralArgumentBuilder; | ||||
| import com.mojang.brigadier.context.CommandContext; | ||||
| import com.mojang.brigadier.exceptions.CommandSyntaxException; | ||||
| import com.mojang.brigadier.suggestion.SuggestionProvider; | ||||
| 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.commands.BadCommandUsage; | ||||
| import fr.pandacube.lib.commands.BrigadierCommand; | ||||
| import fr.pandacube.lib.commands.SuggestionsSupplier; | ||||
| import fr.pandacube.lib.paper.reflect.PandalibPaperReflect; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer; | ||||
| import fr.pandacube.lib.paper.PandaLibPaper; | ||||
| 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.Commands; | ||||
| 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.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; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import io.papermc.paper.command.brigadier.CommandSourceStack; | ||||
| import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.command.Command; | ||||
| import org.bukkit.command.CommandMap; | ||||
| import org.bukkit.command.CommandSender; | ||||
| import org.bukkit.command.ConsoleCommandSender; | ||||
| import org.bukkit.command.PluginCommand; | ||||
| import org.bukkit.entity.Player; | ||||
| import org.bukkit.event.Listener; | ||||
| import org.bukkit.plugin.Plugin; | ||||
| import org.bukkit.util.Vector; | ||||
|  | ||||
| import java.lang.reflect.InvocationTargetException; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import java.util.function.Predicate; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.unwrap; | ||||
| import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap; | ||||
|  | ||||
| /** | ||||
|  * Abstract class to hold a command to be integrated into a Paper server vanilla command dispatcher. | ||||
| @@ -47,65 +55,83 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| @SuppressWarnings("UnstableApiUsage") | ||||
| public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSourceStack> implements Listener { | ||||
|  | ||||
|     private static CommandDispatcher<CommandSourceStack> vanillaPaperDispatcher = null; | ||||
|  | ||||
|     private static final Commands vanillaCommandDispatcher; | ||||
|     private static final CommandDispatcher<BukkitBrigadierCommandSource> nmsDispatcher; | ||||
|  | ||||
|     static { | ||||
|         wrapEx(PandalibPaperReflect::init); | ||||
|         vanillaCommandDispatcher = ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class) | ||||
|                 .getServer() | ||||
|                 .vanillaCommandDispatcher(); | ||||
|         nmsDispatcher = vanillaCommandDispatcher.dispatcher(); | ||||
|     /** | ||||
|      * 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(); | ||||
|     } | ||||
|  | ||||
|     private static void updateVanillaPaperDispatcher(CommandDispatcher<CommandSourceStack> newDispatcher) { | ||||
|         if (vanillaPaperDispatcher == null || newDispatcher != vanillaPaperDispatcher) { | ||||
|             vanillaPaperDispatcher = newDispatcher; | ||||
|  | ||||
|             // vanillaPaperDispatcher.getRoot() is not the real root but a wrapped root. Trying to map the fake root with the real one to trick the Paper/Brigadier (un)wrapper | ||||
|             RootCommandNode<CommandSourceStack> wrappedRoot = vanillaPaperDispatcher.getRoot(); | ||||
|             ReflectClass<?> apiMirrorRootNodeClass = Reflect.ofClassOfInstance(wrappedRoot); | ||||
|             try { | ||||
|                 RootCommandNode<?> unwrappedRoot = ((CommandDispatcher<?>) apiMirrorRootNodeClass.method("getDispatcher").invoke(wrappedRoot)).getRoot(); | ||||
|  | ||||
|                 Reflect.ofClass(CommandNode.class).field("unwrappedCached").setValue(wrappedRoot, unwrappedRoot); | ||||
|                 Reflect.ofClass(CommandNode.class).field("wrappedCached").setValue(unwrappedRoot, wrappedRoot); | ||||
|  | ||||
|             } catch (InvocationTargetException|IllegalAccessException|NoSuchMethodException|NoSuchFieldException e) { | ||||
|                 Log.severe("Unable to trick the Paper/Brigadier unwrapper to properly handle commands redirecting to root command node.", e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Removes a plugin command that overrides a vanilla command, so the vanilla command functionalities are fully | ||||
|      * restored (so, not only the usage, but also the suggestions and the command structure sent to the client). | ||||
|      * @param name the name of the command to restore. | ||||
|      */ | ||||
|     public static void restoreVanillaCommand(String name) { | ||||
|         CommandMap bukkitCmdMap = Bukkit.getCommandMap(); | ||||
|         Command bukkitCommand = bukkitCmdMap.getCommand(name); | ||||
|         if (bukkitCommand != null) { | ||||
|             if (VanillaCommandWrapper.REFLECT.get().isInstance(bukkitCommand)) { | ||||
|                 //Log.info("Command /" + name + " is already a vanilla command."); | ||||
|  | ||||
|         PandaLibPaper.getPlugin().getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, | ||||
|                 event -> updateVanillaPaperDispatcher(event.registrar().getDispatcher())); | ||||
|  | ||||
|  | ||||
|         Bukkit.getServer().getScheduler().runTask(PandaLibPaper.getPlugin(), () -> { | ||||
|             if (vanillaPaperDispatcher == null) | ||||
|                 return; | ||||
|  | ||||
|             CommandNode<CommandSourceStack> targetCommand = vanillaPaperDispatcher.getRoot().getChild("minecraft:" + name); | ||||
|             if (targetCommand == null) { | ||||
|                 Log.warning("There is no vanilla command '" + name + "' to restore."); | ||||
|                 return; | ||||
|             } | ||||
|             Log.info("Removing Bukkit command /" + name + " (" + getCommandIdentity(bukkitCommand) + ")"); | ||||
|             Log.warning("[1.20.6 update] Please test that the bukkit command removal is actually working."); | ||||
|             bukkitCmdMap.getKnownCommands().remove(name.toLowerCase(java.util.Locale.ENGLISH)); | ||||
|             bukkitCommand.unregister(bukkitCmdMap); | ||||
|  | ||||
|             LiteralCommandNode<BukkitBrigadierCommandSource> node = (LiteralCommandNode<BukkitBrigadierCommandSource>) getRootNode().getChild(name); | ||||
|             Command newCommand = new VanillaCommandWrapper(vanillaCommandDispatcher, node).__getRuntimeInstance(); | ||||
|             bukkitCmdMap.getKnownCommands().put(name.toLowerCase(), newCommand); | ||||
|             newCommand.register(bukkitCmdMap); | ||||
|         } | ||||
|             CommandNode<CommandSourceStack> eventuallyBadCommandToReplace = vanillaPaperDispatcher.getRoot().getChild(name); | ||||
|             Boolean isPluginCommand = isPluginCommand(eventuallyBadCommandToReplace); | ||||
|             if (isPluginCommand != null && isPluginCommand) { | ||||
|                 Log.info(getCommandIdentity(eventuallyBadCommandToReplace) + " found in the dispatcher. Restoring the vanilla command."); | ||||
|                 vanillaPaperDispatcher.getRoot().getChildren().removeIf(c -> c.getName().equals(name)); | ||||
|                 vanillaPaperDispatcher.getRoot().addChild(getAliasNode(targetCommand, name)); | ||||
|             } | ||||
|             /*else if (isPluginCommand == null) { | ||||
|                 Log.info(getCommandIdentity(eventuallyBadCommandToReplace) + " found in the dispatcher. Unsure if we restore the vanilla command."); | ||||
|             }*/ | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Returns the vanilla instance of the Brigadier dispatcher. | ||||
|      * @return the vanilla instance of the Brigadier dispatcher. | ||||
|      */ | ||||
|     public static CommandDispatcher<BukkitBrigadierCommandSource> getNMSDispatcher() { | ||||
|         return nmsDispatcher; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the root command node of the Brigadier dispatcher. | ||||
|      * @return the root command node of the Brigadier dispatcher. | ||||
|      */ | ||||
|     protected static RootCommandNode<BukkitBrigadierCommandSource> getRootNode() { | ||||
|         return nmsDispatcher.getRoot(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -114,12 +140,15 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour | ||||
|     /** | ||||
|      * The command node of this command. | ||||
|      */ | ||||
|     protected final LiteralCommandNode<CommandSourceStack> commandNode; | ||||
|     protected LiteralCommandNode<CommandSourceStack> commandNode; | ||||
|     /** | ||||
|      * The command requested aliases. | ||||
|      */ | ||||
|     protected final String[] aliases; | ||||
|  | ||||
|     /** | ||||
|      * The command description. | ||||
|      */ | ||||
|     protected final String description; | ||||
|  | ||||
|     private final RegistrationPolicy registrationPolicy; | ||||
| @@ -135,11 +164,9 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour | ||||
|     public PaperBrigadierCommand(Plugin pl, RegistrationPolicy regPolicy) { | ||||
|         plugin = pl; | ||||
|         registrationPolicy = regPolicy; | ||||
|         commandNode = buildCommand().build(); | ||||
|         String[] aliasesTmp = getAliases(); | ||||
|         aliases = aliasesTmp == null ? new String[0] : aliasesTmp; | ||||
|         description = getDescription(); | ||||
|         postBuildCommand(commandNode); | ||||
|         register(); | ||||
|         //try { | ||||
|         //    PandalibPaperPermissions.addPermissionMapping("minecraft.command." + commandNode.getLiteral().toLowerCase(), getTargetPermission().toLowerCase()); | ||||
| @@ -159,38 +186,175 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour | ||||
|  | ||||
|     private void register() { | ||||
|         plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> { | ||||
|             updateVanillaPaperDispatcher(event.registrar().getDispatcher()); | ||||
|  | ||||
|             commandNode = buildCommand().build(); | ||||
|             postBuildCommand(commandNode); | ||||
|  | ||||
|             if (vanillaPaperDispatcher.getRoot().getChild(commandNode.getName()) != null) { | ||||
|                 Log.info("Command /" + commandNode.getName() + " found in the vanilla dispatcher during initial command registration. Replacing it by force."); | ||||
|                 vanillaPaperDispatcher.getRoot().getChildren().removeIf(c -> c.getName().equals(commandNode.getName())); | ||||
|             } | ||||
|  | ||||
|             registeredAliases = new HashSet<>(event.registrar().register(commandNode, description, List.of(aliases))); | ||||
|             doPostRegistrationFixes(); | ||||
|  | ||||
|             if (registrationPolicy == RegistrationPolicy.ALL) { | ||||
|                 // enforce registration of aliases | ||||
|                 for (String alias : aliases) { | ||||
|                     if (!registeredAliases.contains(alias)) | ||||
|                         registeredAliases.addAll(event.registrar().register(getAliasNode(alias), description)); | ||||
|                     if (!registeredAliases.contains(alias)) { | ||||
|                         Log.info("Command /" + commandNode.getName() + ": forcing registration of alias " + alias); | ||||
|                         registeredAliases.addAll(event.registrar().register(getAliasNode(commandNode, alias), description)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|             Bukkit.getServer().getScheduler().runTask(plugin, () -> { | ||||
|                 if (vanillaPaperDispatcher == null) | ||||
|                     return; | ||||
|  | ||||
|                 Set<String> forceRegistrationAgain = new HashSet<>(); | ||||
|                 forceRegistrationAgain.add(commandNode.getName()); | ||||
|                 if (registrationPolicy == RegistrationPolicy.ALL) | ||||
|                     forceRegistrationAgain.addAll(List.of(aliases)); | ||||
|  | ||||
|                 for (String aliasToForce : forceRegistrationAgain) { | ||||
|                     CommandNode<CommandSourceStack> actualNode = vanillaPaperDispatcher.getRoot().getChild(aliasToForce); | ||||
|                     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)) | ||||
|                                 return; | ||||
|                         } | ||||
|                         else if (BukkitCommandNode.REFLECT.get().isInstance(actualNode)) { | ||||
|                             BukkitCommandNode bcn = wrap(actualNode, BukkitCommandNode.class); | ||||
|                             if (bcn.getBukkitCommand() instanceof PluginCommand pc && pc.getPlugin().equals(plugin)) | ||||
|                                 return; | ||||
|                         } | ||||
|                         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)); | ||||
|                     vanillaPaperDispatcher.getRoot().addChild(newPCN); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private LiteralCommandNode<CommandSourceStack> getAliasNode(String alias) { | ||||
|          return literal(alias) | ||||
|  | ||||
|  | ||||
|     private void doPostRegistrationFixes() { | ||||
|         postRegistrationFixNode(new HashSet<>(), commandNode); | ||||
|     } | ||||
|  | ||||
|     private void postRegistrationFixNode(Set<CommandNode<CommandSourceStack>> fixedNodes, CommandNode<CommandSourceStack> originalNode) { | ||||
|         if (originalNode instanceof RootCommandNode) | ||||
|             return; | ||||
|         if (fixedNodes.contains(originalNode)) | ||||
|             return; | ||||
|         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)); | ||||
|                     cmdNodeClass.field("forks").setValue(unwrappedNode, cmdNodeClass.field("forks").getValue(originalNode)); | ||||
|                 } | ||||
|             } catch (IllegalAccessException | NoSuchFieldException e) { | ||||
|                 throw new RuntimeException(e); | ||||
|             } | ||||
|  | ||||
|             postRegistrationFixNode(fixedNodes, originalNode.getRedirect()); | ||||
|         } | ||||
|         else { | ||||
|             try { | ||||
|                 for (CommandNode<CommandSourceStack> child : originalNode.getChildren()) | ||||
|                     postRegistrationFixNode(fixedNodes, child); | ||||
|             } catch (UnsupportedOperationException ignored) { | ||||
|                 // in case getChildren is not possible (vanilla commands are wrapped by Paper API) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private static LiteralCommandNode<CommandSourceStack> getAliasNode(CommandNode<CommandSourceStack> commandNode, String alias) { | ||||
|          return LiteralArgumentBuilder.<CommandSourceStack>literal(alias) | ||||
|                 .requires(commandNode.getRequirement()) | ||||
|                 .executes(commandNode.getCommand()) | ||||
|                 .redirect(commandNode) | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|     private static String getCommandIdentity(Command bukkitCmd) { | ||||
|         if (bukkitCmd instanceof PluginCommand cmd) { | ||||
|             return "Bukkit command: /" + cmd.getName() + " from plugin " + cmd.getPlugin().getName(); | ||||
|     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 (VanillaCommandWrapper.REFLECT.get().isInstance(bukkitCmd)) { | ||||
|             return "Vanilla command: /" + bukkitCmd.getName(); | ||||
|         else if (BukkitCommandNode.REFLECT.get().isInstance(command)) { | ||||
|             BukkitCommandNode wrappedBCN = wrap(command, BukkitCommandNode.class); | ||||
|             Command bukkitCmd = wrappedBCN.getBukkitCommand(); | ||||
|             if (bukkitCmd instanceof PluginCommand cmd) { | ||||
|                 return "Node /" + command.getName() + " wrapping Bukkit command /" + bukkitCmd.getName() + " from plugin " + cmd.getPlugin().getName(); | ||||
|             } | ||||
|             else if (VanillaCommandWrapper.REFLECT.get().isInstance(bukkitCmd)) { | ||||
|                 VanillaCommandWrapper vcw = wrap(bukkitCmd, VanillaCommandWrapper.class); | ||||
|                 CommandNode<CommandSourceStack> vanillaCmd = vcw.vanillaCommand(); | ||||
|                 if (vanillaCmd != command) | ||||
|                     return "Node /" + command.getName() + " wrapping non-plugin command /" + bukkitCmd.getName() + " wrapping: " + getCommandIdentity(vcw.vanillaCommand()); | ||||
|                 else | ||||
|                     return "Node /" + command.getName() + " wrapping non-plugin command /" + bukkitCmd.getName() + " wrapping back the node (risk of StackOverflow?)"; | ||||
|             } | ||||
|             else | ||||
|                 return "Node /" + command.getName() + " wrapping " + bukkitCmd.getClass().getName() + " /" + bukkitCmd.getName(); | ||||
|         } | ||||
|         else | ||||
|             return bukkitCmd.getClass().getName() + ": /" + bukkitCmd.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)) { | ||||
|             BukkitCommandNode wrappedBCN = wrap(command, BukkitCommandNode.class); | ||||
|             Command bukkitCmd = wrappedBCN.getBukkitCommand(); | ||||
|             if (bukkitCmd instanceof PluginCommand) { | ||||
|                 return true; | ||||
|             } | ||||
|             else if (VanillaCommandWrapper.REFLECT.get().isInstance(bukkitCmd)) { | ||||
|                 VanillaCommandWrapper vcw = wrap(bukkitCmd, VanillaCommandWrapper.class); | ||||
|                 CommandNode<CommandSourceStack> vanillaCmd = vcw.vanillaCommand(); | ||||
|                 if (vanillaCmd != command) | ||||
|                     return isPluginCommand(vcw.vanillaCommand()); | ||||
|                 else | ||||
|                     return false; | ||||
|             } | ||||
|             else | ||||
|                 return null; | ||||
|         } | ||||
|         else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the aliases that are actually registered in the server. | ||||
|      * @return the actually registered aliases. | ||||
|      */ | ||||
|     protected Set<String> getRegisteredAliases() { | ||||
|         return Set.copyOf(registeredAliases); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -235,12 +399,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); | ||||
|     } | ||||
| @@ -364,6 +531,30 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<CommandSour | ||||
|      * Minecraft's argument type | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Creates a new instance of the Brigadier argument type {@code minecraft:vec3}. | ||||
|      * @return the {@code minecraft:vec3} argument type. | ||||
|      */ | ||||
|     public static ArgumentType<Object> argumentMinecraftVec3() { | ||||
|         return Vec3Argument.vec3(true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the value of the provided argument of type {@code minecraft:vec3}, from the provided context. | ||||
|      * @param context the command execution context. | ||||
|      * @param argument the argument name. | ||||
|      * @param deflt a default value if the argument is not found. | ||||
|      * @return the value of the argument. | ||||
|      */ | ||||
|     public Vector tryGetMinecraftVec3Argument(CommandContext<CommandSourceStack> context, String argument, | ||||
|                                               Vector deflt) { | ||||
|         return tryGetArgument(context, argument, Coordinates.REFLECT.get(), | ||||
|                 nmsCoordinate -> CraftVector.toBukkit( | ||||
|                         wrap(nmsCoordinate, Coordinates.class).getPosition(context.getSource()) | ||||
|                 ), | ||||
|                 deflt); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -63,6 +63,7 @@ public class DirectionalVector { | ||||
|      * contained in the provided {@link Location}. | ||||
|      * {@link Location#getYaw()} and {@link Location#getPitch()} values are automatically | ||||
|      * converted to conform {@link #yaw} and {@link #pitch} specification. | ||||
|      * @param l the location. | ||||
|      */ | ||||
|     public DirectionalVector(Location l) { | ||||
|         this( | ||||
| @@ -79,6 +80,7 @@ public class DirectionalVector { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Creates a new {@link DirectionalVector} from a simple {@link Vector}. | ||||
|      * @param v the vector representing the direction. If v.getX() and v.getZ() are 0, | ||||
|      *          the yaw will be 0. This may have inconsistency if the vector is calculated | ||||
|      *          from a {@link Location}'s yaw and pitch. In this case, prefer using | ||||
| @@ -126,7 +128,10 @@ public class DirectionalVector { | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Gets a Vector using the internal X, Y and Z values, that is a simple directional 3D vector. | ||||
|      * @return this vector as a simple 3D {@link Vector}. | ||||
|      */ | ||||
|     public Vector toVector() { | ||||
|         return new Vector(x, y, z); | ||||
|     } | ||||
| @@ -135,7 +140,8 @@ public class DirectionalVector { | ||||
|     /** | ||||
|      * Set the yaw and the pitch of the provided {@link Location} | ||||
|      * with the values inside the current {@link DirectionalVector} | ||||
|      * after conversion of these values | ||||
|      * after conversion of these values. | ||||
|      * @param l the location. | ||||
|      */ | ||||
|     public void putIntoLocation(Location l) { | ||||
|         /*              std   : -PI/2         : <0 ? +2PI : MC | ||||
| @@ -148,7 +154,10 @@ public class DirectionalVector { | ||||
|         l.setPitch((float) Math.toDegrees(-pitch)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Gets the vector pointing to the opposite direction. | ||||
|      * @return the opposite vector. | ||||
|      */ | ||||
|     public DirectionalVector getOpposite() { | ||||
|         return new DirectionalVector( | ||||
|                 -x, | ||||
| @@ -163,6 +172,7 @@ public class DirectionalVector { | ||||
|      * If the current direction is the player face direction, | ||||
|      * this method return the direction of the back of the head. | ||||
|      * This is an alias of {@link #getOpposite()} | ||||
|      * @return the opposite of this vector. | ||||
|      */ | ||||
|     public DirectionalVector getBackDirection() { | ||||
|         return getOpposite(); | ||||
| @@ -171,6 +181,7 @@ public class DirectionalVector { | ||||
|     /** | ||||
|      * If the current direction is the player face direction, | ||||
|      * this method return the direction of the bottom of the head. | ||||
|      * @return the vector pointing on the bottom, as if this vector was the front orientation of a player head. | ||||
|      */ | ||||
|     public DirectionalVector getBottomDirection() { | ||||
|         return new DirectionalVector( | ||||
| @@ -182,6 +193,7 @@ public class DirectionalVector { | ||||
|     /** | ||||
|      * If the current direction is the player face direction, | ||||
|      * this method return the direction of the top of the head. | ||||
|      * @return the vector pointing on the top, as if this vector was the front orientation of a player head. | ||||
|      */ | ||||
|     public DirectionalVector getTopDirection() { | ||||
|         return new DirectionalVector( | ||||
| @@ -194,6 +206,7 @@ public class DirectionalVector { | ||||
|     /** | ||||
|      * If the current direction is the player face direction, | ||||
|      * this method return the direction of the left of the head. | ||||
|      * @return the vector pointing on the left, as if this vector was the front orientation of a player head. | ||||
|      */ | ||||
|     public DirectionalVector getLeftDirection() { | ||||
|         return new DirectionalVector( | ||||
| @@ -206,6 +219,7 @@ public class DirectionalVector { | ||||
|     /** | ||||
|      * If the current direction is the player face direction, | ||||
|      * this method return the direction of the right of the head. | ||||
|      * @return the vector pointing on the right, as if this vector was the front orientation of a player head. | ||||
|      */ | ||||
|     public DirectionalVector getRightDirection() { | ||||
|         return new DirectionalVector( | ||||
|   | ||||
| @@ -2,24 +2,29 @@ package fr.pandacube.lib.paper.geometry; | ||||
|  | ||||
| import org.bukkit.Location; | ||||
| import org.bukkit.entity.Player; | ||||
| import org.bukkit.util.BoundingBox; | ||||
| import org.bukkit.util.RayTraceResult; | ||||
| import org.bukkit.util.Vector; | ||||
|  | ||||
| /** | ||||
|  * Utility class related to geometry and Minecraft. | ||||
|  */ | ||||
| public class GeometryUtil { | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Value equal to <code>{@link Math#PI}</code>. | ||||
| 	 */ | ||||
| 	public static final double PI   = Math.PI; | ||||
| 	 | ||||
| 	static final double PI   = Math.PI; | ||||
|  | ||||
| 	/** | ||||
| 	 * Value equal to <code>{@link Math#PI} / 2</code>. | ||||
| 	 */ | ||||
| 	public static final double PId2 = PI/2; | ||||
| 	 | ||||
| 	static final double PId2 = PI/2; | ||||
|  | ||||
| 	/** | ||||
| 	 * Value equal to <code>{@link Math#PI} * 2</code>. | ||||
| 	 */ | ||||
| 	public static final double PIx2 = PI*2; | ||||
| 	static final double PIx2 = PI*2; | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| @@ -55,9 +60,21 @@ public class GeometryUtil { | ||||
| 	 * Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight | ||||
| 	 */ | ||||
| 	public static final double PLAYER_EYE_HEIGHT_SNEAK = 1.27; | ||||
| 	/** | ||||
| 	 * The size of a skin pixel. | ||||
| 	 */ | ||||
| 	public static final double PLAYER_SKIN_PIXEL_SIZE = PLAYER_SKIN_HEIGHT / 32; | ||||
| 	/** | ||||
| 	 * The height of the center of rotation of the head, that is the distance between neck and the player's foot. | ||||
| 	 */ | ||||
| 	public static final double PLAYER_HEAD_ROTATION_HEIGHT = PLAYER_SKIN_PIXEL_SIZE * 24; | ||||
| 	/** | ||||
| 	 * The height of the center of rotation of the head, that is the distance between neck and the player's foot, but when the player is sneaking. | ||||
| 	 */ | ||||
| 	public static final double PLAYER_HEAD_ROTATION_HEIGHT_SNEAK = PLAYER_HEAD_ROTATION_HEIGHT - (PLAYER_SKIN_HEIGHT - PLAYER_SKIN_HEIGHT_SNEAK); | ||||
| 	/** | ||||
| 	 * The size of the first layer of the players head. | ||||
| 	 */ | ||||
| 	public static final double PLAYER_HEAD_SIZE = PLAYER_SKIN_PIXEL_SIZE * 8; | ||||
| 	 | ||||
| 	 | ||||
| @@ -67,7 +84,6 @@ public class GeometryUtil { | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
|  | ||||
| 	 | ||||
| 	 | ||||
| @@ -76,7 +92,7 @@ public class GeometryUtil { | ||||
| 	/** | ||||
| 	 * Get the {@link Location}s of the 8 vertex of the player head<br/> | ||||
| 	 * This method only work if the player is standing up | ||||
| 	 * (not dead, not gliding, not sleeping). | ||||
| 	 * (not dead, not gliding, not sleeping, not swimming). | ||||
| 	 * @param playerLocation the location of the player, generally provided by {@link Player#getLocation()} | ||||
| 	 * @param isSneaking if the player is sneaking. Generally {@link Player#isSneaking()} | ||||
| 	 * @return an array of 8 {@link Location}s with x, y, and z values filled (yaw and pitch are ignored). | ||||
| @@ -129,27 +145,22 @@ public class GeometryUtil { | ||||
| 	/** | ||||
| 	 * Check if the path from <i>start</i> location to <i>end</i> pass through | ||||
| 	 * the axis aligned bounding box defined by <i>min</i> and <i>max</i>. | ||||
| 	 * @param start the start of the path. | ||||
| 	 * @param end the end of the path. | ||||
| 	 * @param min the min of the bounding box. | ||||
| 	 * @param max the max of the bounding box. | ||||
| 	 * @return true if the path intersects the bounding box. | ||||
| 	 * @deprecated use {@link BoundingBox#rayTrace(Vector, Vector, double)} instead. | ||||
| 	 */ | ||||
| 	@Deprecated | ||||
|     public static boolean hasIntersection(Vector start, Vector end, Vector min, Vector max) { | ||||
|         final double epsilon = 0.0001f; | ||||
|   | ||||
|         Vector d = end.clone().subtract(start).multiply(0.5); | ||||
|         Vector e = max.clone().subtract(min).multiply(0.5); | ||||
|         Vector c = start.clone().add(d).subtract(min.clone().add(max).multiply(0.5)); | ||||
|         Vector ad = d.clone(); | ||||
|         ad.setX(Math.abs(ad.getX())); | ||||
|         ad.setY(Math.abs(ad.getY())); | ||||
|         ad.setZ(Math.abs(ad.getZ())); | ||||
|  | ||||
|         return !( | ||||
| 				Math.abs(c.getX()) > e.getX() + ad.getX() | ||||
| 				|| Math.abs(c.getY()) > e.getY() + ad.getY() | ||||
| 				|| Math.abs(c.getZ()) > e.getX() + ad.getZ() | ||||
| 				|| Math.abs(d.getY() * c.getZ() - d.getZ() * c.getY()) > e.getY() * ad.getZ() + e.getZ() * ad.getY() + epsilon | ||||
| 				|| Math.abs(d.getZ() * c.getX() - d.getX() * c.getZ()) > e.getZ() * ad.getX() + e.getX() * ad.getZ() + epsilon | ||||
| 				|| Math.abs(d.getX() * c.getY() - d.getY() * c.getX()) > e.getX() * ad.getY() + e.getY() * ad.getX() + epsilon | ||||
| 		); | ||||
|         RayTraceResult res = BoundingBox.of(min, max).rayTrace(start, end.clone().subtract(start), end.distance(start)); | ||||
| 		return res != null; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| 	private GeometryUtil() {} | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,11 +2,10 @@ package fr.pandacube.lib.paper.geometry.blocks; | ||||
|  | ||||
| import fr.pandacube.lib.util.RandomUtil; | ||||
| import org.bukkit.Location; | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.block.Block; | ||||
| import org.bukkit.util.BlockVector; | ||||
| import org.bukkit.util.BoundingBox; | ||||
| import org.bukkit.util.Vector; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| import java.util.Iterator; | ||||
|  | ||||
| @@ -30,10 +29,19 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 		volume = original.volume; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Construct a {@link AABBBlock} based on the two provided Bukkit's {@link Vector}. | ||||
| 	 * @param p1 the first vector. | ||||
| 	 * @param p2 the second vector. | ||||
| 	 */ | ||||
| 	public AABBBlock(Vector p1, Vector p2) { | ||||
| 		this(p1.getBlockX(), p1.getBlockY(), p1.getBlockZ(), p2.getBlockX(), p2.getBlockY(), p2.getBlockZ()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Construct a {@link AABBBlock} based on the provided Bukkit's {@link BoundingBox}. | ||||
| 	 * @param bb the bounding box. | ||||
| 	 */ | ||||
| 	public AABBBlock(BoundingBox bb) { | ||||
| 		pos1 = bb.getMin(); | ||||
| 		pos2 = bb.getMax(); | ||||
| @@ -41,15 +49,35 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 		volume = (int) bb.getVolume(); | ||||
| 		bukkitBoundingBox = bb; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Construct a {@link AABBBlock} based on the two provided Bukkit's {@link Location}. | ||||
| 	 * The worlds defined in the provided locations are ignored. | ||||
| 	 * @param l1 the first location. | ||||
| 	 * @param l2 the second location. | ||||
| 	 */ | ||||
| 	public AABBBlock(Location l1, Location l2) { | ||||
| 		this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ()); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Construct a {@link AABBBlock} based on the two provided Bukkit's {@link BlockVector}. | ||||
| 	 * @param l1 the first block vector. | ||||
| 	 * @param l2 the second block vector. | ||||
| 	 */ | ||||
| 	public AABBBlock(BlockVector l1, BlockVector l2) { | ||||
| 		this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ()); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Construct a {@link AABBBlock} based on the individual coordinates of the 2 vectors. | ||||
| 	 * @param p1x the x value of the first vector. | ||||
| 	 * @param p1y the y value of the first vector. | ||||
| 	 * @param p1z the z value of the first vector. | ||||
| 	 * @param p2x the x value of the second vector. | ||||
| 	 * @param p2y the y value of the second vector. | ||||
| 	 * @param p2z the z value of the second vector. | ||||
| 	 */ | ||||
| 	public AABBBlock(int p1x, int p1y, int p1z, int p2x, int p2y, int p2z) { | ||||
| 		/* | ||||
| 		 * Prends les points extérieurs permettant de former un bounding box englobant | ||||
| @@ -74,22 +102,45 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the value of the "minimum" {@link Vector}, that is the vector with the lowest coordinates that is inside this bounding box. | ||||
| 	 * @return the minimum vector. | ||||
| 	 */ | ||||
| 	public Vector getMin() { | ||||
| 		return pos1.clone(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the value of the "maximum" {@link Vector}, that is the vector with the highest coordinates that is inside this bounding box. | ||||
| 	 * @return the maximum vector. | ||||
| 	 */ | ||||
| 	public Vector getMax() { | ||||
| 		return pos2.clone(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the {@link BlockVector} with the lowest coordinates in this bounding box. | ||||
| 	 * @return the minimum block vector. | ||||
| 	 */ | ||||
| 	public BlockVector getMinBlock() { | ||||
| 		return pos1.toBlockVector(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the {@link BlockVector} with the highest coordinates in this bounding box. | ||||
| 	 * @return the maximum block vector. | ||||
| 	 */ | ||||
| 	public BlockVector getMaxBlock() { | ||||
| 		return pos2.clone().add(new Vector(-1, -1, -1)).toBlockVector(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets a new {@link AABBBlock} with its coordinates shifted by the provided amount. | ||||
| 	 * @param x the x shift. | ||||
| 	 * @param y the y shift. | ||||
| 	 * @param z the z shift. | ||||
| 	 * @return a shifted bounding box. | ||||
| 	 */ | ||||
| 	public AABBBlock shift(int x, int y, int z) { | ||||
| 		return new AABBBlock(this, x, y, z); | ||||
| 	} | ||||
| @@ -101,7 +152,6 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	 | ||||
| 	public boolean overlaps(BoundingBox bb) { | ||||
| 		return asBukkitBoundingBox().overlaps(bb); | ||||
| 	} | ||||
| @@ -109,15 +159,23 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 	public boolean isInside(Vector v) { | ||||
| 		return asBukkitBoundingBox().contains(v); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the coordinate of the center of this bounding box. | ||||
| 	 * @return the center of this bounding box. | ||||
| 	 */ | ||||
| 	public Vector getCenter() { | ||||
| 		return center.clone(); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	public long getVolume() { | ||||
| 		return volume; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the Bukkit equivalent of this bounding box. | ||||
| 	 * @return a {@link BoundingBox} corresponding to this {@link AABBBlock}. | ||||
| 	 */ | ||||
| 	public BoundingBox asBukkitBoundingBox() { | ||||
| 		if (bukkitBoundingBox == null) { | ||||
| 			bukkitBoundingBox = new BoundingBox(pos1.getX(), pos1.getY(), pos1.getZ(), | ||||
| @@ -134,7 +192,7 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Iterator<BlockVector> iterator() { | ||||
| 	public @NotNull Iterator<BlockVector> iterator() { | ||||
| 		return new Iterator<>() { | ||||
| 			private int x = pos1.getBlockX(), | ||||
| 					y = pos1.getBlockY(), | ||||
| @@ -162,22 +220,6 @@ public class AABBBlock implements BlockSet, Cloneable { | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	public Iterable<Block> asBlockIterable(World w) { | ||||
| 		return () -> new Iterator<>() { | ||||
| 			final Iterator<BlockVector> nested = AABBBlock.this.iterator(); | ||||
| 			@Override | ||||
| 			public boolean hasNext() { | ||||
| 				return nested.hasNext(); | ||||
| 			} | ||||
| 			@Override | ||||
| 			public Block next() { | ||||
| 				BlockVector bv = nested.next(); | ||||
| 				return w.getBlockAt(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ()); | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	@Override | ||||
|   | ||||
| @@ -12,19 +12,33 @@ import java.util.Collection; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * A group of {@link AABBBlock}. | ||||
|  */ | ||||
| public class AABBBlockGroup implements BlockSet { | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * The list of {@link AABBBlock} contained in this group. This list is unmodifiable. | ||||
| 	 */ | ||||
| 	public final List<AABBBlock> subAABB; | ||||
|  | ||||
| 	private final AABBBlock englobingAABB; | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a new {@link AABBBlockGroup}. | ||||
| 	 * @param in the list of {@link AABBBlock} that will be contained in this group. | ||||
| 	 */ | ||||
| 	public AABBBlockGroup(Collection<AABBBlock> in) { | ||||
| 		if (in.isEmpty()) | ||||
| 			throw new IllegalArgumentException("Provided collection must not be empty."); | ||||
| 		subAABB = List.copyOf(in); | ||||
| 		englobingAABB = initEnglobingAABB(); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a new {@link AABBBlockGroup}. | ||||
| 	 * @param in an array of {@link AABBBlock} that will be contained in this group. | ||||
| 	 */ | ||||
| 	public AABBBlockGroup(AABBBlock... in) { | ||||
| 		this(Arrays.asList(in)); | ||||
| 	} | ||||
|   | ||||
| @@ -1,23 +1,58 @@ | ||||
| package fr.pandacube.lib.paper.geometry.blocks; | ||||
|  | ||||
| import org.bukkit.Location; | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.block.Block; | ||||
| import org.bukkit.entity.Entity; | ||||
| import org.bukkit.util.BlockVector; | ||||
| import org.bukkit.util.BoundingBox; | ||||
| import org.bukkit.util.Vector; | ||||
|  | ||||
| import java.util.Iterator; | ||||
|  | ||||
| /** | ||||
|  * Represents a set of blocks in a world. | ||||
|  */ | ||||
| public interface BlockSet extends Iterable<BlockVector> { | ||||
|  | ||||
|     /** | ||||
|      * Gets a random coordinate that is inside this block set. | ||||
|      * @return a random coordinate inside this block set. | ||||
|      */ | ||||
|     Vector getRandomPosition(); | ||||
|  | ||||
|     /** | ||||
|      * Gets the volume, in blocks (or cubic meters), of this block set. | ||||
|      * @return the volume of this block set. | ||||
|      */ | ||||
|     long getVolume(); | ||||
|  | ||||
|     /** | ||||
|      * Gets the englobing bounding box if this block set. | ||||
|      * @return the englobing bounding box if this block set. | ||||
|      */ | ||||
|     AABBBlock getEnglobingAABB(); | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Tells if this block set overlaps the provided bounding box. | ||||
|      * @param bb the provided bounding box | ||||
|      * @return true if its overlaps, false otherwise. | ||||
|      */ | ||||
|     boolean overlaps(BoundingBox bb); | ||||
|     /** | ||||
|      * Tells if this block set overlaps the bounding box of the provided entity. | ||||
|      * @param e the provided entity. | ||||
|      * @return true if its overlaps, false otherwise. | ||||
|      */ | ||||
|     default boolean overlaps(Entity e) { | ||||
|         return overlaps(e.getBoundingBox()); | ||||
|     } | ||||
|     /** | ||||
|      * Tells if this block set overlaps the provided one. that is there is at least one block in common. | ||||
|      * @param bs the provided block set. | ||||
|      * @return true if its overlaps, false otherwise. | ||||
|      */ | ||||
|     default boolean overlaps(BlockSet bs) { | ||||
|         if (this instanceof AABBBlock b1) { | ||||
|             if (bs instanceof AABBBlock b2) | ||||
| @@ -37,20 +72,78 @@ public interface BlockSet extends Iterable<BlockVector> { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Tells if the provided vector is inside this bounding box. | ||||
|      * @param v the vector. | ||||
|      * @return true if its inside, false otherwise. | ||||
|      */ | ||||
|     boolean isInside(Vector v); | ||||
|  | ||||
|     /** | ||||
|      * Tells if the provided location is inside this bounding box. | ||||
|      * The world of the location is ignored. | ||||
|      * @param l the location. | ||||
|      * @return true if its inside, false otherwise. | ||||
|      */ | ||||
|     default boolean isInside(Location l) { | ||||
|         return isInside(l.toVector()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tells if the provided block is inside this bounding box. | ||||
|      * The world of the block is ignored. | ||||
|      * @param b the block. | ||||
|      * @return true if its inside, false otherwise. | ||||
|      */ | ||||
|     default boolean isInside(Block b) { | ||||
|         return isInside(b.getLocation().add(.5, .5, .5)); | ||||
|     } | ||||
|     default boolean isInside(Entity p) { | ||||
|         return isInside(p.getLocation()); | ||||
|  | ||||
|     /** | ||||
|      * Tells if the provided entity is inside this bounding box. | ||||
|      * It calls {@link #isInside(Location)} using the returned value of {@link Entity#getLocation()}. | ||||
|      * The world of the entity is ignored. | ||||
|      * @param e the entity. | ||||
|      * @return true if its inside, false otherwise. | ||||
|      */ | ||||
|     default boolean isInside(Entity e) { | ||||
|         return isInside(e.getLocation()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Gets an iterator iterating through all the blocks of this block set. | ||||
|      * @param w the world of the blocks. | ||||
|      * @return a new iterator. | ||||
|      */ | ||||
|     default Iterable<Block> asBlockIterable(World w) { | ||||
|         return () -> new Iterator<>() { | ||||
|             final Iterator<BlockVector> nested = BlockSet.this.iterator(); | ||||
|             @Override | ||||
|             public boolean hasNext() { | ||||
|                 return nested.hasNext(); | ||||
|             } | ||||
|             @Override | ||||
|             public Block next() { | ||||
|                 BlockVector bv = nested.next(); | ||||
|                 return w.getBlockAt(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ()); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Tests the two block set overlap each other. | ||||
|      * This method works on any implementation of this interface, but they should override the | ||||
|      * {@link #overlaps(BlockSet)} method to provide a more optimized code. | ||||
|      * @param bs1 the first block set. | ||||
|      * @param bs2 the second block set. | ||||
|      * @return true if the two block set overlap, false otherwise. | ||||
|      */ | ||||
|     static boolean overlap(BlockSet bs1, BlockSet bs2) { | ||||
|         if (!bs1.getEnglobingAABB().overlaps(bs2.getEnglobingAABB())) | ||||
|             return false; | ||||
|   | ||||
| @@ -37,13 +37,21 @@ public class GUIHotBar implements Listener { | ||||
| 	private final int defaultSlot; | ||||
| 	 | ||||
| 	private final List<Player> currentPlayers = new ArrayList<>(); | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Setup 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); | ||||
| @@ -69,8 +78,9 @@ 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); | ||||
| 	} | ||||
| 	 | ||||
| @@ -144,7 +167,7 @@ public class GUIHotBar implements Listener { | ||||
| 	 | ||||
|  | ||||
| 	@EventHandler | ||||
| 	public void onPlayerDropItem(PlayerDropItemEvent event) { | ||||
| 	void onPlayerDropItem(PlayerDropItemEvent event) { | ||||
| 		if (!currentPlayers.contains(event.getPlayer())) | ||||
| 			return; | ||||
| 		 | ||||
| @@ -159,7 +182,7 @@ public class GUIHotBar implements Listener { | ||||
| 	 | ||||
| 	 | ||||
| 	@EventHandler | ||||
| 	public void onPlayerInteract(PlayerInteractEvent event) { | ||||
| 	void onPlayerInteract(PlayerInteractEvent event) { | ||||
| 		if (!currentPlayers.contains(event.getPlayer())) | ||||
| 			return; | ||||
| 		 | ||||
| @@ -188,7 +211,7 @@ public class GUIHotBar implements Listener { | ||||
| 	 | ||||
| 	 | ||||
| 	@EventHandler | ||||
| 	public void onInventoryClick(InventoryClickEvent event) { | ||||
| 	void onInventoryClick(InventoryClickEvent event) { | ||||
| 		if (event.getClickedInventory() == null || !(event.getClickedInventory() instanceof PlayerInventory inv)) | ||||
| 			return; | ||||
|  | ||||
| @@ -213,20 +236,20 @@ public class GUIHotBar implements Listener { | ||||
|  | ||||
| 	 | ||||
| 	@EventHandler | ||||
| 	public void onPlayerQuit(PlayerQuitEvent event) { | ||||
| 	void onPlayerQuit(PlayerQuitEvent event) { | ||||
| 		removePlayer(event.getPlayer()); | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	@EventHandler | ||||
| 	public void onPlayerDeath(PlayerDeathEvent event) { | ||||
| 	void onPlayerDeath(PlayerDeathEvent event) { | ||||
| 		if (!currentPlayers.contains(event.getEntity())) | ||||
| 			return; | ||||
| 		event.getDrops().removeAll(itemsAndSetters.keySet()); | ||||
| 	} | ||||
| 	 | ||||
| 	@EventHandler | ||||
| 	public void onPlayerRespawn(PlayerRespawnEvent event) { | ||||
| 	void onPlayerRespawn(PlayerRespawnEvent event) { | ||||
| 		if (!currentPlayers.contains(event.getPlayer())) | ||||
| 			return; | ||||
|  | ||||
|   | ||||
| @@ -38,7 +38,7 @@ public class GUIInventory implements Listener { | ||||
| 		if (title == null) | ||||
| 			inv = Bukkit.createInventory(null, nbLines * 9); | ||||
| 		else | ||||
| 			inv = Bukkit.createInventory(null, nbLines * 9, title.getAdv()); | ||||
| 			inv = Bukkit.createInventory(null, nbLines * 9, title.get()); | ||||
|  | ||||
| 		setCloseEvent(closeEventAction); | ||||
|  | ||||
| @@ -49,7 +49,11 @@ public class GUIInventory implements Listener { | ||||
| 		Bukkit.getPluginManager().registerEvents(this, PandaLibPaper.getPlugin()); | ||||
|  | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets the action when the player closes the inventory window. | ||||
| 	 * @param closeEventAction the action to run. | ||||
| 	 */ | ||||
| 	protected void setCloseEvent(Consumer<InventoryCloseEvent> closeEventAction) { | ||||
| 		onCloseEvent = closeEventAction; | ||||
| 	} | ||||
| @@ -188,13 +192,13 @@ public class GUIInventory implements Listener { | ||||
| 			onCloseEvent.accept(event); | ||||
| 		isOpened = false; | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
|  | ||||
| 	 | ||||
|  | ||||
| 	 | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Tells the number of inventory line needed to show the provided number of slot. | ||||
| 	 * @param nb the number of slot. | ||||
| 	 * @return the number of line. | ||||
| 	 */ | ||||
| 	public static int nbLineForNbElements(int nb) { | ||||
| 		return nb / 9 + ((nb % 9 == 0) ? 0 : 1); | ||||
| 	} | ||||
|   | ||||
| @@ -0,0 +1,191 @@ | ||||
| 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 { | ||||
|  | ||||
|     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() < 41) | ||||
|             throw new IllegalArgumentException("base inventory should have a size of 41 (" + 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); | ||||
|     } | ||||
|  | ||||
|     @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); | ||||
|             default -> throw new IllegalArgumentException("Not implemented. This is a bug"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @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 -> new ItemStack(Material.AIR); // for horses/wolves armor | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     @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; | ||||
|     } | ||||
| } | ||||
| @@ -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."); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| package fr.pandacube.lib.paper.util; | ||||
| package fr.pandacube.lib.paper.inventory; | ||||
| 
 | ||||
| import com.google.common.collect.Streams; | ||||
| import fr.pandacube.lib.chat.Chat; | ||||
| @@ -18,6 +18,9 @@ import java.util.function.Consumer; | ||||
| 
 | ||||
| import static fr.pandacube.lib.chat.ChatStatic.chatComponent; | ||||
| 
 | ||||
| /** | ||||
|  * A builder for {@link ItemStack}. | ||||
|  */ | ||||
| public class ItemStackBuilder { | ||||
| 
 | ||||
| 	/** | ||||
| @@ -77,10 +80,22 @@ public class ItemStackBuilder { | ||||
| 		return (cachedMeta != null) ? cachedMeta : (cachedMeta = stack.getItemMeta()); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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, m -> { | ||||
| 			metaUpdater.accept(m); | ||||
| @@ -88,38 +103,67 @@ public class ItemStackBuilder { | ||||
| 		}); | ||||
| 		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) { | ||||
| 		if (displayName != null) { | ||||
| 			return rawDisplayName(Chat.italicFalseIfNotSet(chatComponent(displayName)).asComponent()); | ||||
| 		} | ||||
| 		else | ||||
| 			return rawDisplayName(null); | ||||
| 		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)).getAdv()) | ||||
| 					.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 = getOrInitMeta().lore(); | ||||
| @@ -128,14 +172,19 @@ public class ItemStackBuilder { | ||||
| 					Streams.concat( | ||||
| 							baseLore.stream(), | ||||
| 							lores.stream() | ||||
| 							.map(line -> Chat.italicFalseIfNotSet(chatComponent(line)).getAdv()) | ||||
| 							.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; | ||||
| @@ -143,69 +192,120 @@ public class ItemStackBuilder { | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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) { | ||||
| 		if (apply) { | ||||
| 			enchant(Enchantment.UNBREAKING, 1); | ||||
| 			return hideEnchants(); | ||||
| 		} | ||||
| 		return this; | ||||
| 		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); | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 
 | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Build the {@link ItemStack}. | ||||
| 	 * @return the build item stack. | ||||
| 	 */ | ||||
| 	public ItemStack build() { | ||||
| 		return stack; | ||||
| 	} | ||||
| @@ -14,4 +14,7 @@ public class PaperJson { | ||||
|         Json.registerTypeAdapterFactory(ItemStackAdapter.FACTORY); | ||||
|         Json.registerTypeAdapterFactory(ConfigurationSerializableAdapter.FACTORY); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private PaperJson() {} | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import com.destroystokyo.paper.event.server.ServerTickEndEvent; | ||||
| import com.destroystokyo.paper.event.server.ServerTickStartEvent; | ||||
| import fr.pandacube.lib.chat.Chat; | ||||
| import fr.pandacube.lib.chat.ChatColorGradient; | ||||
| import fr.pandacube.lib.chat.ChatColorUtil; | ||||
| import fr.pandacube.lib.chat.ChatConfig.PandaTheme; | ||||
| import fr.pandacube.lib.paper.PandaLibPaper; | ||||
| import fr.pandacube.lib.paper.players.PaperOffPlayer; | ||||
| @@ -13,16 +12,15 @@ import fr.pandacube.lib.paper.scheduler.SchedulerUtil; | ||||
| import fr.pandacube.lib.paper.util.AutoUpdatedBossBar; | ||||
| import fr.pandacube.lib.paper.util.AutoUpdatedBossBar.BarUpdater; | ||||
| import fr.pandacube.lib.players.standalone.AbstractPlayerManager; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import fr.pandacube.lib.util.MemoryUtil; | ||||
| import fr.pandacube.lib.util.MemoryUtil.MemoryUnit; | ||||
| import fr.pandacube.lib.util.TimeUtil; | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import net.kyori.adventure.bossbar.BossBar; | ||||
| 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.md_5.bungee.api.ChatColor; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.command.CommandSender; | ||||
| import org.bukkit.command.ConsoleCommandSender; | ||||
| @@ -45,10 +43,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(); | ||||
| @@ -77,14 +82,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) | ||||
| @@ -95,23 +108,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) | ||||
| @@ -133,10 +130,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); | ||||
| @@ -144,7 +150,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); | ||||
| @@ -152,7 +162,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; | ||||
| @@ -160,7 +174,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; | ||||
| @@ -168,8 +186,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(); | ||||
| 	} | ||||
| @@ -179,7 +200,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; | ||||
| 		 | ||||
| @@ -187,7 +208,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; | ||||
| 		 | ||||
| @@ -214,7 +235,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(); | ||||
| @@ -234,7 +255,7 @@ public class PerformanceAnalysisManager implements Listener { | ||||
|  | ||||
|  | ||||
| 	@EventHandler | ||||
| 	public void onPlayerQuit(PlayerQuitEvent event) { | ||||
| 	void onPlayerQuit(PlayerQuitEvent event) { | ||||
| 		removePlayerToBars(event.getPlayer()); | ||||
| 	} | ||||
|  | ||||
| @@ -299,15 +320,17 @@ public class PerformanceAnalysisManager implements Listener { | ||||
| 					 | ||||
| 					// keep the legacy text when generating the bar to save space when converting to component | ||||
| 					StringBuilder s = new StringBuilder(); | ||||
| 					ChatColor prevC = ChatColor.RESET; | ||||
| 					TextColor prevC = null; | ||||
| 					for (int i = 58; i >= 0; i--) { | ||||
| 						int t = tpsHistory[i]; | ||||
| 						ChatColor newC = ChatColorUtil.toBungee(tps1sGradient.pickColorAt(t)); | ||||
| 						TextColor newC = tps1sGradient.pickColorAt(t); | ||||
| 						if (!newC.equals(prevC)) { | ||||
| 							s.append(newC); | ||||
| 							s.append(text("|").color(newC).getLegacyText()); | ||||
| 							prevC = newC; | ||||
| 						} | ||||
| 						s.append("|"); | ||||
| 						else { | ||||
| 							s.append("|"); | ||||
| 						} | ||||
| 					} | ||||
| 					 | ||||
| 					 | ||||
| @@ -374,28 +397,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; | ||||
|  | ||||
| @@ -409,7 +438,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 | ||||
| 	 */ | ||||
| @@ -428,8 +457,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]; | ||||
|  | ||||
| @@ -447,15 +480,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 garbase 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(); | ||||
| @@ -477,7 +517,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); | ||||
| 	} | ||||
|   | ||||
| @@ -1,14 +1,10 @@ | ||||
| package fr.pandacube.lib.paper.players; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.util.PrimaryWorlds; | ||||
| import fr.pandacube.lib.paper.world.PrimaryWorlds; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCDataConverter; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.dataconverter.MCTypeRegistry; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.SharedConstants; | ||||
| 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.reflect.wrapper.minecraft.nbt.Tag; | ||||
| import fr.pandacube.lib.paper.util.PlayerDataWrapper; | ||||
| 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; | ||||
| @@ -119,26 +115,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); | ||||
| @@ -154,29 +155,21 @@ public interface PaperOffPlayer extends AbstractOffPlayer { | ||||
|     /** | ||||
|      * Gets the NBT data from the player-data file. | ||||
|      * It will not work if the player is online, because the data on the file are not synchronized with real-time values. | ||||
|      * @param convertTag true to convert the data to the current MC version, false to keep the saved version | ||||
|      * @return the NBT data from the player-data file, or null if the file does not exists. | ||||
|      * @return the NBT data from the player-data file, or null if the file does not exist. | ||||
|      * @throws IllegalStateException if the player is online. | ||||
|      * @throws IOException if an error occurs reading the data. | ||||
|      */ | ||||
|     default CompoundTag getPlayerData(boolean convertTag) throws IOException { | ||||
|     default CompoundTag getPlayerData() { | ||||
|         if (isOnline()) | ||||
|             throw new IllegalStateException("Cannot access data file of " + getName() + " because they’re online."); | ||||
|         CompoundTag data = ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class) | ||||
|                 .getServer() | ||||
|                 .getPlayerList() | ||||
|                 .playerIo() | ||||
|                 .getPlayerData(getUniqueId().toString()); | ||||
|         if (data != null && convertTag) { | ||||
|             int srcVersion = data.contains("DataVersion", Tag.TAG_ANY_NUMERIC()) ? data.getInt("DataVersion") : -1; | ||||
|             int destVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); | ||||
|             try { | ||||
|                 data = MCDataConverter.convertTag(MCTypeRegistry.PLAYER(), data, srcVersion, destVersion); | ||||
|             } catch (Exception e) { | ||||
|                 throw new IOException("Unable to upgrade data format of player " + getName() + " (" + getUniqueId() + ") from version " + destVersion + " to " + destVersion); | ||||
|             } | ||||
|             throw new IllegalStateException("Cannot access data file of " + getName() + " because they're online."); | ||||
|         try { | ||||
|             return ReflectWrapper.wrapTyped(Bukkit.getServer(), CraftServer.class) | ||||
|                     .getServer() | ||||
|                     .getPlayerList() | ||||
|                     .playerIo() | ||||
|                     .load(getName(), getUniqueId().toString()).orElse(null); | ||||
|         } catch (Exception|LinkageError e) { | ||||
|             throw new PlayerDataLoadException(getName(), getUniqueId(), e); | ||||
|         } | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -184,10 +177,9 @@ public interface PaperOffPlayer extends AbstractOffPlayer { | ||||
|      * It will not work if the player is online, because the data on the file are not synchronized with real-time values. | ||||
|      * @return the NBT data from the player-data file. | ||||
|      * @throws IllegalStateException if the player is online. | ||||
|      * @throws IOException if an error occurs reading the data. | ||||
|      */ | ||||
|     default PlayerDataWrapper getPlayerDataWrapper() throws IOException { | ||||
|         return new PlayerDataWrapper(getPlayerData(true)); | ||||
|     default PlayerDataWrapper getPlayerDataWrapper() { | ||||
|         return new PlayerDataWrapper(getPlayerData()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -213,25 +205,23 @@ public interface PaperOffPlayer extends AbstractOffPlayer { | ||||
|      * @return the file where the player-data is stored. | ||||
|      */ | ||||
|     default File getPlayerDataFile(boolean old) { | ||||
|         File playerDataDir = new File(WorldUtil.worldDir(PrimaryWorlds.PRIMARY_WORLDS.get(0)), "playerdata"); | ||||
|         File playerDataDir = new File(WorldUtil.worldDir(PrimaryWorlds.PRIMARY_WORLDS.getFirst()), "playerdata"); | ||||
|         return new File(playerDataDir, getUniqueId() + (old ? ".dat_old" : ".dat")); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the player’s inventory. | ||||
|      * @return the player’s inventory. | ||||
|      * @throws IOException if an error occurs reading the data. | ||||
|      */ | ||||
|     default PlayerInventory getInventory() throws IOException { | ||||
|     default PlayerInventory getInventory() { | ||||
|         return getPlayerDataWrapper().getInventory(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the player’s enderchest. | ||||
|      * @return the player’s enderchest. | ||||
|      * @throws IOException if an error occurs reading the data. | ||||
|      */ | ||||
|     default Inventory getEnderChest() throws IOException { | ||||
|     default Inventory getEnderChest() { | ||||
|         return getPlayerDataWrapper().getEnderChest(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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() {} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,236 @@ | ||||
| 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.nbt.ListTag; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.Tag; | ||||
| import fr.pandacube.lib.paper.util.ExperienceUtil; | ||||
| 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.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) { | ||||
|         if (!data.contains(key, Tag.TAG_LIST())) | ||||
|             return Map.of(); | ||||
|         ListTag list = data.getList(key, Tag.TAG_COMPOUND()); | ||||
|         if (list == null) | ||||
|             return Map.of(); | ||||
|  | ||||
|         Map<Integer, ItemStack> stacks = new TreeMap<>(); | ||||
|         for (int i = 0; i < list.size(); i++) { | ||||
|             CompoundTag itemTag = list.getCompound(i); | ||||
|             int nbtSlot = itemTag.getByte("Slot") & 255; | ||||
|             fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.parse(itemTag) | ||||
|                     .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) { | ||||
|         ListTag list = new ListTag(); | ||||
|         for (Entry<Integer, ItemStack> is : stacks.entrySet()) { | ||||
|             ItemStack stack = filterStack(is.getValue()); | ||||
|             if (stack == null) | ||||
|                 continue; | ||||
|             CompoundTag itemTag = new CompoundTag(); | ||||
|             itemTag.putByte("Slot", is.getKey().byteValue()); | ||||
|             list.add(list.size(), CraftItemStack.asNMSCopy(is.getValue()).save(itemTag)); | ||||
|         } | ||||
|         data.put(key, list); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private ItemStack filterStack(ItemStack is) { | ||||
|         return is == null || is.getType().isEmpty() || is.getAmount() == 0 ? null : is; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private int getHeldItemSlot() { | ||||
|         if (!data.contains("SelectedItemSlot")) | ||||
|             return 0; | ||||
|         return data.getInt("SelectedItemSlot"); | ||||
|     } | ||||
|  | ||||
|     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() { | ||||
|         if (!data.contains("Score")) | ||||
|             return 0; | ||||
|         return data.getInt("Score"); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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() { | ||||
|         if (!data.contains("XpTotal")) | ||||
|             return 0; | ||||
|         return data.getInt("XpTotal"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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() {} | ||||
| } | ||||
|   | ||||
| @@ -1,716 +0,0 @@ | ||||
| package fr.pandacube.lib.paper.reflect; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| import java.io.PrintStream; | ||||
| import java.io.StringReader; | ||||
| import java.lang.reflect.Constructor; | ||||
| import java.lang.reflect.Modifier; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.TreeMap; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import org.bukkit.Bukkit; | ||||
|  | ||||
| import fr.pandacube.lib.util.log.Log; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectField; | ||||
| import fr.pandacube.lib.reflect.ReflectMember; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
| import net.fabricmc.mappingio.MappingReader; | ||||
| import net.fabricmc.mappingio.format.MappingFormat; | ||||
| import net.fabricmc.mappingio.tree.MappingTree; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
|  | ||||
| /** | ||||
|  * Provides reflection tools related to Minecraft servers internals. | ||||
|  * It automatically deals with the obfuscated classes and methods. | ||||
|  */ | ||||
| public class NMSReflect { | ||||
| 	 | ||||
|  | ||||
| 	private static String OBF_NAMESPACE; | ||||
| 	private static String MOJ_NAMESPACE; | ||||
|  | ||||
| 	/* package */ static final Map<String, ClassMapping> CLASSES_BY_OBF = new TreeMap<>(); | ||||
| 	/* package */ static final Map<String, ClassMapping> CLASSES_BY_MOJ = new TreeMap<>(); | ||||
| 	 | ||||
| 	private static Boolean IS_SERVER_OBFUSCATED; | ||||
| 	 | ||||
| 	private static boolean isInit = false; | ||||
|  | ||||
| 	/** | ||||
| 	 * Initialize all the obfuscation mapping data. | ||||
| 	 */ | ||||
| 	public static void init() { | ||||
| 		 | ||||
| 		synchronized (NMSReflect.class) { | ||||
| 			if (isInit) | ||||
| 				return; | ||||
| 			isInit = true; | ||||
| 		} | ||||
| 		 | ||||
| 		Log.info("[NMSReflect] Initializing NMS obfuscation mapping..."); | ||||
| 		 | ||||
| 		try { | ||||
| 			ReflectClass<?> obfHelperClass; | ||||
| 			try { | ||||
| 				obfHelperClass = Reflect.ofClass("io.papermc.paper.util.ObfHelper"); | ||||
| 	 | ||||
| 				OBF_NAMESPACE = (String) obfHelperClass.field("SPIGOT_NAMESPACE").getStaticValue(); | ||||
| 				MOJ_NAMESPACE = (String) obfHelperClass.field("MOJANG_PLUS_YARN_NAMESPACE").getStaticValue(); | ||||
| 			} catch (ReflectiveOperationException e) { | ||||
| 				throw new ReflectiveOperationException("Unable to find the Paper obfuscation mapping class or class members.", e); | ||||
| 			} | ||||
| 			 | ||||
| 			List<ClassMapping> mappings = loadMappings(obfHelperClass); | ||||
| 			for (ClassMapping clazz : mappings) { | ||||
| 				CLASSES_BY_OBF.put(clazz.obfName, clazz); | ||||
| 				CLASSES_BY_MOJ.put(clazz.mojName, clazz); | ||||
| 			} | ||||
| 			 | ||||
| 			// determine if the runtime server is obfuscated | ||||
| 			ClassNotFoundException exIfUnableToDetermine = null; | ||||
| 			for (ClassMapping clazz : CLASSES_BY_OBF.values()) { | ||||
| 				if (clazz.obfName.equals(clazz.mojName) // avoid direct collision between obf and unobf class names | ||||
| 						|| CLASSES_BY_MOJ.containsKey(clazz.obfName) // avoid indirect collision | ||||
| 						|| CLASSES_BY_OBF.containsKey(clazz.mojName))// avoid indirect collision | ||||
| 					continue; | ||||
| 				 | ||||
| 				try { | ||||
| 					Class.forName(clazz.obfName); | ||||
| 					IS_SERVER_OBFUSCATED = true; | ||||
| 					break; | ||||
| 				} catch (ClassNotFoundException e) { | ||||
| 					try { | ||||
| 						Class.forName(clazz.mojName); | ||||
| 						IS_SERVER_OBFUSCATED = false; | ||||
| 						break; | ||||
| 					} catch (ClassNotFoundException ee) { | ||||
| 						ee.addSuppressed(e); | ||||
| 						if (exIfUnableToDetermine == null) | ||||
| 							exIfUnableToDetermine = ee; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if (IS_SERVER_OBFUSCATED == null) { | ||||
| 				throw new IllegalStateException("Unable to determine if this server is obfuscated or not", exIfUnableToDetermine); | ||||
| 			} | ||||
| 			if (IS_SERVER_OBFUSCATED) { | ||||
| 				Log.info("[NMSReflect] NMS runtime classes are obfuscated."); | ||||
| 			} | ||||
| 			else { | ||||
| 				Log.info("[NMSReflect] NMS runtime classes are mojang mapped."); | ||||
| 			} | ||||
| 			 | ||||
| 			int missingRuntimeClasses = 0; | ||||
| 			for (ClassMapping clazz : mappings) { | ||||
| 				try { | ||||
| 					clazz.cacheReflectClass(); | ||||
| 				} catch (Throwable e) { | ||||
| 					missingRuntimeClasses++; | ||||
| 					if (e instanceof ClassNotFoundException cnfe) { | ||||
| 						Log.warning("[NMSReflect] Missing runtime class " + cnfe.getMessage() + (IS_SERVER_OBFUSCATED ? (" (moj class: " + clazz.mojName + ")") : "")); | ||||
| 					} | ||||
| 					else { | ||||
| 						Log.warning("[NMSReflect] Unable to load runtime class " + (IS_SERVER_OBFUSCATED ? (clazz.obfName + " (moj class: " + clazz.mojName + ")") : clazz.mojName)); | ||||
| 						Log.warning(e); // throwable on separate log message due to sometimes the message not showing at all because of this exception | ||||
| 					} | ||||
| 					CLASSES_BY_OBF.remove(clazz.obfName); | ||||
| 					CLASSES_BY_MOJ.remove(clazz.mojName); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if (missingRuntimeClasses > 0) { | ||||
| 				Log.warning("[NMSReflect] " + missingRuntimeClasses + " class have been removed from the mapping data due to the previously stated errors."); | ||||
| 			} | ||||
| 			 | ||||
| 		} catch (Throwable t) { | ||||
| 			CLASSES_BY_OBF.clear(); | ||||
| 			CLASSES_BY_MOJ.clear(); | ||||
| 			Log.severe("[NMSReflect] The plugin will not have access to NMS stuff due to an error while loading the obfuscation mapping.", t); | ||||
| 		} | ||||
| 		Log.info("[NMSReflect] Obfuscation mapping loaded for " + CLASSES_BY_OBF.size() + " classes."); | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Returns the class mapping instance for the provided class. | ||||
| 	 * @param mojName the binary name of the desired class, on the mojang mapping. | ||||
| 	 * @return the class mapping instance for the provided class. | ||||
| 	 * @throws NullPointerException if there is no mapping for the provided Mojang mapped class. | ||||
| 	 */ | ||||
| 	public static ClassMapping mojClass(String mojName) { | ||||
| 		return Objects.requireNonNull(CLASSES_BY_MOJ.get(mojName), "Unable to find the Mojang mapped class '" + mojName + "'"); | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
|     private static List<ClassMapping> loadMappings(ReflectClass<?> obfHelperClass) throws IOException { | ||||
|         try (final InputStream mappingsInputStream = obfHelperClass.get().getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) { | ||||
|             if (mappingsInputStream == null) { | ||||
|             	throw new RuntimeException("Unable to find the obfuscation mapping file in the Paper jar."); | ||||
|             } | ||||
|              | ||||
|             MemoryMappingTree tree = new MemoryMappingTree(); | ||||
|             MappingReader.read(new InputStreamReader(mappingsInputStream, StandardCharsets.UTF_8), MappingFormat.TINY_2_FILE, tree); | ||||
|              | ||||
|             List<ClassMapping> classes = new ArrayList<>(); | ||||
|             for (MappingTree.ClassMapping cls : tree.getClasses()) { | ||||
|                 classes.add(new ClassMapping(cls)); | ||||
|             } | ||||
|             return classes; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * Prints an HTML rendering of the currently loaded obfuscation mapping, into the provided {@link PrintStream}. | ||||
| 	 * @param out the stream in which to print the HTML content. | ||||
| 	 */ | ||||
| 	public static void printHTMLMapping(PrintStream out) { | ||||
| 		String title = "Obfuscation mapping - " + Bukkit.getName() + " version " + Bukkit.getVersion(); | ||||
| 		out.println("<!DOCTYPE html><html><head>\n" | ||||
| 				+ "<title>" + title + "</title>\n" | ||||
| 				+ """ | ||||
| 				<style> | ||||
| 					html { | ||||
| 						background-color: #2F2F2F; | ||||
| 						color: white; | ||||
| 						font-size: 14px; | ||||
| 						font-family: Consolas, monospace; | ||||
| 					} | ||||
| 					a:not(.cl) { | ||||
| 						color: #1290C3; | ||||
| 					} | ||||
| 					table { | ||||
| 						border-collapse: collapse; | ||||
| 						width: 100%; | ||||
| 						margin: auto; | ||||
| 					} | ||||
| 					tr:nth-child(2n) { | ||||
| 						background-color: #373737; | ||||
| 					} | ||||
| 					tr:hover { | ||||
| 						background-color: #555; | ||||
| 					} | ||||
| 					tr > *:first-child { | ||||
| 						padding-right: .5em; | ||||
| 						white-space: nowrap; | ||||
| 					} | ||||
| 					 | ||||
| 					b.pu { | ||||
| 						color: #0C0; | ||||
| 					} | ||||
| 					b.pt { | ||||
| 						color: #FC0; | ||||
| 					} | ||||
| 					b.pv { | ||||
| 						color: #F00; | ||||
| 					} | ||||
| 					b.pk { | ||||
| 						color: #66F; | ||||
| 					} | ||||
| 					td { | ||||
| 						padding-top: 0; | ||||
| 						padding-bottom: 0; | ||||
| 					} | ||||
| 					th { | ||||
| 						text-align: left; | ||||
| 						font-size: 1.1em; | ||||
| 						border-top: solid 1px white; | ||||
| 					} | ||||
| 					.kw { | ||||
| 						color: #CC6C1D; | ||||
| 					} | ||||
| 					.cl { | ||||
| 						color: #1290C3; | ||||
| 					} | ||||
| 					.mtd { | ||||
| 						color: #1EB540; | ||||
| 					} | ||||
| 					.fld { | ||||
| 						color: #8DDAF8; | ||||
| 					} | ||||
| 					.st { | ||||
| 						font-style: italic; | ||||
| 					} | ||||
| 					.st.fn { | ||||
| 						font-weight: bold; | ||||
| 					} | ||||
| 				</style> | ||||
| 				</head><body> | ||||
| 				""" | ||||
| 				+ "<h1>" + title + "</h1>\n" | ||||
| 				+ """ | ||||
| 				<p> | ||||
| 					<b>C</b>: <span class='kw'>class</span>   | ||||
| 					<b>E</b>: <span class='kw'>enum</span>   | ||||
| 					<b>I</b>: <span class='kw'>interface</span>   | ||||
| 					<b>@</b>: <span class='kw'>@interface</span>   | ||||
| 					<b>R</b>: <span class='kw'>record</span><br> | ||||
| 					<b>●</b>: field   | ||||
| 					<b>c</b>: constructor   | ||||
| 					<b>⬤</b>: method<br> | ||||
| 					<b class='pu'>⬤</b>: <span class='kw'>public</span>   | ||||
| 					<b class='pt'>⬤</b>: <span class='kw'>protected</span>   | ||||
| 					<b class='pk'>⬤</b>: package   | ||||
| 					<b class='pv'>⬤</b>: <span class='kw'>private</span><br> | ||||
| 					<sup>S</sup>: <span class='kw'>static</span>   | ||||
| 					<sup>A</sup>: <span class='kw'>abstract</span>   | ||||
| 					<sup>F</sup>: <span class='kw'>final</span> | ||||
| 				</p> | ||||
| 				<table> | ||||
| 				"""); | ||||
| 		out.println("<tr><th>ns</th><th>" + OBF_NAMESPACE + "</th><th>" + MOJ_NAMESPACE + "</th></tr>"); | ||||
| 		for (ClassMapping clazz : CLASSES_BY_OBF.values()) { | ||||
| 			clazz.printHTML(out); | ||||
| 		} | ||||
| 		out.println("</table><p>Generated by <a href='https://github.com/marcbal'>marcbal</a>" | ||||
| 				+ " using <a href='https://github.com/PandacubeFr/PandaLib/blob/master/Paper/src/main/java/fr/pandacube/lib/paper/reflect/NMSReflect.java'>this tool</a>" | ||||
| 				+ " running on <a href='https://papermc.io/'>" + Bukkit.getName() + "</a> version " + Bukkit.getVersion() + "</p>" | ||||
| 				+ "</body></html>"); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * Represents the mapping between the obfuscated and Mojang names of a class and all its members. | ||||
| 	 */ | ||||
| 	public static class ClassMapping { | ||||
|     	private static int nextID = 0; | ||||
|     	 | ||||
|     	/* package */ final int id = nextID++; | ||||
| 		/* package */ final String obfName; | ||||
| 		/* package */ final String mojName; | ||||
|  | ||||
| 		private final Map<MethodId, MemberMapping<MethodId, ReflectMethod<?>>> methodsByObf = new TreeMap<>(); | ||||
| 		private final Map<MethodId, MemberMapping<MethodId, ReflectMethod<?>>> methodsByMoj = new TreeMap<>(); | ||||
| 		private final Map<String, MemberMapping<String, ReflectField<?>>> fieldsByObf = new TreeMap<>(); | ||||
| 		private final Map<String, MemberMapping<String, ReflectField<?>>> fieldsByMoj = new TreeMap<>(); | ||||
| 		 | ||||
| 		private ReflectClass<?> runtimeReflectClass = null; | ||||
| 		 | ||||
| 		private ClassMapping(MappingTree.ClassMapping cls) { | ||||
|             obfName = binaryClassName(cls.getName(OBF_NAMESPACE)); | ||||
|             mojName = binaryClassName(cls.getName(MOJ_NAMESPACE)); | ||||
| 			 | ||||
| 			cls.getMethods().stream().map(MemberMapping::of).forEach(method -> { | ||||
| 				method.declaringClass = this; | ||||
| 				methodsByObf.put(method.obfDesc.identifier, method); | ||||
| 				methodsByMoj.put(method.mojDesc.identifier, method); | ||||
| 			}); | ||||
|             cls.getFields().stream().map(MemberMapping::of).forEach(field -> { | ||||
|             	field.declaringClass = this; | ||||
| 				fieldsByObf.put(field.obfDesc.identifier, field); | ||||
| 				fieldsByMoj.put(field.mojDesc.identifier, field); | ||||
|             }); | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		 | ||||
| 		private synchronized void cacheReflectClass() throws ClassNotFoundException { | ||||
| 			if (runtimeReflectClass == null) | ||||
| 				runtimeReflectClass = Reflect.ofClass(IS_SERVER_OBFUSCATED ? obfName : mojName); | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		/** | ||||
| 		 * Returns the actual runtime {@link Class} represented by this {@link ClassMapping}, wrapped into a | ||||
| 		 * {@link ReflectClass}. | ||||
| 		 * @return the actual runtime {@link Class} represented by this {@link ClassMapping}, wrapped into a | ||||
| 		 * 		 * {@link ReflectClass}. | ||||
| 		 */ | ||||
| 		public ReflectClass<?> runtimeReflect() { | ||||
| 			return runtimeReflectClass; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Returns the actual runtime Class represented by this {@link ClassMapping}. | ||||
| 		 * @return the actual runtime Class represented by this {@link ClassMapping}. | ||||
| 		 */ | ||||
| 		public Class<?> runtimeClass() { | ||||
| 			return runtimeReflectClass.get(); | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Returns the actual runtime Method that has the provided mojang name and parameter types, wrapped into a | ||||
| 		 * {@link ReflectMethod}. | ||||
| 		 * @param mojName the Mojang mapped name of the method. | ||||
| 		 * @param mojParametersType the list of parameters of the method. | ||||
| 		 * Each parameter type must be an instance of one of the following type: | ||||
| 		 * {@link NMSTypeWrapper}, {@link Class}, {@link ReflectClass} or {@link ClassMapping}. | ||||
| 		 * @return the actual runtime Method that has the provided mojang name and parameter types, wrapped into a | ||||
| 		 *         {@link ReflectMethod}. | ||||
| 		 * @throws IllegalArgumentException if one of the parameter has an invalid type | ||||
| 		 * @throws NullPointerException if one of the parameter is null, or if there is no mapping for the provided Mojang mapped method. | ||||
| 		 * @throws ClassNotFoundException if there is no runtime class to represent one of the provided parametersType. | ||||
| 		 * @throws NoSuchMethodException if there is no runtime method to represent the provided method. | ||||
| 		 */ | ||||
| 		public ReflectMethod<?> mojMethod(String mojName, Object... mojParametersType) throws ClassNotFoundException, NoSuchMethodException { | ||||
| 			MethodId mId = new MethodId(mojName, NMSTypeWrapper.toTypeList(Arrays.asList(mojParametersType))); | ||||
| 			MemberMapping<MethodId, ReflectMethod<?>> mm = methodsByMoj.get(mId); | ||||
| 			Objects.requireNonNull(mm, "Unable to find the Mojang mapped method " + mId); | ||||
| 			 | ||||
| 			try { | ||||
| 				return mm.getReflectMember(); | ||||
| 			} catch (ReflectiveOperationException e) { | ||||
| 				if (e instanceof ClassNotFoundException cnfe) | ||||
| 					throw cnfe; | ||||
| 				if (e instanceof NoSuchMethodException nsme) | ||||
| 					throw nsme; | ||||
| 				// should not have another exception | ||||
| 				throw new RuntimeException(e); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
|  | ||||
| 		/** | ||||
| 		 * Returns the actual runtime Field that has the provided mojang name, wrapped into a {@link ReflectField}. | ||||
| 		 * @param mojName the Mojang mapped name of the field. | ||||
| 		 * @return the actual runtime Field that has the provided mojang name, wrapped into a {@link ReflectField}. | ||||
| 		 * @throws NullPointerException if there is no mapping for the provided Mojang mapped field. | ||||
| 		 * @throws NoSuchFieldException if there is no runtime field to represent the provided mojang field. | ||||
| 		 */ | ||||
| 		public ReflectField<?> mojField(String mojName) throws NoSuchFieldException { | ||||
| 			MemberMapping<String, ReflectField<?>> fm = fieldsByMoj.get(mojName); | ||||
| 			Objects.requireNonNull(fm, "Unable to find the Mojang mapped field '" + mojName + "'"); | ||||
| 			try { | ||||
| 				return fm.getReflectMember(); | ||||
| 			} catch (ReflectiveOperationException e) { | ||||
| 				if (e instanceof NoSuchFieldException nsfe) | ||||
| 					throw nsfe; | ||||
| 				// should not have another exception | ||||
| 				throw new RuntimeException(e); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
|  | ||||
| 		 | ||||
| 		/* package */ String toClickableHTML(boolean isObfClass) { | ||||
| 	    	String classToPrint = isObfClass ? obfName : mojName; | ||||
| 	    	String classSimpleName = classToPrint.substring(classToPrint.lastIndexOf('.') + 1); | ||||
| 	    	String htmlTitle = classSimpleName.equals(classToPrint) ? "" : (" title='" + classToPrint + "'"); | ||||
| 			return "<a href='#c" + id + "'" + htmlTitle + " class='cl'>" + classSimpleName + "</a>"; | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
| 		 | ||||
| 		/* package */ NMSTypeWrapper toType(boolean obf) { | ||||
| 			return new NMSTypeWrapper(obf ? obfName : mojName, 0); | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		private void printHTML(PrintStream out) { | ||||
| 			String modifiersHTML = classModifiersToHTML(runtimeClass()); | ||||
| 			out.println("<tr id='c" + id + "'><th>" + modifiersHTML + "</th><th>" + nameToHTML(true) + "</th><th>" + nameToHTML(false) + "</th></tr>"); | ||||
| 			fieldsByObf.values().stream().filter(MemberMapping::isStatic).forEach(f -> f.printHTML(out)); | ||||
| 			methodsByObf.values().stream().filter(MemberMapping::isStatic).forEach(m -> m.printHTML(out)); | ||||
| 			printConstructorsHTML(out); | ||||
| 			fieldsByObf.values().stream().filter(mm -> !mm.isStatic()).forEach(f -> f.printHTML(out)); | ||||
| 			methodsByObf.values().stream().filter(mm -> !mm.isStatic()).forEach(m -> m.printHTML(out)); | ||||
| 		} | ||||
| 		 | ||||
| 		private String nameToHTML(boolean obf) { | ||||
| 			String classToPrint = obf ? obfName : mojName; | ||||
| 			int packageSep = classToPrint.lastIndexOf('.'); | ||||
| 	    	String classSimpleName = classToPrint.substring(packageSep + 1); | ||||
| 	    	String classPackages = classToPrint.substring(0, Math.max(packageSep, 0)); | ||||
| 	    	String classHTML = (packageSep >= 0 ? (classPackages + ".") : "") + "<b class='cl'>" + classSimpleName + "</b>"; | ||||
| 			 | ||||
| 			NMSTypeWrapper superClass = superClass(obf); | ||||
| 			String superClassHTML = superClass == null ? "" : (" <span class='kw'>extends</span> " + superClass.toHTML(obf)); | ||||
| 			 | ||||
| 			List<NMSTypeWrapper> superInterfaces = superInterfaces(obf); | ||||
| 			String superInterfacesHTML = superInterfaces.isEmpty() ? "" | ||||
| 					: (" <span class='kw'>implements</span> " + superInterfaces.stream().map(t -> t.toHTML(obf)).collect(Collectors.joining(", "))); | ||||
| 			 | ||||
| 			return classHTML + superClassHTML + superInterfacesHTML; | ||||
| 		} | ||||
| 		 | ||||
| 		private NMSTypeWrapper superClass(boolean obf) { | ||||
| 			Class<?> superClass = runtimeClass().getSuperclass(); | ||||
| 			if (superClass == null || superClass.equals(Object.class) || superClass.equals(Enum.class) || superClass.equals(Record.class)) | ||||
| 				return null; | ||||
| 			ClassMapping cm = (IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ).get(superClass.getName()); | ||||
| 			return (cm != null) ? cm.toType(obf) : NMSTypeWrapper.of(superClass); | ||||
| 		} | ||||
| 		 | ||||
| 		private List<NMSTypeWrapper> superInterfaces(boolean obf) { | ||||
| 			Class<?>[] interfaces = runtimeClass().getInterfaces(); | ||||
| 			List<NMSTypeWrapper> types = new ArrayList<>(interfaces.length); | ||||
| 			for (Class<?> i : interfaces) { | ||||
| 				ClassMapping cm = (IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ).get(i.getName()); | ||||
| 				types.add((cm != null) ? cm.toType(obf) : NMSTypeWrapper.of(i)); | ||||
| 			} | ||||
| 			return types; | ||||
| 		} | ||||
| 		 | ||||
| 	     | ||||
| 	    private void printConstructorsHTML(PrintStream out) { | ||||
| 	    	String classObfSimpleName = obfName.substring(obfName.lastIndexOf('.') + 1); | ||||
| 	    	String classMojSimpleName = mojName.substring(mojName.lastIndexOf('.') + 1); | ||||
| 			for (Constructor<?> ct : runtimeClass().getDeclaredConstructors()) { | ||||
| 				List<NMSTypeWrapper> obfParams = new ArrayList<>(); | ||||
| 				List<NMSTypeWrapper> mojParams = new ArrayList<>(); | ||||
| 				for (Class<?> param : ct.getParameterTypes()) { | ||||
| 					ClassMapping cm = (IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ).get(param.getName()); | ||||
| 					if (cm == null) { | ||||
| 						NMSTypeWrapper t = NMSTypeWrapper.of(param); | ||||
| 						obfParams.add(t); | ||||
| 						mojParams.add(t); | ||||
| 					} | ||||
| 					else { | ||||
| 						obfParams.add(cm.toType(true)); | ||||
| 						mojParams.add(cm.toType(false)); | ||||
| 					} | ||||
| 				} | ||||
| 				out.println("<tr>" | ||||
| 						+ "<td>" + elementModifiersToHTML("c", ct.getModifiers()) + "</td>" | ||||
| 						+ "<td><b class='mtd'>" + classObfSimpleName + "</b>(" + obfParams.stream().map(t -> t.toHTML(true)).collect(Collectors.joining(", ")) + ")</td>" | ||||
| 						+ "<td><b class='mtd'>" + classMojSimpleName + "</b>(" + mojParams.stream().map(t -> t.toHTML(false)).collect(Collectors.joining(", ")) + ")</td>" | ||||
| 						+ "</tr>"); | ||||
| 			} | ||||
| 	    	 | ||||
| 	    } | ||||
|     } | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|     private record MethodId(String name, List<NMSTypeWrapper> parametersType) implements Comparable<MethodId> { | ||||
|     	@Override | ||||
|     	public int compareTo(MethodId o) { | ||||
|     		int cmp = name.compareTo(o.name); | ||||
|     		if (cmp != 0) | ||||
|     			return cmp; | ||||
|     		return toString().compareTo(o.toString()); | ||||
|     	} | ||||
|     	 | ||||
| 		private String toHTML(boolean isObfClass, boolean isStatic, boolean isFinal) { | ||||
| 			String paramsHTML = parametersType.stream().map(p -> p.toHTML(isObfClass)).collect(Collectors.joining(", ")); | ||||
| 			String cl = "mtd"; | ||||
| 			if (isStatic) | ||||
| 				cl += " st"; | ||||
| 			if (isFinal) | ||||
| 				cl += " fn"; | ||||
| 			return "<span class='" + cl + "'>" + name + "</span>(" + paramsHTML + ")"; | ||||
| 		} | ||||
| 		 | ||||
| 		public String toString() { | ||||
| 			String paramsStr = parametersType.stream().map(NMSTypeWrapper::toString).collect(Collectors.joining(", ")); | ||||
| 			return name + "(" + paramsStr + ")"; | ||||
| 		} | ||||
|     	 | ||||
|     } | ||||
|  | ||||
|  | ||||
|      | ||||
|     private record MemberDesc<I extends Comparable<I>>(I identifier, NMSTypeWrapper returnType) { | ||||
| 		private String toHTML(boolean isObfClass, boolean isStatic, boolean isFinal) { | ||||
| 			String identifierHTML = ""; | ||||
| 			if (identifier instanceof MethodId mId) | ||||
| 				identifierHTML = mId.toHTML(isObfClass, isStatic, isFinal); | ||||
| 			else if (identifier instanceof String n) { | ||||
| 				String cl = "fld"; | ||||
| 				if (isStatic) | ||||
| 					cl += " st"; | ||||
| 				if (isFinal) | ||||
| 					cl += " fn"; | ||||
| 				identifierHTML = "<span class='" + cl + "'>" + n + "</span>"; | ||||
| 			} | ||||
| 			return returnType.toHTML(isObfClass) + " " + identifierHTML; | ||||
| 		} | ||||
| 		 | ||||
| 		private static MemberDesc<MethodId> of(MappingTree.MethodMapping member, String namespace) { | ||||
| 			String desc = member.getDesc(namespace); | ||||
| 			try (StringReader descReader = new StringReader(desc)) { | ||||
| 				char r = (char) descReader.read(); | ||||
| 				if (r != '(') | ||||
| 					throw new IllegalArgumentException("Invalid method description '" + desc + "'. Must start with '('."); | ||||
| 				 | ||||
| 				List<NMSTypeWrapper> paramsType = new ArrayList<>(); | ||||
| 				 | ||||
| 				while (((char) descReader.read()) != ')') { | ||||
| 					descReader.skip(-1); | ||||
| 					paramsType.add(NMSTypeWrapper.parse(descReader)); | ||||
| 				} | ||||
| 				 | ||||
| 				NMSTypeWrapper retType = NMSTypeWrapper.parse(descReader); | ||||
| 				return new MemberDesc<>(new MethodId(member.getName(namespace), Collections.unmodifiableList(paramsType)), retType); | ||||
| 			} catch (IOException e) { | ||||
| 				throw new RuntimeException("StringReader read error", e); | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
|  | ||||
| 		 | ||||
| 		private static MemberDesc<String> of(MappingTree.FieldMapping member, String namespace) { | ||||
| 			StringReader descReader = new StringReader(member.getDesc(namespace)); | ||||
| 			return new MemberDesc<>(member.getName(namespace), NMSTypeWrapper.parse(descReader)); | ||||
| 		} | ||||
|     } | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|     private static abstract class MemberMapping<I extends Comparable<I>, R extends ReflectMember<?, ?, ?, ?>> { | ||||
|     	private final String htmlTypeChar; | ||||
|     	/* package */ final MemberDesc<I> obfDesc, mojDesc; | ||||
|     	/* package */ ClassMapping declaringClass; | ||||
|     	private MemberMapping(String htmlType, MemberDesc<I> obfDesc, MemberDesc<I> mojDesc) { | ||||
|     		htmlTypeChar = htmlType; | ||||
|     		this.obfDesc = obfDesc; | ||||
|     		this.mojDesc = mojDesc; | ||||
| 		} | ||||
|     	 | ||||
| 		/* package */ void printHTML(PrintStream out) { | ||||
| 			int mod = 0; | ||||
| 			try { | ||||
| 				mod = getReflectMember().getModifiers(); | ||||
| 			} catch (ReflectiveOperationException e) { | ||||
| 				// ignore | ||||
| 			} | ||||
| 			boolean isStatic = Modifier.isStatic(mod); | ||||
| 			boolean isFinal = Modifier.isFinal(mod); | ||||
| 			out.println("<tr>" | ||||
| 					+ "<td>" + elementModifiersToHTML(htmlTypeChar, mod) + "</td>" | ||||
| 					+ "<td>" + obfDesc.toHTML(true, isStatic, isFinal) + "</td>" | ||||
| 					+ "<td>" + mojDesc.toHTML(false, isStatic, isFinal) + "</td>" | ||||
| 					+ "</tr>"); | ||||
| 		} | ||||
| 		 | ||||
| 		/* package */ MemberDesc<I> getReflectDesc() { | ||||
| 			return (IS_SERVER_OBFUSCATED ? obfDesc : mojDesc); | ||||
| 		} | ||||
| 		 | ||||
| 		/* package */ abstract R getReflectMember() throws ReflectiveOperationException; | ||||
| 		 | ||||
| 		/* package */ boolean isStatic() { | ||||
| 			try { | ||||
| 				return Modifier.isStatic(getReflectMember().getModifiers()); | ||||
| 			} catch (ReflectiveOperationException e) { | ||||
| 				Log.severe(e); | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		private static MemberMapping<MethodId, ReflectMethod<?>> of(MappingTree.MethodMapping mioMapping) { | ||||
|     		return new MemberMapping<>("⬤", MemberDesc.of(mioMapping, OBF_NAMESPACE), MemberDesc.of(mioMapping, MOJ_NAMESPACE)) { | ||||
| 				@Override | ||||
| 				ReflectMethod<?> getReflectMember() throws ClassNotFoundException, NoSuchMethodException { | ||||
| 					MethodId id = getReflectDesc().identifier; | ||||
| 					return declaringClass.runtimeReflectClass.method(id.name, NMSTypeWrapper.toClassArray(id.parametersType)); | ||||
| 				} | ||||
|     		}; | ||||
| 		} | ||||
| 		 | ||||
| 		private static MemberMapping<String, ReflectField<?>> of(MappingTree.FieldMapping mioMapping) { | ||||
|     		return new MemberMapping<>("●", MemberDesc.of(mioMapping, OBF_NAMESPACE), MemberDesc.of(mioMapping, MOJ_NAMESPACE)) { | ||||
| 				@Override | ||||
| 				ReflectField<?> getReflectMember() throws NoSuchFieldException { | ||||
| 					String id = getReflectDesc().identifier; | ||||
| 					return declaringClass.runtimeReflectClass.field(id); | ||||
| 				} | ||||
|     		}; | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
|     	 | ||||
|     } | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|     /* package */ static String binaryClassName(String cl) { | ||||
|     	return cl.replace('/', '.'); | ||||
|     } | ||||
|      | ||||
| 	 | ||||
| 	 | ||||
| 	private static String classModifiersToHTML(Class<?> clazz) { | ||||
| 		String elementHTMLType; | ||||
|  | ||||
| 		if (clazz.isEnum()) | ||||
| 			elementHTMLType = "E"; | ||||
| 		else if (clazz.isAnnotation()) | ||||
| 			elementHTMLType = "@"; | ||||
| 		else if (clazz.isInterface()) | ||||
| 			elementHTMLType = "I"; | ||||
| 		else if (clazz.isRecord()) | ||||
| 			elementHTMLType = "R"; | ||||
| 		else if (clazz.isPrimitive()) | ||||
| 			elementHTMLType = ""; | ||||
| 		else  | ||||
| 			elementHTMLType = "C"; | ||||
| 		 | ||||
| 		return elementModifiersToHTML(elementHTMLType, clazz.getModifiers()); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	 | ||||
| 	private static String elementModifiersToHTML(String elementHTMLType, int elModifiers) { | ||||
| 		String html = "<b class='"; | ||||
| 		 | ||||
| 		if (Modifier.isPublic(elModifiers)) | ||||
| 			html += "pu"; | ||||
| 		else if (Modifier.isProtected(elModifiers)) | ||||
| 			html += "pt"; | ||||
| 		else if (Modifier.isPrivate(elModifiers)) | ||||
| 			html += "pv"; | ||||
| 		else | ||||
| 			html += "pk"; | ||||
| 		 | ||||
| 		html += "'>" + elementHTMLType + "</b>"; | ||||
|  | ||||
| 		boolean isStatic = Modifier.isStatic(elModifiers); | ||||
| 		boolean isAbstract = Modifier.isAbstract(elModifiers); | ||||
| 		boolean isFinal = Modifier.isFinal(elModifiers); | ||||
|  | ||||
| 		if (isStatic || isAbstract || isFinal) { | ||||
| 			html += "<sup>"; | ||||
| 			if (isStatic) | ||||
| 				html += "S"; | ||||
| 			if (isAbstract) | ||||
| 				html += "A"; | ||||
| 			if (isFinal) | ||||
| 				html += "F"; | ||||
| 			html += "</sup>"; | ||||
| 		} | ||||
| 		 | ||||
| 		return html; | ||||
| 	} | ||||
|      | ||||
|     // ● (field) | ||||
| 	// ⬤ (method) | ||||
|      | ||||
| } | ||||
| @@ -1,194 +0,0 @@ | ||||
| package fr.pandacube.lib.paper.reflect; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.StringReader; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
|  | ||||
| /* package */ class NMSTypeWrapper implements Comparable<NMSTypeWrapper> { | ||||
| 	private final String type; | ||||
| 	private final int arrayDepth; | ||||
| 	 | ||||
| 	/* package */ NMSTypeWrapper(String type, int arrayDepth) { | ||||
| 		this.type = type; | ||||
| 		this.arrayDepth = arrayDepth; | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public boolean equals(Object obj) { | ||||
| 		return obj instanceof NMSTypeWrapper ot && type.equals(ot.type) && arrayDepth == ot.arrayDepth; | ||||
| 	} | ||||
| 	@Override | ||||
| 	public int hashCode() { | ||||
| 		return type.hashCode() ^ arrayDepth; | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public int compareTo(NMSTypeWrapper o) { | ||||
| 		return toString().compareTo(o.toString()); | ||||
| 	} | ||||
| 	 | ||||
| 	Class<?> toClass() throws ClassNotFoundException { | ||||
| 		 | ||||
| 		Class<?> cl = switch(type) { | ||||
| 		case "boolean" -> boolean.class; | ||||
| 		case "byte" -> byte.class; | ||||
| 		case "char" -> char.class; | ||||
| 		case "double" -> double.class; | ||||
| 		case "float" -> float.class; | ||||
| 		case "int" -> int.class; | ||||
| 		case "long" -> long.class; | ||||
| 		case "short" -> short.class; | ||||
| 		case "void" -> void.class; | ||||
| 		default -> Class.forName(type); | ||||
| 		}; | ||||
| 		 | ||||
| 		for (int i = 0; i < arrayDepth; i++) { | ||||
| 			cl = cl.arrayType(); | ||||
| 		} | ||||
| 		 | ||||
| 		return cl; | ||||
| 	} | ||||
| 	 | ||||
| 	/* package */ static NMSTypeWrapper of(Class<?> cl) { | ||||
| 		int arrayDepth = 0; | ||||
| 		while (cl.isArray()) { | ||||
| 			cl = cl.getComponentType(); | ||||
| 			arrayDepth++; | ||||
| 		} | ||||
| 		return new NMSTypeWrapper(cl.getName(), arrayDepth); | ||||
| 	} | ||||
| 	 | ||||
| 	public static NMSTypeWrapper of(ReflectClass<?> rc) { | ||||
| 		return arrayOf(rc, 0); | ||||
| 	} | ||||
| 	 | ||||
| 	public static NMSTypeWrapper arrayOf(ReflectClass<?> rc, int arrayDepth) { | ||||
| 		return new NMSTypeWrapper(rc.get().getName(), arrayDepth); | ||||
| 	} | ||||
| 	 | ||||
| 	public static NMSTypeWrapper mojOf(ClassMapping cm) { | ||||
| 		return arrayMojOf(cm, 0); | ||||
| 	} | ||||
| 	 | ||||
| 	public static NMSTypeWrapper arrayMojOf(ClassMapping cm, int arrayDepth) { | ||||
| 		return new NMSTypeWrapper(cm.mojName, arrayDepth); | ||||
| 	} | ||||
| 	 | ||||
| 	/* package */ static NMSTypeWrapper toType(Object typeObj) { | ||||
| 		Objects.requireNonNull(typeObj, "typeObj cannot be null"); | ||||
| 		if (typeObj instanceof Class<?> cl) { | ||||
| 			return of(cl); | ||||
| 		} | ||||
| 		else if (typeObj instanceof ClassMapping cm) { | ||||
| 			return mojOf(cm); | ||||
| 		} | ||||
| 		else if (typeObj instanceof ReflectClass<?> rc) { | ||||
| 			return of(rc); | ||||
| 		} | ||||
| 		else if (typeObj instanceof NMSTypeWrapper t) { | ||||
| 			return t; | ||||
| 		} | ||||
| 		else | ||||
| 			throw new IllegalArgumentException("Unsupported object of type " + typeObj.getClass()); | ||||
| 	} | ||||
|  | ||||
| 	/* package */ String toHTML(boolean isObfClass) { | ||||
| 		ClassMapping clMapping = (isObfClass ? NMSReflect.CLASSES_BY_OBF : NMSReflect.CLASSES_BY_MOJ).get(type); | ||||
|     	String typeHTML; | ||||
|     	if (clMapping != null) { | ||||
|     		typeHTML = clMapping.toClickableHTML(isObfClass); | ||||
|     	} | ||||
|     	else { | ||||
| 	    	String classToPrint = type; | ||||
| 	    	String classSimpleName = classToPrint.substring(classToPrint.lastIndexOf('.') + 1); | ||||
| 	    	String htmlTitle = classSimpleName.equals(classToPrint) ? "" : (" title='" + classToPrint + "'"); | ||||
|     		if (!htmlTitle.isEmpty()) { | ||||
|     			typeHTML = "<span" + htmlTitle + " class='cl'>" + classSimpleName + "</span>"; | ||||
|         	} | ||||
|         	else { | ||||
|         		typeHTML = "<span class='" + (isPrimitive() ? "kw" : "cl") + "'>" + classSimpleName + "</span>"; | ||||
|         	} | ||||
|     	} | ||||
|     	 | ||||
| 		 | ||||
| 		return typeHTML + "[]".repeat(arrayDepth); | ||||
| 	} | ||||
| 	 | ||||
| 	public String toString() { | ||||
| 		return type + "[]".repeat(arrayDepth); | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	public boolean isPrimitive() { | ||||
| 		try { | ||||
| 			return toClass().isPrimitive(); | ||||
| 		} catch (ClassNotFoundException e) { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| 	/* package */ static NMSTypeWrapper parse(StringReader desc) { | ||||
| 		try { | ||||
| 			int arrayDepth = 0; | ||||
| 			char c; | ||||
| 			while ((c = (char) desc.read()) == '[') { | ||||
| 				arrayDepth++; | ||||
| 			} | ||||
| 			String type = switch(c) { | ||||
| 			case 'Z' -> "boolean"; | ||||
| 			case 'B' -> "byte"; | ||||
| 			case 'C' -> "char"; | ||||
| 			case 'D' -> "double"; | ||||
| 			case 'F' -> "float"; | ||||
| 			case 'I' -> "int"; | ||||
| 			case 'J' -> "long"; | ||||
| 			case 'S' -> "short"; | ||||
| 			case 'L' -> { | ||||
| 				StringBuilder sbClass = new StringBuilder(); | ||||
| 				char r; | ||||
| 				while ((r = (char) desc.read()) != ';') { | ||||
|     				sbClass.append(r); | ||||
| 				} | ||||
| 				yield NMSReflect.binaryClassName(sbClass.toString()); | ||||
| 			} | ||||
| 			default -> "void"; | ||||
| 			}; | ||||
| 			return new NMSTypeWrapper(type, arrayDepth); | ||||
| 		} catch (IOException e) { | ||||
| 			throw new RuntimeException("StringReader read error", e); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
|  | ||||
| 	 | ||||
| 	/* package */ static List<NMSTypeWrapper> toTypeList(List<Object> paramsType) { | ||||
| 		List<NMSTypeWrapper> types = new ArrayList<>(paramsType.size()); | ||||
| 		for (int i = 0; i < paramsType.size(); i++) { | ||||
| 			Object param = paramsType.get(i); | ||||
| 			try { | ||||
| 				types.add(NMSTypeWrapper.toType(param)); | ||||
| 			} catch (NullPointerException|IllegalArgumentException e) { | ||||
| 				throw new IllegalArgumentException("Invalid parameterType at index " + i, e); | ||||
| 			} | ||||
| 		} | ||||
| 		return types; | ||||
| 	} | ||||
| 	 | ||||
| 	/* package */ static Class<?>[] toClassArray(List<NMSTypeWrapper> types) throws ClassNotFoundException { | ||||
| 		Class<?>[] classes = new Class<?>[types.size()]; | ||||
| 		for (int i = 0; i < types.size(); i++) { | ||||
| 			classes[i] = types.get(i).toClass(); | ||||
| 		} | ||||
| 		return classes; | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| @@ -23,4 +23,6 @@ public class OBCReflect { | ||||
| 		return Reflect.ofClass(CRAFTBUKKIT_PACKAGE + "." + obcClass); | ||||
| 	} | ||||
|  | ||||
| 	private OBCReflect() { } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -17,17 +17,15 @@ 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.BlockPosArgument; | ||||
| 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.ComponentArgument; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Coordinates; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.EntityArgument; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.EntitySelector; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.GameProfileArgument; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.ResourceLocationArgument; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Vec3Argument; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.BlockPos; | ||||
| 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.core.Vec3i; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CollectionTag; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag; | ||||
| @@ -66,6 +64,7 @@ import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack; | ||||
| 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.Vec3; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.VoxelShape; | ||||
| @@ -74,9 +73,14 @@ 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.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; | ||||
|  | ||||
| import static fr.pandacube.lib.reflect.wrapper.WrapperRegistry.initWrapper; | ||||
| @@ -93,12 +97,14 @@ public class PandalibPaperReflect { | ||||
|      * @throws Exception if a problem occurs when initializing wrapper classes. | ||||
|      */ | ||||
|     public static void init() throws Exception { | ||||
|         NMSReflect.init(); | ||||
|         synchronized (PandalibPaperReflect.class) { | ||||
|             if (isInit) | ||||
|                 return; | ||||
|             isInit = true; | ||||
|         } | ||||
|  | ||||
|         ReflectionWrapperBypass.enable(); | ||||
|  | ||||
|         initWrapperClasses(); | ||||
|     } | ||||
|  | ||||
| @@ -127,91 +133,99 @@ public class PandalibPaperReflect { | ||||
|         thAcc.catchThrowable(() -> initWrapper(MCTypeRegistry.class, MCTypeRegistry.REFLECT.get())); | ||||
|  | ||||
|         // minecraft.commands | ||||
|         thAcc.catchThrowable(() -> initWrapper(BlockPosArgument.class, BlockPosArgument.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Commands.class, Commands.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CommandSourceStack.class, CommandSourceStack.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ComponentArgument.class, ComponentArgument.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Coordinates.class, Coordinates.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(EntityArgument.class, EntityArgument.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(EntitySelector.class, EntitySelector.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(GameProfileArgument.class, GameProfileArgument.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ResourceLocationArgument.class, ResourceLocationArgument.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Vec3Argument.class, Vec3Argument.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Commands.class, Commands.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CommandSourceStack.class, CommandSourceStack.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Coordinates.class, Coordinates.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(GameProfileArgument.class, GameProfileArgument.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ResourceLocationArgument.class, ResourceLocationArgument.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Vec3Argument.class, Vec3Argument.REFLECT.get())); | ||||
|         // minecraft.core | ||||
|         thAcc.catchThrowable(() -> initWrapper(BlockPos.class, BlockPos.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Vec3i.class, Vec3i.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(BlockPos.class, BlockPos.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(HolderLookupProvider.class, HolderLookupProvider.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(RegistryAccess.class, RegistryAccess.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Vec3i.class, Vec3i.REFLECT.get())); | ||||
|         // minecraft.nbt | ||||
|         thAcc.catchThrowable(() -> initWrapper(CollectionTag.class, CollectionTag.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CompoundTag.class, CompoundTag.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ListTag.class, ListTag.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(NbtAccounter.class, NbtAccounter.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(NbtIo.class, NbtIo.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(StringTag.class, StringTag.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Tag.class, Tag.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CollectionTag.class, CollectionTag.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CompoundTag.class, CompoundTag.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ListTag.class, ListTag.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(NbtAccounter.class, NbtAccounter.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(NbtIo.class, NbtIo.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(StringTag.class, StringTag.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Tag.class, Tag.REFLECT.get())); | ||||
|         // minecraft.network.chat | ||||
|         thAcc.catchThrowable(() -> initWrapper(Component.class, Component.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Component.class, Component.REFLECT.get())); | ||||
|         // minecraft.network.protocol.custom | ||||
|         thAcc.catchThrowable(() -> initWrapper(BrandPayload.class, BrandPayload.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CustomPacketPayload.class, CustomPacketPayload.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(BrandPayload.class, BrandPayload.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(CustomPacketPayload.class, CustomPacketPayload.REFLECT.get())); | ||||
|         // minecraft.network.protocol | ||||
|         thAcc.catchThrowable(() -> initWrapper(ClientboundCustomPayloadPacket.class, ClientboundCustomPayloadPacket.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ClientboundGameEventPacket.class, ClientboundGameEventPacket.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ClientboundGameEventPacket.Type.class, ClientboundGameEventPacket.Type.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Packet.class, Packet.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ClientboundCustomPayloadPacket.class, ClientboundCustomPayloadPacket.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ClientboundGameEventPacket.class, ClientboundGameEventPacket.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ClientboundGameEventPacket.Type.class, ClientboundGameEventPacket.Type.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Packet.class, Packet.REFLECT.get())); | ||||
|         // minecraft.network | ||||
|         thAcc.catchThrowable(() -> initWrapper(FriendlyByteBuf.class, FriendlyByteBuf.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(FriendlyByteBuf.class, FriendlyByteBuf.REFLECT.get())); | ||||
|         // minecraft.resources | ||||
|         thAcc.catchThrowable(() -> initWrapper(ResourceLocation.class, ResourceLocation.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ResourceLocation.class, ResourceLocation.REFLECT.get())); | ||||
|         // minecraft.server | ||||
|         thAcc.catchThrowable(() -> initWrapper(ChunkMap.class, ChunkMap.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DedicatedPlayerList.class, DedicatedPlayerList.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DedicatedServer.class, DedicatedServer.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DedicatedServerProperties.class, DedicatedServerProperties.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(MinecraftServer.class, MinecraftServer.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(PlayerList.class, PlayerList.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerChunkCache.class, ServerChunkCache.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerCommonPacketListenerImpl.class, ServerCommonPacketListenerImpl.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerGamePacketListenerImpl.class, ServerGamePacketListenerImpl.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerLevel.class, ServerLevel.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerPlayer.class, ServerPlayer.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Settings.class, Settings.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ChunkMap.class, ChunkMap.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DedicatedPlayerList.class, DedicatedPlayerList.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DedicatedServer.class, DedicatedServer.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DedicatedServerProperties.class, DedicatedServerProperties.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(MinecraftServer.class, MinecraftServer.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(PlayerList.class, PlayerList.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerChunkCache.class, ServerChunkCache.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerCommonPacketListenerImpl.class, ServerCommonPacketListenerImpl.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerGamePacketListenerImpl.class, ServerGamePacketListenerImpl.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerLevel.class, ServerLevel.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ServerPlayer.class, ServerPlayer.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Settings.class, Settings.REFLECT.get())); | ||||
|         // minecraft.util | ||||
|         thAcc.catchThrowable(() -> initWrapper(ProgressListener.class, ProgressListener.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ProgressListener.class, ProgressListener.REFLECT.get())); | ||||
|         // minecraft.world.block | ||||
|         thAcc.catchThrowable(() -> initWrapper(Block.class, Block.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(BambooStalkBlock.class, BambooStalkBlock.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Block.class, Block.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(BambooStalkBlock.class, BambooStalkBlock.REFLECT.get())); | ||||
|         // minecraft.world | ||||
|         thAcc.catchThrowable(() -> initWrapper(AABB.class, AABB.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ChunkPos.class, ChunkPos.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ChunkStorage.class, ChunkStorage.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(DataVersion.class, DataVersion.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Entity.class, Entity.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(ItemStack.class, ItemStack.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Level.class, Level.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(MapItemSavedData.class, MapItemSavedData.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(PlayerDataStorage.class, PlayerDataStorage.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(SavedData.class, SavedData.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(Vec3.class, Vec3.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(VoxelShape.class, VoxelShape.MAPPING.runtimeClass())); | ||||
|         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(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(Vec3.class, Vec3.REFLECT.get())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(VoxelShape.class, VoxelShape.REFLECT.get())); | ||||
|         // minecraft | ||||
|         thAcc.catchThrowable(() -> initWrapper(DetectedVersion.class, DetectedVersion.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(SharedConstants.class, SharedConstants.MAPPING.runtimeClass())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(WorldVersion.class, WorldVersion.MAPPING.runtimeClass())); | ||||
|         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(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())); | ||||
|         thAcc.catchThrowable(() -> initWrapper(WorldConfiguration.class, WorldConfiguration.REFLECT.get())); | ||||
|         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() {} | ||||
| } | ||||
|   | ||||
| @@ -12,11 +12,25 @@ import org.bukkit.event.Listener; | ||||
| import org.bukkit.event.block.BlockPlaceEvent; | ||||
| import org.bukkit.util.BoundingBox; | ||||
|  | ||||
| // simplified version of https://github.com/Camotoy/BambooCollisionFix/tree/c7d7d5327791cbb416d106de0b9eb0bf2461acbd/src/main/java/net/camotoy/bamboocollisionfix | ||||
| // we remove the bamboo bounding box due to bedrock clients not having the same placement for bamboos | ||||
| // simplified version of | ||||
|  | ||||
| /** | ||||
|  * Disables the server-side bounding box of the bamboo stalk blocks. | ||||
|  * Bamboo stalks are the thin bamboo plants that are shifted differently, depending on the X and Z coordinate. | ||||
|  * This X/Z dependent shift is different between Java and Bedrock implementation. | ||||
|  * But since this block as a collision box and players cannot go through them, Bedrock players are often rolled back | ||||
|  * when they are walking around bamboo stalk due to the server being in Java Edition and thinking the player tries to | ||||
|  * move through the bamboo. | ||||
|  * To avoid this issue, we reduce to 0 the size of the bounding box on the server. | ||||
|  * <br> | ||||
|  * See <a href="https://github.com/Camotoy/BambooCollisionFix/tree/c7d7d5327791cbb416d106de0b9eb0bf2461acbd/src/main/java/net/camotoy/bamboocollisionfix">the original implementation</a>. | ||||
|  */ | ||||
| public final class BedrockBambooCollisionFixer implements Listener { | ||||
|     private final BoundingBox originalBambooBoundingBox = new BoundingBox(6.5D / 16D, 0.0D, 6.5D / 16.0D, 9.5D / 16.0D, 1D, 9.5D / 16.0D); | ||||
|  | ||||
|     /** | ||||
|      * Creates a new {@link BedrockBambooCollisionFixer}. There is no need for multiple instances. | ||||
|      */ | ||||
|     public BedrockBambooCollisionFixer() { | ||||
|         // Make the bamboo block have zero collision. | ||||
|         try { | ||||
| @@ -34,7 +48,7 @@ public final class BedrockBambooCollisionFixer implements Listener { | ||||
|      * our ability. | ||||
|      */ | ||||
|     @EventHandler | ||||
|     public void onBlockPlace(BlockPlaceEvent event) { | ||||
|     void onBlockPlace(BlockPlaceEvent event) { | ||||
|         if (event.getBlockPlaced().getBlockData().getMaterial().equals(Material.BAMBOO)) { | ||||
|             BoundingBox currentBambooBoundingBox = originalBambooBoundingBox.clone().shift(event.getBlockPlaced().getLocation()); | ||||
|             for (LivingEntity e : event.getBlock().getLocation().getNearbyLivingEntities(5)) { | ||||
|   | ||||
| @@ -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() {} | ||||
| 	 | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.craftbukkit; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.OBCReflect; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt.CompoundTag; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| @@ -12,7 +11,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class CraftItemStack extends ReflectWrapperTyped<ItemStack> { | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> OBCReflect.ofClass("inventory.CraftItemStack")); | ||||
|     public static final ReflectMethod<?> asCraftMirror = wrapEx(() -> REFLECT.method("asCraftMirror", fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.MAPPING.runtimeClass())); | ||||
|     public static final ReflectMethod<?> asCraftMirror = wrapEx(() -> REFLECT.method("asCraftMirror", fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.REFLECT.get())); | ||||
|     public static final ReflectMethod<?> asNMSCopy = wrapEx(() -> REFLECT.method("asNMSCopy", ItemStack.class)); | ||||
|  | ||||
|     public static ItemStack asCraftMirror(fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack original) { | ||||
| @@ -20,11 +19,6 @@ public class CraftItemStack extends ReflectWrapperTyped<ItemStack> { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static ItemStack asCraftMirror(CompoundTag nbt) { | ||||
|         return asCraftMirror(fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.of(nbt)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack asNMSCopy(ItemStack original) { | ||||
|         return wrap(wrapReflectEx(() -> asNMSCopy.invokeStatic(original)), fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ItemStack.class); | ||||
|     } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
| public class CraftNamespacedKey extends ReflectWrapper { | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> OBCReflect.ofClass("util.CraftNamespacedKey")); | ||||
|     public static final ReflectMethod<?> toMinecraft = wrapEx(() -> REFLECT.method("toMinecraft", NamespacedKey.class)); | ||||
|     public static final ReflectMethod<?> fromMinecraft = ThrowableUtil.wrapEx(() -> REFLECT.method("fromMinecraft", ResourceLocation.MAPPING.runtimeClass())); | ||||
|     public static final ReflectMethod<?> fromMinecraft = ThrowableUtil.wrapEx(() -> REFLECT.method("fromMinecraft", ResourceLocation.REFLECT.get())); | ||||
|  | ||||
|     public static ResourceLocation toMinecraft(NamespacedKey key) { | ||||
|         return wrap(wrapReflectEx(() -> toMinecraft.invokeStatic(key)), ResourceLocation.class); | ||||
|   | ||||
| @@ -15,8 +15,8 @@ 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.MAPPING.runtimeClass())); | ||||
|     public static final ReflectMethod<?> toBukkit_BlockPos = ThrowableUtil.wrapEx(() -> REFLECT.method("toBukkit", BlockPos.MAPPING.runtimeClass())); | ||||
|     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)); | ||||
|  | ||||
|   | ||||
| @@ -1,16 +1,14 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.craftbukkit; | ||||
|  | ||||
| import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; | ||||
| import com.mojang.brigadier.tree.CommandNode; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.OBCReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Commands; | ||||
| 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.ReflectWrapperTyped; | ||||
| import io.papermc.paper.command.brigadier.CommandSourceStack; | ||||
| import org.bukkit.command.CommandSender; | ||||
| import org.bukkit.command.defaults.BukkitCommand; | ||||
|  | ||||
| @@ -19,21 +17,21 @@ 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.MAPPING.runtimeClass(), CommandNode.class)); | ||||
|     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<BukkitBrigadierCommandSource> vanillaCommand) { | ||||
|     public VanillaCommandWrapper(Commands dispatcher, CommandNode<CommandSourceStack> vanillaCommand) { | ||||
|         this(wrapReflectEx(() -> CONSTRUCTOR.instantiate(unwrap(dispatcher), vanillaCommand))); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public CommandNode<BukkitBrigadierCommandSource> vanillaCommand() { | ||||
|         return (CommandNode<BukkitBrigadierCommandSource>) wrapReflectEx(() -> vanillaCommand.getValue(__getRuntimeInstance())); | ||||
|     public CommandNode<CommandSourceStack> vanillaCommand() { | ||||
|         return (CommandNode<CommandSourceStack>) wrapReflectEx(() -> vanillaCommand.getValue(__getRuntimeInstance())); | ||||
|     } | ||||
|  | ||||
|     public static BukkitBrigadierCommandSource getListener(CommandSender sender) { | ||||
|         return (BukkitBrigadierCommandSource) wrapReflectEx(() -> getListener.invokeStatic(sender)); | ||||
|     public static CommandSourceStack getListener(CommandSender sender) { | ||||
|         return (CommandSourceStack) wrapReflectEx(() -> getListener.invokeStatic(sender)); | ||||
|     } | ||||
|  | ||||
|     protected VanillaCommandWrapper(Object obj) { | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class MCDataConverter extends ReflectWrapper { | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("ca.spottedleaf.dataconverter.minecraft.MCDataConverter")); | ||||
|     private static final ReflectMethod<?> convertTag = wrapEx(() -> REFLECT.method("convertTag", MCDataType.REFLECT.get(), CompoundTag.MAPPING.runtimeClass(), int.class, int.class)); | ||||
|     private static final ReflectMethod<?> convertTag = wrapEx(() -> REFLECT.method("convertTag", MCDataType.REFLECT.get(), CompoundTag.REFLECT.get(), int.class, int.class)); | ||||
|  | ||||
|     public static CompoundTag convertTag(MCDataType type, CompoundTag data, int fromVersion, int toVersion) { | ||||
|         return wrap(wrapReflectEx(() -> convertTag.invokeStatic(unwrap(type), unwrap(data), fromVersion, toVersion)), CompoundTag.class); | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| 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 NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.DetectedVersion")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.DetectedVersion")); | ||||
|  | ||||
|     protected DetectedVersion(Object obj) { | ||||
|         super(obj); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| 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; | ||||
|  | ||||
| @@ -8,9 +9,9 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class SharedConstants extends ReflectWrapper { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.SharedConstants")); | ||||
|     private static final ReflectMethod<?> getCurrentVersion = wrapEx(() -> MAPPING.mojMethod("getCurrentVersion")); | ||||
|     private static final ReflectMethod<?> getProtocolVersion = wrapEx(() -> MAPPING.mojMethod("getProtocolVersion")); | ||||
|     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")); | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.DataVersion; | ||||
| 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; | ||||
| @@ -14,8 +14,8 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| @ConcreteWrapper(WorldVersion.__concrete.class) | ||||
| public interface WorldVersion extends ReflectWrapperI { | ||||
|     ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.WorldVersion")); | ||||
|     ReflectMethod<?> getDataVersion = wrapEx(() -> MAPPING.mojMethod("getDataVersion")); | ||||
|     ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.WorldVersion")); | ||||
|     ReflectMethod<?> getDataVersion = wrapEx(() -> REFLECT.method("getDataVersion")); | ||||
|  | ||||
|     default DataVersion getDataVersion() { | ||||
|         return wrap(wrapReflectEx(() -> getDataVersion.invoke(__getRuntimeInstance())), DataVersion.class); | ||||
|   | ||||
| @@ -1,24 +0,0 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class BlockPosArgument extends ReflectWrapperTyped<ArgumentType<?>> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.coordinates.BlockPosArgument")); | ||||
|     private static final ReflectMethod<?> blockPos = wrapEx(() -> MAPPING.mojMethod("blockPos")); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> blockPos() { | ||||
|         return (ArgumentType<Object>) wrapReflectEx(() -> blockPos.invokeStatic()); | ||||
|     } | ||||
|  | ||||
|     protected BlockPosArgument(Object obj) { | ||||
|         super(obj); | ||||
|     } | ||||
| } | ||||
| @@ -1,13 +1,13 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| public class CommandSourceStack extends ReflectWrapperTyped<BukkitBrigadierCommandSource> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.CommandSourceStack")); | ||||
| public class CommandSourceStack extends ReflectWrapperTyped<io.papermc.paper.command.brigadier.CommandSourceStack> { | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.CommandSourceStack")); | ||||
|  | ||||
|     protected CommandSourceStack(Object obj) { | ||||
|         super(obj); | ||||
|   | ||||
| @@ -1,22 +1,21 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; | ||||
| import com.mojang.brigadier.CommandDispatcher; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectField; | ||||
| 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 Commands extends ReflectWrapper { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.Commands")); | ||||
|     public static final ReflectField<?> dispatcher = wrapEx(() -> MAPPING.mojField("dispatcher")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.Commands")); | ||||
|     public static final ReflectField<?> dispatcher = wrapEx(() -> REFLECT.field("dispatcher")); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public CommandDispatcher<BukkitBrigadierCommandSource> dispatcher() { | ||||
|         return (CommandDispatcher<BukkitBrigadierCommandSource>) wrapReflectEx(() -> dispatcher.getValue(__getRuntimeInstance())); | ||||
|     public CommandDispatcher<CommandSourceStack> dispatcher() { | ||||
|         return (CommandDispatcher<CommandSourceStack>) wrapReflectEx(() -> dispatcher.getValue(__getRuntimeInstance())); | ||||
|     } | ||||
|  | ||||
|     protected Commands(Object obj) { | ||||
|   | ||||
| @@ -1,24 +0,0 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class ComponentArgument extends ReflectWrapperTyped<ArgumentType<?>> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.ComponentArgument")); | ||||
|     private static final ReflectMethod<?> textComponent = wrapEx(() -> MAPPING.mojMethod("textComponent")); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> textComponent() { | ||||
|         return (ArgumentType<Object>) wrapReflectEx(() -> textComponent.invokeStatic()); | ||||
|     } | ||||
|  | ||||
|     protected ComponentArgument(Object obj) { | ||||
|         super(obj); | ||||
|     } | ||||
| } | ||||
| @@ -1,33 +1,26 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Vec3; | ||||
| 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 fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.BlockPos; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Vec3; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| 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; | ||||
| import static fr.pandacube.lib.reflect.wrapper.ReflectWrapper.wrap; | ||||
|  | ||||
| @ConcreteWrapper(Coordinates.__concrete.class) | ||||
| public interface Coordinates extends ReflectWrapperI { | ||||
|     NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.coordinates.Coordinates")); | ||||
|     ReflectMethod<?> getPosition = wrapEx(() -> MAPPING.mojMethod("getPosition", CommandSourceStack.MAPPING)); | ||||
|     ReflectMethod<?> getBlockPos = wrapEx(() -> MAPPING.mojMethod("getBlockPos", CommandSourceStack.MAPPING)); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.arguments.coordinates.Coordinates")); | ||||
|     ReflectMethod<?> getPosition = wrapEx(() -> REFLECT.method("getPosition", CommandSourceStack.REFLECT.get())); | ||||
|  | ||||
|     default Vec3 getPosition(BukkitBrigadierCommandSource source) { | ||||
|     default Vec3 getPosition(io.papermc.paper.command.brigadier.CommandSourceStack source) { | ||||
|         return wrap(wrapReflectEx(() -> getPosition.invoke(__getRuntimeInstance(), source)), Vec3.class); | ||||
|     } | ||||
|  | ||||
|     default BlockPos getBlockPos(BukkitBrigadierCommandSource source) { | ||||
|         return wrap(wrapReflectEx(() -> getBlockPos.invoke(__getRuntimeInstance(), source)), BlockPos.class); | ||||
|     } | ||||
|  | ||||
|     class __concrete extends ReflectWrapper implements Coordinates { | ||||
|         protected __concrete(Object obj) { | ||||
|             super(obj); | ||||
|   | ||||
| @@ -1,43 +0,0 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class EntityArgument extends ReflectWrapperTyped<ArgumentType<?>> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.EntityArgument")); | ||||
|     private static final ReflectMethod<?> entity = wrapEx(() -> MAPPING.mojMethod("entity")); | ||||
|     private static final ReflectMethod<?> entities = wrapEx(() -> MAPPING.mojMethod("entities")); | ||||
|     private static final ReflectMethod<?> player = wrapEx(() -> MAPPING.mojMethod("player")); | ||||
|     private static final ReflectMethod<?> players = wrapEx(() -> MAPPING.mojMethod("players")); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> entity() { | ||||
|         return (ArgumentType<Object>) wrapReflectEx(() -> entity.invokeStatic()); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> entities() { | ||||
|         return (ArgumentType<Object>) wrapReflectEx(() -> entities.invokeStatic()); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> player() { | ||||
|         return (ArgumentType<Object>) wrapReflectEx(() -> player.invokeStatic()); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> players() { | ||||
|         return (ArgumentType<Object>) wrapReflectEx(() -> players.invokeStatic()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected EntityArgument(Object obj) { | ||||
|         super(obj); | ||||
|     } | ||||
| } | ||||
| @@ -1,46 +0,0 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectListWrapper; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerPlayer; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.Entity; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class EntitySelector extends ReflectWrapper { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.selector.EntitySelector")); | ||||
|     private static final ReflectMethod<?> findEntities = wrapEx(() -> MAPPING.mojMethod("findEntities", CommandSourceStack.MAPPING)); | ||||
|     private static final ReflectMethod<?> findPlayers = wrapEx(() -> MAPPING.mojMethod("findPlayers", CommandSourceStack.MAPPING)); | ||||
|     private static final ReflectMethod<?> findSingleEntity = wrapEx(() -> MAPPING.mojMethod("findSingleEntity", CommandSourceStack.MAPPING)); | ||||
|     private static final ReflectMethod<?> findSinglePlayer = wrapEx(() -> MAPPING.mojMethod("findSinglePlayer", CommandSourceStack.MAPPING)); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public ReflectListWrapper<Entity> findEntities(BukkitBrigadierCommandSource source) { | ||||
|         return wrapList((List<Object>) wrapReflectEx(() -> findEntities.invoke(__getRuntimeInstance(), source)), Entity.class); | ||||
|     } | ||||
|  | ||||
|     public Entity findSingleEntity(BukkitBrigadierCommandSource source) { | ||||
|         return wrap(wrapReflectEx(() -> findSingleEntity.invoke(__getRuntimeInstance(), source)), Entity.class); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public ReflectListWrapper<ServerPlayer> findPlayers(BukkitBrigadierCommandSource source) { | ||||
|         return wrapList((List<Object>) wrapReflectEx(() -> findPlayers.invoke(__getRuntimeInstance(), source)), ServerPlayer.class); | ||||
|     } | ||||
|  | ||||
|     public ServerPlayer findSinglePlayer(BukkitBrigadierCommandSource source) { | ||||
|         return wrap(wrapReflectEx(() -> findSinglePlayer.invoke(__getRuntimeInstance(), source)), ServerPlayer.class); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     protected EntitySelector(Object obj) { | ||||
|         super(obj); | ||||
|     } | ||||
| } | ||||
| @@ -1,17 +1,17 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| 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 static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class GameProfileArgument extends ReflectWrapperTyped<ArgumentType<?>> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.GameProfileArgument")); | ||||
|     private static final ReflectMethod<?> gameProfile = wrapEx(() -> MAPPING.mojMethod("gameProfile")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.arguments.GameProfileArgument")); | ||||
|     private static final ReflectMethod<?> gameProfile = wrapEx(() -> REFLECT.method("gameProfile")); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> gameProfile() { | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| 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 static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class ResourceLocationArgument extends ReflectWrapperTyped<ArgumentType<?>> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.ResourceLocationArgument")); | ||||
|     private static final ReflectMethod<?> id = wrapEx(() -> MAPPING.mojMethod("id")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.arguments.ResourceLocationArgument")); | ||||
|     private static final ReflectMethod<?> id = wrapEx(() -> REFLECT.method("id")); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> id() { | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
| 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 static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class Vec3Argument extends ReflectWrapperTyped<ArgumentType<?>> { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.commands.arguments.coordinates.Vec3Argument")); | ||||
|     private static final ReflectMethod<?> vec3 = wrapEx(() -> MAPPING.mojMethod("vec3", boolean.class)); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.commands.arguments.coordinates.Vec3Argument")); | ||||
|     private static final ReflectMethod<?> vec3 = wrapEx(() -> REFLECT.method("vec3", boolean.class)); | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public static ArgumentType<Object> vec3(boolean centerIntegers) { | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.core; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| public class BlockPos extends Vec3i { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.core.BlockPos")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.core.BlockPos")); | ||||
|  | ||||
|     protected BlockPos(Object obj) { | ||||
|         super(obj); | ||||
|   | ||||
| @@ -0,0 +1,20 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.core; | ||||
|  | ||||
| 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.ReflectWrapper; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| @ConcreteWrapper(HolderLookupProvider.__concrete.class) | ||||
| public interface HolderLookupProvider extends ReflectWrapperI { | ||||
|     ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.core.HolderLookup$Provider")); | ||||
|  | ||||
|     class __concrete extends ReflectWrapper implements HolderLookupProvider { | ||||
|         protected __concrete(Object obj) { | ||||
|             super(obj); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.core; | ||||
|  | ||||
| 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.ReflectWrapper; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| @ConcreteWrapper(RegistryAccess.__concrete.class) | ||||
| public interface RegistryAccess extends HolderLookupProvider { | ||||
|     ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.core.RegistryAccess")); | ||||
|  | ||||
|     class __concrete extends ReflectWrapper implements RegistryAccess { | ||||
|         protected __concrete(Object obj) { | ||||
|             super(obj); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,17 +1,18 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.core; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
| 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 Vec3i extends ReflectWrapper { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.core.Vec3i")); | ||||
|     public static final ReflectMethod<?> getX = wrapEx(() -> MAPPING.mojMethod("getX")); | ||||
|     public static final ReflectMethod<?> getY = wrapEx(() -> MAPPING.mojMethod("getY")); | ||||
|     public static final ReflectMethod<?> getZ = wrapEx(() -> MAPPING.mojMethod("getZ")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.core.Vec3i")); | ||||
|     public static final ReflectMethod<?> getX = wrapEx(() -> REFLECT.method("getX")); | ||||
|     public static final ReflectMethod<?> getY = wrapEx(() -> REFLECT.method("getY")); | ||||
|     public static final ReflectMethod<?> getZ = wrapEx(() -> REFLECT.method("getZ")); | ||||
|  | ||||
|     public int getX() { | ||||
|         return (int) wrapReflectEx(() -> getX.invoke(__getRuntimeInstance())); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperTyped; | ||||
|  | ||||
| import java.util.AbstractList; | ||||
| @@ -9,7 +9,7 @@ import java.util.AbstractList; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| public class CollectionTag extends ReflectWrapperTyped<AbstractList<?>> implements Tag { | ||||
| 	public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.CollectionTag")); | ||||
| 	public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.CollectionTag")); | ||||
|  | ||||
|  | ||||
| 	public int size() { | ||||
|   | ||||
| @@ -1,60 +1,60 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| 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.ReflectWrapper; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import fr.pandacube.lib.reflect.ReflectConstructor; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| 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 CompoundTag extends ReflectWrapper implements Tag { | ||||
| 	public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.CompoundTag")); | ||||
| 	public static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor()); | ||||
| 	private static final ReflectMethod<?> putBoolean = wrapEx(() -> MAPPING.mojMethod("putBoolean", String.class, boolean.class)); | ||||
| 	private static final ReflectMethod<?> putByte = wrapEx(() -> MAPPING.mojMethod("putByte", String.class, byte.class)); | ||||
| 	private static final ReflectMethod<?> putByteArray = wrapEx(() -> MAPPING.mojMethod("putByteArray", String.class, byte[].class)); | ||||
| 	private static final ReflectMethod<?> putByteArray_List = wrapEx(() -> MAPPING.mojMethod("putByteArray", String.class, List.class)); | ||||
| 	private static final ReflectMethod<?> putDouble = wrapEx(() -> MAPPING.mojMethod("putDouble", String.class, double.class)); | ||||
| 	private static final ReflectMethod<?> putFloat = wrapEx(() -> MAPPING.mojMethod("putFloat", String.class, float.class)); | ||||
| 	private static final ReflectMethod<?> putInt = wrapEx(() -> MAPPING.mojMethod("putInt", String.class, int.class)); | ||||
| 	private static final ReflectMethod<?> putIntArray = wrapEx(() -> MAPPING.mojMethod("putIntArray", String.class, int[].class)); | ||||
| 	private static final ReflectMethod<?> putIntArray_List = wrapEx(() -> MAPPING.mojMethod("putIntArray", String.class, List.class)); | ||||
| 	private static final ReflectMethod<?> putString = wrapEx(() -> MAPPING.mojMethod("putString", String.class, String.class)); | ||||
| 	private static final ReflectMethod<?> putUUID = wrapEx(() -> MAPPING.mojMethod("putUUID", String.class, UUID.class)); | ||||
| 	private static final ReflectMethod<?> putLong = wrapEx(() -> MAPPING.mojMethod("putLong", String.class, long.class)); | ||||
| 	private static final ReflectMethod<?> putLongArray = wrapEx(() -> MAPPING.mojMethod("putLongArray", String.class, long[].class)); | ||||
| 	private static final ReflectMethod<?> putLongArray_List = wrapEx(() -> MAPPING.mojMethod("putLongArray", String.class, List.class)); | ||||
| 	private static final ReflectMethod<?> putShort = wrapEx(() -> MAPPING.mojMethod("putShort", String.class, short.class)); | ||||
| 	private static final ReflectMethod<?> put = wrapEx(() -> MAPPING.mojMethod("put", String.class, Tag.MAPPING)); | ||||
| 	public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.CompoundTag")); | ||||
| 	public static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor()); | ||||
| 	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(() -> MAPPING.mojMethod("getTagType", String.class)); | ||||
| 	private static final ReflectMethod<?> getByte = wrapEx(() -> MAPPING.mojMethod("getByte", String.class)); | ||||
| 	private static final ReflectMethod<?> getShort = wrapEx(() -> MAPPING.mojMethod("getShort", String.class)); | ||||
| 	private static final ReflectMethod<?> getInt = wrapEx(() -> MAPPING.mojMethod("getInt", String.class)); | ||||
| 	private static final ReflectMethod<?> getLong = wrapEx(() -> MAPPING.mojMethod("getLong", String.class)); | ||||
| 	private static final ReflectMethod<?> getFloat = wrapEx(() -> MAPPING.mojMethod("getFloat", String.class)); | ||||
| 	private static final ReflectMethod<?> getDouble = wrapEx(() -> MAPPING.mojMethod("getDouble", String.class)); | ||||
| 	private static final ReflectMethod<?> getString = wrapEx(() -> MAPPING.mojMethod("getString", String.class)); | ||||
| 	private static final ReflectMethod<?> getByteArray = wrapEx(() -> MAPPING.mojMethod("getByteArray", String.class)); | ||||
| 	private static final ReflectMethod<?> getIntArray = wrapEx(() -> MAPPING.mojMethod("getIntArray", String.class)); | ||||
| 	private static final ReflectMethod<?> getLongArray = wrapEx(() -> MAPPING.mojMethod("getLongArray", String.class)); | ||||
| 	private static final ReflectMethod<?> getCompound = wrapEx(() -> MAPPING.mojMethod("getCompound", String.class)); | ||||
| 	private static final ReflectMethod<?> getBoolean = wrapEx(() -> MAPPING.mojMethod("getBoolean", String.class)); | ||||
| 	private static final ReflectMethod<?> getList = wrapEx(() -> MAPPING.mojMethod("getList", String.class, int.class)); | ||||
| 	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)); | ||||
| 	private static final ReflectMethod<?> getLong = wrapEx(() -> REFLECT.method("getLong", String.class)); | ||||
| 	private static final ReflectMethod<?> getFloat = wrapEx(() -> REFLECT.method("getFloat", String.class)); | ||||
| 	private static final ReflectMethod<?> getDouble = wrapEx(() -> REFLECT.method("getDouble", String.class)); | ||||
| 	private static final ReflectMethod<?> getString = wrapEx(() -> REFLECT.method("getString", String.class)); | ||||
| 	private static final ReflectMethod<?> getByteArray = wrapEx(() -> REFLECT.method("getByteArray", String.class)); | ||||
| 	private static final ReflectMethod<?> getIntArray = wrapEx(() -> REFLECT.method("getIntArray", String.class)); | ||||
| 	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<?> get = wrapEx(() -> MAPPING.mojMethod("get", String.class)); | ||||
| 	private static final ReflectMethod<?> getAllKeys = wrapEx(() -> MAPPING.mojMethod("getAllKeys")); | ||||
| 	private static final ReflectMethod<?> entries = wrapEx(() -> MAPPING.mojMethod("entries")); | ||||
| 	private static final ReflectMethod<?> size = wrapEx(() -> MAPPING.mojMethod("size")); | ||||
| 	private static final ReflectMethod<?> contains = wrapEx(() -> MAPPING.mojMethod("contains", String.class)); | ||||
| 	private static final ReflectMethod<?> containsStringInt = wrapEx(() -> MAPPING.mojMethod("contains", String.class, int.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<?> 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())); | ||||
| @@ -166,9 +166,9 @@ public class CompoundTag extends ReflectWrapper implements Tag { | ||||
| 	 * The values in the returned Map are not wrapped. | ||||
| 	 */ | ||||
| 	@SuppressWarnings("unchecked") | ||||
| 	public Map<String, ?> entries() { | ||||
| 	public Set<Map.Entry<String, ?>> entrySet() { | ||||
| 		// we cannot easily wrap every value of the map without being able to synchronize the returned map with the wrapped map | ||||
| 		return (Map<String, ?>) wrapReflectEx(() -> entries.invoke(__getRuntimeInstance())); | ||||
| 		return (Set<Map.Entry<String, ?>>) wrapReflectEx(() -> entrySet.invoke(__getRuntimeInstance())); | ||||
| 	} | ||||
| 	public int size() { | ||||
| 		return (int) wrapReflectEx(() -> size.invoke(__getRuntimeInstance())); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectConstructor; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| @@ -9,9 +9,9 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class ListTag extends CollectionTag { | ||||
| 	public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.ListTag")); | ||||
| 	public static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor()); | ||||
| 	private static final ReflectMethod<?> getCompound = wrapEx(() -> MAPPING.mojMethod("getCompound", int.class)); | ||||
| 	public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.ListTag")); | ||||
| 	public static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor()); | ||||
| 	private static final ReflectMethod<?> getCompound = wrapEx(() -> REFLECT.method("getCompound", int.class)); | ||||
|  | ||||
| 	public CompoundTag getCompound(int index) { | ||||
| 		return wrap(wrapReflectEx(() -> getCompound.invoke(__getRuntimeInstance(), index)), CompoundTag.class); | ||||
|   | ||||
| @@ -1,18 +1,15 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| 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 java.io.File; | ||||
| import java.nio.file.Path; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| public class NbtAccounter extends ReflectWrapper { | ||||
| 	public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.NbtAccounter")); | ||||
| 	private static final ReflectMethod<?> unlimitedHeap = wrapEx(() -> MAPPING.mojMethod("unlimitedHeap")); | ||||
| 	public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.NbtAccounter")); | ||||
| 	private static final ReflectMethod<?> unlimitedHeap = wrapEx(() -> REFLECT.method("unlimitedHeap")); | ||||
|  | ||||
| 	private NbtAccounter(Object obj) { | ||||
| 		super(obj); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| 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; | ||||
|  | ||||
| @@ -10,9 +10,9 @@ import java.nio.file.Path; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| public class NbtIo extends ReflectWrapper { | ||||
| 	public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.NbtIo")); | ||||
| 	private static final ReflectMethod<?> readCompressed = wrapEx(() -> MAPPING.mojMethod("readCompressed", Path.class, NbtAccounter.MAPPING)); | ||||
| 	private static final ReflectMethod<?> writeCompressed = wrapEx(() -> MAPPING.mojMethod("writeCompressed", CompoundTag.MAPPING, Path.class)); | ||||
| 	public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.NbtIo")); | ||||
| 	private static final ReflectMethod<?> readCompressed = wrapEx(() -> REFLECT.method("readCompressed", Path.class, NbtAccounter.REFLECT.get())); | ||||
| 	private static final ReflectMethod<?> writeCompressed = wrapEx(() -> REFLECT.method("writeCompressed", CompoundTag.REFLECT.get(), Path.class)); | ||||
| 	 | ||||
| 	private NbtIo(Object obj) { | ||||
| 		super(obj); | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| 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; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
|  | ||||
| public class StringTag extends ReflectWrapper implements Tag { | ||||
| 	public static final ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.StringTag")); | ||||
| 	public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.StringTag")); | ||||
|  | ||||
|  | ||||
| 	protected StringTag(Object nms) { | ||||
|   | ||||
| @@ -1,23 +1,23 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.nbt; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectField; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect.ClassMapping; | ||||
| 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(Tag.__concrete.class) | ||||
| public interface Tag extends ReflectWrapperI { | ||||
| 	ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.nbt.Tag")); | ||||
| 	ReflectMethod<?> getAsString = wrapEx(() -> MAPPING.mojMethod("getAsString")); | ||||
| 	ReflectField<?> TAG_LIST = wrapEx(() -> MAPPING.mojField("TAG_LIST")); | ||||
| 	ReflectField<?> TAG_COMPOUND = wrapEx(() -> MAPPING.mojField("TAG_COMPOUND")); | ||||
| 	ReflectField<?> TAG_ANY_NUMERIC = wrapEx(() -> MAPPING.mojField("TAG_ANY_NUMERIC")); | ||||
| 	ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.nbt.Tag")); | ||||
| 	ReflectMethod<?> getAsString = wrapEx(() -> REFLECT.method("getAsString")); | ||||
| 	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() { | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.netty.ByteBuf; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectConstructor; | ||||
| import fr.pandacube.lib.reflect.ReflectMethod; | ||||
|  | ||||
| @@ -9,9 +10,9 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class FriendlyByteBuf extends ByteBuf { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.FriendlyByteBuf")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor(ByteBuf.REFLECT.get())); | ||||
|     private static final ReflectMethod<?> writeUtf = wrapEx(() -> MAPPING.mojMethod("writeUtf", String.class)); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.FriendlyByteBuf")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(ByteBuf.REFLECT.get())); | ||||
|     private static final ReflectMethod<?> writeUtf = wrapEx(() -> REFLECT.method("writeUtf", String.class)); | ||||
|  | ||||
|     public FriendlyByteBuf(ByteBuf parent) { | ||||
|         this(wrapReflectEx(() -> CONSTRUCTOR.instantiate(unwrap(parent)))); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.chat; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| 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.ReflectWrapper; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI; | ||||
| @@ -9,7 +10,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| @ConcreteWrapper(Component.__concrete.class) | ||||
| public interface Component extends ReflectWrapperI { | ||||
|     NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.chat.Component")); | ||||
|     ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.chat.Component")); | ||||
|  | ||||
|  | ||||
|     class __concrete extends ReflectWrapper implements Component { | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol.custom.CustomPacketPayload; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectConstructor; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
|  | ||||
| @@ -9,8 +10,8 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class ClientboundCustomPayloadPacket extends ReflectWrapper implements Packet { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor(CustomPacketPayload.MAPPING.runtimeClass())); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(CustomPacketPayload.REFLECT.get())); | ||||
|  | ||||
|     protected ClientboundCustomPayloadPacket(Object obj) { | ||||
|         super(obj); | ||||
|   | ||||
| @@ -1,18 +1,19 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
| 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.wrapper.ReflectWrapper; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class ClientboundGameEventPacket extends ReflectWrapper implements Packet { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.protocol.game.ClientboundGameEventPacket")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor(Type.MAPPING.runtimeClass(), float.class)); | ||||
|     private static final ReflectField<?> FIELD_RAIN_LEVEL_CHANGE = wrapEx(() -> MAPPING.mojField("RAIN_LEVEL_CHANGE")); | ||||
|     private static final ReflectField<?> FIELD_THUNDER_LEVEL_CHANGE = wrapEx(() -> MAPPING.mojField("THUNDER_LEVEL_CHANGE")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.protocol.game.ClientboundGameEventPacket")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(Type.REFLECT.get(), float.class)); | ||||
|     private static final ReflectField<?> FIELD_RAIN_LEVEL_CHANGE = wrapEx(() -> REFLECT.field("RAIN_LEVEL_CHANGE")); | ||||
|     private static final ReflectField<?> FIELD_THUNDER_LEVEL_CHANGE = wrapEx(() -> REFLECT.field("THUNDER_LEVEL_CHANGE")); | ||||
|  | ||||
|     public static Type RAIN_LEVEL_CHANGE() { | ||||
|         return wrap(wrapReflectEx(FIELD_RAIN_LEVEL_CHANGE::getStaticValue), Type.class); | ||||
| @@ -31,7 +32,7 @@ public class ClientboundGameEventPacket extends ReflectWrapper implements Packet | ||||
|  | ||||
|  | ||||
|     public static class Type extends ReflectWrapper { | ||||
|         public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.protocol.game.ClientboundGameEventPacket$Type")); | ||||
|         public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.protocol.game.ClientboundGameEventPacket$Type")); | ||||
|  | ||||
|         protected Type(Object obj) { | ||||
|             super(obj); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| 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.ReflectWrapper; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI; | ||||
| @@ -9,7 +10,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| @ConcreteWrapper(Packet.__concrete.class) | ||||
| public interface Packet extends ReflectWrapperI { | ||||
|     NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.protocol.Packet")); | ||||
|     ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.protocol.Packet")); | ||||
|  | ||||
|     class __concrete extends ReflectWrapper implements Packet { | ||||
|         protected __concrete(Object obj) { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol.custom; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectConstructor; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; | ||||
|  | ||||
| @@ -8,8 +9,8 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class BrandPayload extends ReflectWrapper implements CustomPacketPayload { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.protocol.common.custom.BrandPayload")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> MAPPING.runtimeReflect().constructor(String.class)); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.protocol.common.custom.BrandPayload")); | ||||
|     private static final ReflectConstructor<?> CONSTRUCTOR = wrapEx(() -> REFLECT.constructor(String.class)); | ||||
|  | ||||
|     public BrandPayload(String brand) { | ||||
|         this(wrapReflectEx(() -> CONSTRUCTOR.instantiate(brand))); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.protocol.custom; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| 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.ReflectWrapper; | ||||
| import fr.pandacube.lib.reflect.wrapper.ReflectWrapperI; | ||||
| @@ -9,7 +10,7 @@ import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| @ConcreteWrapper(CustomPacketPayload.__concrete.class) | ||||
| public interface CustomPacketPayload extends ReflectWrapperI { | ||||
|     NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.network.protocol.common.custom.CustomPacketPayload")); | ||||
|     ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.network.protocol.common.custom.CustomPacketPayload")); | ||||
|  | ||||
|     class __concrete extends ReflectWrapper implements CustomPacketPayload { | ||||
|         protected __concrete(Object obj) { | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.resources; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| 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 ResourceLocation extends ReflectWrapper { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.resources.ResourceLocation")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.resources.ResourceLocation")); | ||||
|  | ||||
|  | ||||
|     protected ResourceLocation(Object obj) { | ||||
|   | ||||
| @@ -1,15 +1,16 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.server; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.paper.reflect.wrapper.minecraft.world.ChunkStorage; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
| import fr.pandacube.lib.reflect.ReflectField; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapReflectEx; | ||||
|  | ||||
| public class ChunkMap extends ChunkStorage { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.server.level.ChunkMap")); | ||||
|     private static final ReflectField<?> FIELD_level = wrapEx(() -> MAPPING.mojField("level")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.server.level.ChunkMap")); | ||||
|     private static final ReflectField<?> FIELD_level = wrapEx(() -> REFLECT.field("level")); | ||||
|  | ||||
|     public final ServerLevel level; | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| package fr.pandacube.lib.paper.reflect.wrapper.minecraft.server; | ||||
|  | ||||
| import fr.pandacube.lib.paper.reflect.NMSReflect; | ||||
| import fr.pandacube.lib.reflect.Reflect; | ||||
| import fr.pandacube.lib.reflect.ReflectClass; | ||||
|  | ||||
| import static fr.pandacube.lib.util.ThrowableUtil.wrapEx; | ||||
|  | ||||
| public class DedicatedPlayerList extends PlayerList { | ||||
|     public static final NMSReflect.ClassMapping MAPPING = wrapEx(() -> NMSReflect.mojClass("net.minecraft.server.dedicated.DedicatedPlayerList")); | ||||
|     public static final ReflectClass<?> REFLECT = wrapEx(() -> Reflect.ofClass("net.minecraft.server.dedicated.DedicatedPlayerList")); | ||||
|  | ||||
|     protected DedicatedPlayerList(Object obj) { | ||||
|         super(obj); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user