Compare commits
44 commits
legacy/nms
...
master
Author | SHA1 | Date | |
---|---|---|---|
14a6c330d2 | |||
aaecbf447d | |||
e142050b7a | |||
|
cc59b911a5 | ||
|
7651be3f14 | ||
|
c74684a7a8 | ||
|
0c61f5c517 | ||
|
fa5e26ffd8 | ||
|
7e392eb0ec | ||
|
48762f9703 | ||
|
59d4f84625 | ||
|
c2074e4504 | ||
|
7586ba5502 | ||
3f382583a7 | |||
bd6a24a242 | |||
603f144d26 | |||
445dc1d2e9 | |||
|
cba7cbf6e5 | ||
|
f25c727a15 | ||
|
ef49048ee1 | ||
|
89d4234a8c | ||
|
b5df2bb80c | ||
|
5557a76976 | ||
|
89cbc9b8be | ||
|
8206cb403b | ||
|
293c239fb8 | ||
|
2af12469be | ||
|
74635f22e3 | ||
|
eff2fb016f | ||
|
886fba1822 | ||
|
e9039eb56d | ||
|
a5e7a4afdc | ||
|
fa08e1b5ff | ||
|
d7429c301e | ||
|
8a2daf1580 | ||
|
4bb68cc5d2 | ||
|
616e215797 | ||
|
8d11483afe | ||
|
31ff370dc0 | ||
|
19fdd6ff66 | ||
|
63ac9bb38e | ||
|
a5acef0666 | ||
|
c390ebbd5e | ||
|
15640e5886 |
25 changed files with 3071 additions and 682 deletions
26
.github/workflows/maven.yml
vendored
26
.github/workflows/maven.yml
vendored
|
@ -1,26 +0,0 @@
|
||||||
name: Java CI
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Set up JDK 11
|
|
||||||
uses: actions/setup-java@v1
|
|
||||||
with:
|
|
||||||
java-version: 11
|
|
||||||
|
|
||||||
- name: Build with Maven
|
|
||||||
run: mvn -B package --file pom.xml
|
|
||||||
|
|
||||||
- run: mkdir -p target
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@master
|
|
||||||
with:
|
|
||||||
name: MapReflectionAPI
|
|
||||||
path: target
|
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
@ -30,7 +30,7 @@
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
104
README.md
104
README.md
|
@ -1,9 +1,11 @@
|
||||||
# MapReflectionAPI
|
# MapReflectionAPI
|
||||||
|
|
||||||
This plugin helps developer with viewing images on maps. It supports Spigot 1.12 - 1.19.
|
This plugin helps developer with displaying images on maps. It supports Spigot 1.12 - 1.21.
|
||||||
|
|
||||||
## Usage:
|
## Usage:
|
||||||
|
|
||||||
|
### Using the API:
|
||||||
|
|
||||||
First, include the API using Maven:
|
First, include the API using Maven:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
@ -16,7 +18,7 @@ First, include the API using Maven:
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>tech.sbdevelopment</groupId>
|
<groupId>tech.sbdevelopment</groupId>
|
||||||
<artifactId>MapReflectionAPI</artifactId>
|
<artifactId>MapReflectionAPI</artifactId>
|
||||||
<version>1.4.3</version>
|
<version>1.6.4</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
@ -50,17 +52,8 @@ controller.showInHand(p, true);
|
||||||
It's also possible to split one image onto multiple itemframes. For example using the following code.
|
It's also possible to split one image onto multiple itemframes. For example using the following code.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
BufferedImage leftTopFrame = ...;
|
//--- Wrap image (into 2 rows and 2 columns) ---
|
||||||
BufferedImage leftBottomFrame = ...;
|
MultiMapWrapper wrapper = MapReflectionAPI.getMapManager().wrapMultiImage(ImageIO.read(new File("image.png")), 2, 2);
|
||||||
BufferedImage rightTopFrame = ...;
|
|
||||||
BufferedImage rightBottomFrame = ...;
|
|
||||||
BufferedImage[][] images = {
|
|
||||||
{leftBottomFrame, leftTopFrame},
|
|
||||||
{rightBottomFrame, rightTopFrame}
|
|
||||||
};
|
|
||||||
|
|
||||||
//--- Wrap image ---
|
|
||||||
MultiMapWrapper wrapper = MapReflectionAPI.getMapManager().wrapMultiImage(images);
|
|
||||||
MultiMapController controller = wrapper.getController();
|
MultiMapController controller = wrapper.getController();
|
||||||
|
|
||||||
final Player p = Bukkit.getPlayer("SBDeveloper");
|
final Player p = Bukkit.getPlayer("SBDeveloper");
|
||||||
|
@ -81,17 +74,94 @@ ItemFrame leftBottomFrame = ...;
|
||||||
ItemFrame rightTopFrame = ...;
|
ItemFrame rightTopFrame = ...;
|
||||||
ItemFrame rightBottomFrame = ...;
|
ItemFrame rightBottomFrame = ...;
|
||||||
ItemFrame[][] frames = {
|
ItemFrame[][] frames = {
|
||||||
{leftBottomFrame, leftTopFrame},
|
{leftTopFrame, rightTopFrame},
|
||||||
{rightBottomFrame, rightTopFrame}
|
{leftBottomFrame, rightBottomFrame}
|
||||||
};
|
};
|
||||||
controller.showInFrames(p, frames, true);
|
controller.showInFrames(p, frames, true);
|
||||||
```
|
```
|
||||||
|
|
||||||
More information can be found on the [JavaDoc](https://sbdevelopment.tech/javadoc/mapreflectionapi/).
|
More information can be found on the [JavaDoc](https://sbdevelopment.tech/javadoc/mapreflectionapi/).
|
||||||
|
|
||||||
|
### Notes on map render distance:
|
||||||
|
|
||||||
|
MapReflectionAPI does not implement render distance to images shown on maps. This should be implemented by yourself. An example of this is below.
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
|
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||||
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||||
|
|
||||||
|
public class MapRenderDistanceListener implements Listener {
|
||||||
|
private static final int MAP_RENDER_DISTANCE_SQUARED = 1024;
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent e) {
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
|
||||||
|
//Show the maps to the player which are within distance
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerLeave(PlayerQuitEvent e) {
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
|
||||||
|
//Hide the maps to the player which are within distance
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerChangeWorld(PlayerChangedWorldEvent e) {
|
||||||
|
//Hide all the maps in the e.getFrom() world
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
|
||||||
|
//Show the maps to the player which are within distance in e.getPlayer().getWorld()
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onPlayerDeath(PlayerDeathEvent e) {
|
||||||
|
//Hide all the maps in the e.getEntity().getWorld()
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
|
public void onPlayerRespawn(PlayerRespawnEvent e) {
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
|
||||||
|
//Show the maps to the player which are within distance in e.getPlayer().getWorld()
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerMove(PlayerMoveEvent e) {
|
||||||
|
//Hide all the maps in the e.getFrom() world
|
||||||
|
//Show the maps to the player which are within distance in e.getTo().getWorld()
|
||||||
|
|
||||||
|
//FOR EXAMPLE:
|
||||||
|
if (e.getTo() == null) return;
|
||||||
|
if (e.getFrom().getChunk().equals(e.getTo().getChunk())) return;
|
||||||
|
|
||||||
|
for (Frame frame : API.getFramesInWorld(e.getPlayer().getWorld())) {
|
||||||
|
double distanceSquared = e.getTo().distanceSquared(frame.getLocation());
|
||||||
|
|
||||||
|
if (distanceSquared > MAP_RENDER_DISTANCE_SQUARED) {
|
||||||
|
API.hideFrame(e.getPlayer(), frame);
|
||||||
|
} else {
|
||||||
|
API.showFrame(e.getPlayer(), frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerTeleport(PlayerTeleportEvent e) {
|
||||||
|
//Hide all the maps in the e.getFrom() world
|
||||||
|
//Show the maps to the player which are within distance in e.getTo().getWorld()
|
||||||
|
|
||||||
|
//SEE EXAMPLE ABOVE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Credits:
|
## Credits:
|
||||||
|
|
||||||
This is a fork of [MapManager](https://github.com/InventivetalentDev/MapManager). It updates the API to 1.19 and uses
|
This is a fork of [MapManager](https://github.com/InventivetalentDev/MapManager). It updates the API to the latest version of Minecraft and uses other dependencies.
|
||||||
other dependencies.
|
|
||||||
|
|
||||||
This plugin includes classes from BKCommonLib. Please checkout the README in that package for more information.
|
This plugin includes classes from BKCommonLib. Please checkout the README in that package for more information.
|
||||||
|
|
43
pom.xml
43
pom.xml
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<groupId>tech.sbdevelopment</groupId>
|
<groupId>tech.sbdevelopment</groupId>
|
||||||
<artifactId>MapReflectionAPI</artifactId>
|
<artifactId>MapReflectionAPI</artifactId>
|
||||||
<version>1.4.4</version>
|
<version>1.6.6</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>MapReflectionAPI</name>
|
<name>MapReflectionAPI</name>
|
||||||
|
@ -48,14 +48,14 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.14.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>11</release>
|
<release>11</release>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<path>
|
<path>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.24</version>
|
<version>1.18.34</version>
|
||||||
</path>
|
</path>
|
||||||
</annotationProcessorPaths>
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>3.6.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
|
@ -81,6 +81,10 @@
|
||||||
<pattern>org.bstats</pattern>
|
<pattern>org.bstats</pattern>
|
||||||
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bstats</shadedPattern>
|
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bstats</shadedPattern>
|
||||||
</relocation>
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>com.cryptomorin.xseries</pattern>
|
||||||
|
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.xseries</shadedPattern>
|
||||||
|
</relocation>
|
||||||
</relocations>
|
</relocations>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
@ -103,12 +107,19 @@
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.34</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.4.0</version>
|
<version>3.11.2</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>11</release>
|
<release>11</release>
|
||||||
<sourcepath>${maven.lombok.delombok-target}</sourcepath>
|
<sourcepath>${maven.lombok.delombok-target}</sourcepath>
|
||||||
|
@ -155,40 +166,48 @@
|
||||||
<id>dmulloy2-repo</id>
|
<id>dmulloy2-repo</id>
|
||||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>jitpack.io</id>
|
||||||
|
<url>https://jitpack.io</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.19.4-R0.1-SNAPSHOT</version>
|
<version>1.21.5-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.24</version>
|
<version>1.18.34</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bstats</groupId>
|
<groupId>org.bstats</groupId>
|
||||||
<artifactId>bstats-bukkit</artifactId>
|
<artifactId>bstats-bukkit</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>3.1.0</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.cryptomorin</groupId>
|
||||||
|
<artifactId>XSeries</artifactId>
|
||||||
|
<version>13.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Libraries below are provided by Spigot -->
|
<!-- Libraries below are provided by CraftBukkit -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains</groupId>
|
<groupId>org.jetbrains</groupId>
|
||||||
<artifactId>annotations-java5</artifactId>
|
<artifactId>annotations-java5</artifactId>
|
||||||
<version>23.0.0</version>
|
<version>24.1.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-transport</artifactId>
|
<artifactId>netty-transport</artifactId>
|
||||||
<version>4.1.77.Final</version>
|
<version>4.1.118.Final</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -27,6 +27,8 @@ import java.awt.*;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static com.cryptomorin.xseries.reflection.XReflection.supports;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional functionality on top of Bukkit's MapPalette
|
* Additional functionality on top of Bukkit's MapPalette
|
||||||
*/
|
*/
|
||||||
|
@ -50,14 +52,12 @@ public class MapColorPalette {
|
||||||
MCSDBubbleFormat bubbleData = new MCSDBubbleFormat();
|
MCSDBubbleFormat bubbleData = new MCSDBubbleFormat();
|
||||||
try {
|
try {
|
||||||
String bub_path_postfix;
|
String bub_path_postfix;
|
||||||
if (ReflectionUtil.supports(17)) {
|
if (supports(17)) {
|
||||||
bub_path_postfix = "map_1_17.bub";
|
bub_path_postfix = "map_1_17.bub";
|
||||||
} else if (ReflectionUtil.supports(16)) {
|
} else if (supports(16)) {
|
||||||
bub_path_postfix = "map_1_16.bub";
|
bub_path_postfix = "map_1_16.bub";
|
||||||
} else if (ReflectionUtil.supports(12)) {
|
|
||||||
bub_path_postfix = "map_1_12.bub";
|
|
||||||
} else {
|
} else {
|
||||||
bub_path_postfix = "map_1_8_8.bub";
|
bub_path_postfix = "map_1_12.bub";
|
||||||
}
|
}
|
||||||
String bub_path = "/tech/sbdevelopment/mapreflectionapi/libs/bkcommonlib/internal/resources/map/" + bub_path_postfix;
|
String bub_path = "/tech/sbdevelopment/mapreflectionapi/libs/bkcommonlib/internal/resources/map/" + bub_path_postfix;
|
||||||
InputStream input = MapColorPalette.class.getResourceAsStream(bub_path);
|
InputStream input = MapColorPalette.class.getResourceAsStream(bub_path);
|
||||||
|
|
|
@ -30,11 +30,12 @@ import tech.sbdevelopment.mapreflectionapi.listeners.MapListener;
|
||||||
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
|
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
|
||||||
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.MainUtil;
|
import tech.sbdevelopment.mapreflectionapi.utils.MainUtil;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager;
|
import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static com.cryptomorin.xseries.reflection.XReflection.supports;
|
||||||
|
|
||||||
public class MapReflectionAPI extends JavaPlugin {
|
public class MapReflectionAPI extends JavaPlugin {
|
||||||
private static MapReflectionAPI instance;
|
private static MapReflectionAPI instance;
|
||||||
private static MapManager mapManager;
|
private static MapManager mapManager;
|
||||||
|
@ -67,8 +68,8 @@ public class MapReflectionAPI extends JavaPlugin {
|
||||||
getLogger().info("MapReflectionAPI v" + getDescription().getVersion());
|
getLogger().info("MapReflectionAPI v" + getDescription().getVersion());
|
||||||
getLogger().info("Made by © Copyright SBDevelopment 2023");
|
getLogger().info("Made by © Copyright SBDevelopment 2023");
|
||||||
|
|
||||||
if (!ReflectionUtil.supports(12)) {
|
if (!supports(12)) {
|
||||||
getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.19.4!");
|
getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.20.5!");
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package tech.sbdevelopment.mapreflectionapi.api;
|
package tech.sbdevelopment.mapreflectionapi.api;
|
||||||
|
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.ItemFrame;
|
import org.bukkit.entity.ItemFrame;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
@ -117,13 +116,4 @@ public interface MapController extends IMapController {
|
||||||
* @param frame {@link ItemFrame} to clear
|
* @param frame {@link ItemFrame} to clear
|
||||||
*/
|
*/
|
||||||
void clearFrame(Player player, ItemFrame frame);
|
void clearFrame(Player player, ItemFrame frame);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an {@link ItemFrame} by its entity ID
|
|
||||||
*
|
|
||||||
* @param world The world the {@link ItemFrame} is in
|
|
||||||
* @param entityId Entity-ID of the {@link ItemFrame}
|
|
||||||
* @return The found {@link ItemFrame}, or <code>null</code>
|
|
||||||
*/
|
|
||||||
ItemFrame getItemFrameById(World world, int entityId);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,13 @@
|
||||||
package tech.sbdevelopment.mapreflectionapi.api;
|
package tech.sbdevelopment.mapreflectionapi.api;
|
||||||
|
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.ItemFrame;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
||||||
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
||||||
|
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -30,6 +33,8 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import static com.cryptomorin.xseries.reflection.XReflection.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link MapManager} manages all the maps. It also contains functions for wrapping.
|
* The {@link MapManager} manages all the maps. It also contains functions for wrapping.
|
||||||
*/
|
*/
|
||||||
|
@ -172,6 +177,30 @@ public class MapManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an {@link ItemFrame} by its entity ID
|
||||||
|
*
|
||||||
|
* @param world The world the {@link ItemFrame} is in
|
||||||
|
* @param entityId Entity-ID of the {@link ItemFrame}
|
||||||
|
* @return The found {@link ItemFrame}, or <code>null</code>
|
||||||
|
*/
|
||||||
|
public ItemFrame getItemFrameById(World world, int entityId) {
|
||||||
|
Object worldHandle = ReflectionUtil.getHandle(world);
|
||||||
|
Object nmsEntity = ReflectionUtil.callMethod(worldHandle, supports(18) ? "a" : "getEntity", entityId);
|
||||||
|
if (nmsEntity == null) return null;
|
||||||
|
|
||||||
|
Object craftEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity");
|
||||||
|
if (craftEntity == null) return null;
|
||||||
|
|
||||||
|
Class<?> itemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame");
|
||||||
|
if (itemFrameClass == null) return null;
|
||||||
|
|
||||||
|
if (craftEntity.getClass().isAssignableFrom(itemFrameClass))
|
||||||
|
return (ItemFrame) itemFrameClass.cast(craftEntity);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an occupied map ID
|
* Register an occupied map ID
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,6 +27,9 @@ import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.cryptomorin.xseries.reflection.XReflection.*;
|
||||||
|
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link MapSender} sends the Map packets to players.
|
* The {@link MapSender} sends the Map packets to players.
|
||||||
*/
|
*/
|
||||||
|
@ -82,8 +85,9 @@ public class MapSender {
|
||||||
}, 0, 2);
|
}, 0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Class<?> packetPlayOutMapClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutMap");
|
private static final Class<?> packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap");
|
||||||
private static final Class<?> worldMapData = ReflectionUtil.supports(17) ? ReflectionUtil.getNMSClass("world.level.saveddata.maps", "WorldMap$b") : null;
|
private static final Class<?> worldMapData = supports(17) ? getNMSClass("world.level.saveddata.maps", supports(21, 2) ? "WorldMap$c" : "WorldMap$b") : null; //1.21.2+ uses WorldMap$c, 1.17+ uses WorldMap$b
|
||||||
|
private static final Class<?> mapIdClazz = supports(21) ? getNMSClass("world.level.saveddata.maps", "MapId") : null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a map to a player
|
* Send a map to a player
|
||||||
|
@ -108,9 +112,28 @@ public class MapSender {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int id = -id0;
|
int id = -id0;
|
||||||
|
|
||||||
Object packet;
|
Object packet;
|
||||||
if (ReflectionUtil.supports(17)) { //1.17+
|
if (supports(20, 4)) { //1.20.5+
|
||||||
|
Object updateData = ReflectionUtil.callConstructor(worldMapData,
|
||||||
|
content.minX, //X pos
|
||||||
|
content.minY, //Y pos
|
||||||
|
content.maxX, //X size (2nd X pos)
|
||||||
|
content.maxY, //Y size (2nd Y pos)
|
||||||
|
content.array //Data
|
||||||
|
);
|
||||||
|
|
||||||
|
Object mapId = ReflectionUtil.callConstructor(mapIdClazz, id);
|
||||||
|
|
||||||
|
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
|
||||||
|
mapId, //ID
|
||||||
|
(byte) 0, //Scale, 0 = 1 block per pixel
|
||||||
|
false, //Show icons
|
||||||
|
new ReflectionUtil.CollectionParam<>(), //Icons
|
||||||
|
updateData
|
||||||
|
);
|
||||||
|
} else if (supports(17)) { //1.17+
|
||||||
Object updateData = ReflectionUtil.callConstructor(worldMapData,
|
Object updateData = ReflectionUtil.callConstructor(worldMapData,
|
||||||
content.minX, //X pos
|
content.minX, //X pos
|
||||||
content.minY, //Y pos
|
content.minY, //Y pos
|
||||||
|
@ -126,7 +149,7 @@ public class MapSender {
|
||||||
new ReflectionUtil.CollectionParam<>(), //Icons
|
new ReflectionUtil.CollectionParam<>(), //Icons
|
||||||
updateData
|
updateData
|
||||||
);
|
);
|
||||||
} else if (ReflectionUtil.supports(14)) { //1.16-1.14
|
} else if (supports(14)) { //1.16-1.14
|
||||||
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
|
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
|
||||||
id, //ID
|
id, //ID
|
||||||
(byte) 0, //Scale, 0 = 1 block per pixel
|
(byte) 0, //Scale, 0 = 1 block per pixel
|
||||||
|
@ -153,7 +176,7 @@ public class MapSender {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtil.sendPacket(player, packet);
|
sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
|
|
||||||
package tech.sbdevelopment.mapreflectionapi.api;
|
package tech.sbdevelopment.mapreflectionapi.api;
|
||||||
|
|
||||||
import org.bukkit.*;
|
import lombok.Getter;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.entity.ItemFrame;
|
import org.bukkit.entity.ItemFrame;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
@ -30,6 +33,7 @@ import tech.sbdevelopment.mapreflectionapi.api.events.MapContentUpdateEvent;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
||||||
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
|
import tech.sbdevelopment.mapreflectionapi.utils.XMaterial;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -37,19 +41,18 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.cryptomorin.xseries.reflection.XReflection.*;
|
||||||
|
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle;
|
||||||
|
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MapWrapper} wraps one image.
|
* A {@link MapWrapper} wraps one image.
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
public class MapWrapper extends AbstractMapWrapper {
|
public class MapWrapper extends AbstractMapWrapper {
|
||||||
private static final String REFERENCE_METADATA = "MAP_WRAPPER_REF";
|
public static final String REFERENCE_METADATA = "MAP_WRAPPER_REF";
|
||||||
protected ArrayImage content;
|
protected ArrayImage content;
|
||||||
|
|
||||||
private static final Material MAP_MATERIAL;
|
|
||||||
|
|
||||||
static {
|
|
||||||
MAP_MATERIAL = ReflectionUtil.supports(13) ? Material.FILLED_MAP : Material.MAP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new {@link MapWrapper}
|
* Construct a new {@link MapWrapper}
|
||||||
*
|
*
|
||||||
|
@ -59,13 +62,13 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
this.content = image;
|
this.content = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Class<?> craftStackClass = ReflectionUtil.getCraftClass("inventory.CraftItemStack");
|
private static final Class<?> craftStackClass = getCraftClass("inventory.CraftItemStack");
|
||||||
private static final Class<?> setSlotPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutSetSlot");
|
private static final Class<?> setSlotPacketClass = getNMSClass("network.protocol.game", "PacketPlayOutSetSlot");
|
||||||
private static final Class<?> entityClass = ReflectionUtil.getNMSClass("world.entity", "Entity");
|
private static final Class<?> entityClass = getNMSClass("world.entity", "Entity");
|
||||||
private static final Class<?> dataWatcherClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher");
|
private static final Class<?> dataWatcherClass = getNMSClass("network.syncher", "DataWatcher");
|
||||||
private static final Class<?> entityMetadataPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata");
|
private static final Class<?> entityMetadataPacketClass = getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata");
|
||||||
private static final Class<?> entityItemFrameClass = ReflectionUtil.getNMSClass("world.entity.decoration", "EntityItemFrame");
|
private static final Class<?> entityItemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame");
|
||||||
private static final Class<?> dataWatcherItemClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$Item");
|
private static final Class<?> dataWatcherItemClass = getNMSClass("network.syncher", "DataWatcher$Item");
|
||||||
|
|
||||||
protected MapController controller = new MapController() {
|
protected MapController controller = new MapController() {
|
||||||
private final Map<UUID, Integer> viewers = new HashMap<>();
|
private final Map<UUID, Integer> viewers = new HashMap<>();
|
||||||
|
@ -105,7 +108,8 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(@NotNull ArrayImage content) {
|
public void update(@NotNull ArrayImage content) {
|
||||||
MapContentUpdateEvent event = new MapContentUpdateEvent(MapWrapper.this, content);
|
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
|
||||||
|
MapContentUpdateEvent event = new MapContentUpdateEvent(MapWrapper.this, content, async);
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
|
||||||
if (Configuration.getInstance().isImageCache()) {
|
if (Configuration.getInstance().isImageCache()) {
|
||||||
|
@ -162,31 +166,32 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
String inventoryMenuName;
|
String inventoryMenuName;
|
||||||
if (ReflectionUtil.supports(20)) { //1.20
|
if (supports(21)) {
|
||||||
inventoryMenuName = "bQ";
|
//1.21.5 = bQ, 1.21 - 1.21.4 = cc
|
||||||
} else if (ReflectionUtil.supports(19)) { //1.19
|
inventoryMenuName = supports(21, 4) ? "bQ" : "cc";
|
||||||
inventoryMenuName = ReflectionUtil.VER_MINOR == 3 ? "bO" : "bT"; //1.19.4 = bO, >= 1.19.3 = bT
|
} else if (supports(20)) {
|
||||||
} else if (ReflectionUtil.supports(18)) { //1.18
|
//1.20.5 = cb, 1.20.2 - 1.20.4 = bR, 1.20(.1) = bQ
|
||||||
inventoryMenuName = ReflectionUtil.VER_MINOR == 1 ? "bV" : "bU"; //1.18.1 = ap, 1.18(.2) = ao
|
inventoryMenuName = supports(20, 4) ? "cb" : supports(20, 2) ? "bR" : "bQ";
|
||||||
} else if (ReflectionUtil.supports(17)) { //1.17, same as 1.18(.2)
|
} else if (supports(19)) {
|
||||||
|
//1.19.4 = bO, >= 1.19.3 = bT
|
||||||
|
inventoryMenuName = supports(19, 3) ? "bO" : "bT";
|
||||||
|
} else if (supports(18)) {
|
||||||
|
//1.18.1 = ap, 1.18(.2) = ao
|
||||||
|
inventoryMenuName = supports(18, 1) ? "bV" : "bU";
|
||||||
|
} else if (supports(17)) {
|
||||||
|
//1.17, same as 1.18(.2)
|
||||||
inventoryMenuName = "bU";
|
inventoryMenuName = "bU";
|
||||||
} else { //1.12-1.16
|
} else {
|
||||||
|
//1.12-1.16
|
||||||
inventoryMenuName = "defaultContainer";
|
inventoryMenuName = "defaultContainer";
|
||||||
}
|
}
|
||||||
Object inventoryMenu = ReflectionUtil.getField(ReflectionUtil.getHandle(player), inventoryMenuName);
|
Object inventoryMenu = ReflectionUtil.getField(getHandle(player), inventoryMenuName);
|
||||||
|
|
||||||
ItemStack stack;
|
Object nmsStack = asCraftItemStack(player);
|
||||||
if (ReflectionUtil.supports(13)) {
|
|
||||||
stack = new ItemStack(MAP_MATERIAL, 1);
|
|
||||||
} else {
|
|
||||||
stack = new ItemStack(MAP_MATERIAL, 1, (short) getMapId(player));
|
|
||||||
}
|
|
||||||
|
|
||||||
Object nmsStack = createCraftItemStack(stack, (short) getMapId(player));
|
|
||||||
|
|
||||||
Object packet;
|
Object packet;
|
||||||
if (ReflectionUtil.supports(17)) { //1.17+
|
if (supports(17)) { //1.17+
|
||||||
int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, ReflectionUtil.supports(18) ? "j" : "getStateId");
|
int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, supports(18) ? "j" : "getStateId");
|
||||||
|
|
||||||
packet = ReflectionUtil.callConstructor(setSlotPacketClass,
|
packet = ReflectionUtil.callConstructor(setSlotPacketClass,
|
||||||
0, //0 = Player inventory
|
0, //0 = Player inventory
|
||||||
|
@ -202,7 +207,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtil.sendPacketSync(player, packet);
|
sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -212,7 +217,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInHand(Player player, boolean force) {
|
public void showInHand(Player player, boolean force) {
|
||||||
if (player.getInventory().getItemInMainHand().getType() != MAP_MATERIAL && !force)
|
if (player.getInventory().getItemInMainHand().getType() != XMaterial.FILLED_MAP.parseMaterial() && !force)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
|
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
|
||||||
|
@ -230,7 +235,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInFrame(Player player, ItemFrame frame, boolean force) {
|
public void showInFrame(Player player, ItemFrame frame, boolean force) {
|
||||||
if (frame.getItem().getType() != MAP_MATERIAL && !force)
|
if (frame.getItem().getType() != XMaterial.FILLED_MAP.parseMaterial() && !force)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
showInFrame(player, frame.getEntityId());
|
showInFrame(player, frame.getEntityId());
|
||||||
|
@ -245,7 +250,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
public void showInFrame(Player player, int entityId, String debugInfo) {
|
public void showInFrame(Player player, int entityId, String debugInfo) {
|
||||||
if (!isViewing(player)) return;
|
if (!isViewing(player)) return;
|
||||||
|
|
||||||
ItemStack stack = new ItemStack(MAP_MATERIAL, 1);
|
ItemStack stack = new ItemStack(XMaterial.FILLED_MAP.parseMaterial(), 1);
|
||||||
if (debugInfo != null) {
|
if (debugInfo != null) {
|
||||||
ItemMeta itemMeta = stack.getItemMeta();
|
ItemMeta itemMeta = stack.getItemMeta();
|
||||||
itemMeta.setDisplayName(debugInfo);
|
itemMeta.setDisplayName(debugInfo);
|
||||||
|
@ -253,7 +258,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
|
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
|
||||||
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
|
ItemFrame frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityId);
|
||||||
if (frame != null) {
|
if (frame != null) {
|
||||||
frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance());
|
frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance());
|
||||||
frame.setMetadata(REFERENCE_METADATA, new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper.this));
|
frame.setMetadata(REFERENCE_METADATA, new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper.this));
|
||||||
|
@ -267,7 +272,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
public void clearFrame(Player player, int entityId) {
|
public void clearFrame(Player player, int entityId) {
|
||||||
sendItemFramePacket(player, entityId, null, -1);
|
sendItemFramePacket(player, entityId, null, -1);
|
||||||
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
|
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
|
||||||
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
|
ItemFrame frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityId);
|
||||||
if (frame != null) frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance());
|
if (frame != null) frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -277,22 +282,8 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
clearFrame(player, frame.getEntityId());
|
clearFrame(player, frame.getEntityId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Object asCraftItemStack(Player player) {
|
||||||
public ItemFrame getItemFrameById(World world, int entityId) {
|
return createCraftItemStack(new ItemStack(XMaterial.FILLED_MAP.parseMaterial(), 1, (short) getMapId(player)), (short) getMapId(player));
|
||||||
Object worldHandle = ReflectionUtil.getHandle(world);
|
|
||||||
Object nmsEntity = ReflectionUtil.callMethod(worldHandle, ReflectionUtil.supports(18) ? "a" : "getEntity", entityId);
|
|
||||||
if (nmsEntity == null) return null;
|
|
||||||
|
|
||||||
Object craftEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity");
|
|
||||||
if (craftEntity == null) return null;
|
|
||||||
|
|
||||||
Class<?> itemFrameClass = ReflectionUtil.getNMSClass("world.entity.decoration", "EntityItemFrame");
|
|
||||||
if (itemFrameClass == null) return null;
|
|
||||||
|
|
||||||
if (craftEntity.getClass().isAssignableFrom(itemFrameClass))
|
|
||||||
return (ItemFrame) itemFrameClass.cast(craftEntity);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object createCraftItemStack(@NotNull ItemStack stack, int mapId) {
|
private Object createCraftItemStack(@NotNull ItemStack stack, int mapId) {
|
||||||
|
@ -300,17 +291,34 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
|
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
|
||||||
|
|
||||||
if (ReflectionUtil.supports(13)) {
|
//1.20.5 uses new NBT compound system
|
||||||
|
if (supports(20, 4)) {
|
||||||
|
Object mapIdComponent = ReflectionUtil.getDeclaredField(getNMSClass("core.component", "DataComponents"), supports(21, 4) ? "M" : supports(21, 2) ? "L" : "B"); //1.21.2+ uses L, otherwise B
|
||||||
|
Object mapId1 = ReflectionUtil.callConstructor(getNMSClass("world.level.saveddata.maps", "MapId"), mapId);
|
||||||
|
|
||||||
|
// Use generic reflection because of generics
|
||||||
|
// <T> T ItemStack#b(DataComponentType<? super T> dataComponentType, T t)
|
||||||
|
try {
|
||||||
|
Method m = nmsStack.getClass().getMethod("b", getNMSClass("core.component", "DataComponentType"), Object.class);
|
||||||
|
m.setAccessible(true);
|
||||||
|
m.invoke(nmsStack, mapIdComponent, mapId1);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (supports(13)) {
|
||||||
String nbtObjectName;
|
String nbtObjectName;
|
||||||
if (ReflectionUtil.supports(19)) { //1.19
|
if (supports(20)) { //1.20
|
||||||
|
nbtObjectName = "w";
|
||||||
|
} else if (supports(19)) { //1.19
|
||||||
nbtObjectName = "v";
|
nbtObjectName = "v";
|
||||||
} else if (ReflectionUtil.supports(18)) { //1.18
|
} else if (supports(18)) { //1.18
|
||||||
nbtObjectName = ReflectionUtil.VER_MINOR == 1 ? "t" : "u"; //1.18.1 = t, 1.18(.2) = u
|
nbtObjectName = supports(18, 1) ? "t" : "u"; //1.18.1 = t, 1.18(.2) = u
|
||||||
} else { //1.13 - 1.17
|
} else { //1.13 - 1.17
|
||||||
nbtObjectName = "getOrCreateTag";
|
nbtObjectName = "getOrCreateTag";
|
||||||
}
|
}
|
||||||
Object nbtObject = ReflectionUtil.callMethod(nmsStack, nbtObjectName);
|
Object nbtObject = ReflectionUtil.callMethod(nmsStack, nbtObjectName);
|
||||||
ReflectionUtil.callMethod(nbtObject, ReflectionUtil.supports(18) ? "a" : "setInt", "map", mapId);
|
ReflectionUtil.callMethod(nbtObject, supports(18) ? "a" : "setInt", "map", mapId);
|
||||||
}
|
}
|
||||||
return nmsStack;
|
return nmsStack;
|
||||||
}
|
}
|
||||||
|
@ -319,26 +327,30 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
Object nmsStack = createCraftItemStack(stack, mapId);
|
Object nmsStack = createCraftItemStack(stack, mapId);
|
||||||
|
|
||||||
String dataWatcherObjectName;
|
String dataWatcherObjectName;
|
||||||
if (ReflectionUtil.supports(19)) { //1.19
|
if (supports(21)) { //1.21
|
||||||
dataWatcherObjectName = ReflectionUtil.VER_MINOR == 3 ? "g" : "ao"; //1.19.4 = g, >= 1.19.3 = ao
|
dataWatcherObjectName = supports(21, 2) ? "e" : "f"; //1.21.2+ = e, 1.21(.1) = f
|
||||||
} else if (ReflectionUtil.supports(18)) { //1.18
|
} else if (supports(19, 3)) { //1.19.3 and 1.20(.1)
|
||||||
dataWatcherObjectName = ReflectionUtil.VER_MINOR == 1 ? "ap" : "ao"; //1.18.1 = ap, 1.18(.2) = ao
|
dataWatcherObjectName = "g";
|
||||||
} else if (ReflectionUtil.supports(17)) { //1.17
|
} else if (supports(19)) { //1.19-1.19.2
|
||||||
dataWatcherObjectName = "ao";
|
dataWatcherObjectName = "ao";
|
||||||
} else if (ReflectionUtil.supports(14)) { //1.14 - 1.16
|
} else if (supports(18)) { //1.18
|
||||||
|
dataWatcherObjectName = supports(18, 1) ? "ap" : "ao"; //1.18.1 = ap, 1.18(.2) = ao
|
||||||
|
} else if (supports(17)) { //1.17
|
||||||
|
dataWatcherObjectName = "ao";
|
||||||
|
} else if (supports(14)) { //1.14 - 1.16
|
||||||
dataWatcherObjectName = "ITEM";
|
dataWatcherObjectName = "ITEM";
|
||||||
} else if (ReflectionUtil.supports(13)) { //1.13
|
} else if (supports(13)) { //1.13
|
||||||
dataWatcherObjectName = "e";
|
dataWatcherObjectName = "e";
|
||||||
} else { //1.12
|
} else { //1.12
|
||||||
dataWatcherObjectName = "c";
|
dataWatcherObjectName = "c";
|
||||||
}
|
}
|
||||||
Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, dataWatcherObjectName);
|
Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, dataWatcherObjectName);
|
||||||
|
|
||||||
ReflectionUtil.ListParam list = new ReflectionUtil.ListParam<>();
|
ReflectionUtil.ListParam<Object> list = new ReflectionUtil.ListParam<>();
|
||||||
|
|
||||||
Object packet;
|
Object packet;
|
||||||
if (ReflectionUtil.supports(19, 2)) { //1.19.3
|
if (supports(19, 3)) { //1.19.3
|
||||||
Class<?> dataWatcherRecordClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$b");
|
Class<?> dataWatcherRecordClass = getNMSClass("network.syncher", "DataWatcher$" + (supports(20, 4) ? "c" : "b"));
|
||||||
// Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter
|
// Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter
|
||||||
Object dataWatcherItem;
|
Object dataWatcherItem;
|
||||||
try {
|
try {
|
||||||
|
@ -369,16 +381,7 @@ public class MapWrapper extends AbstractMapWrapper {
|
||||||
ReflectionUtil.setDeclaredField(packet, "b", list);
|
ReflectionUtil.setDeclaredField(packet, "b", list);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtil.sendPacketSync(player, packet);
|
sendPacket(player, packet);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public ArrayImage getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MapController getController() {
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public interface MultiMapController extends IMapController {
|
||||||
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
||||||
*
|
*
|
||||||
* @param player {@link Player} that will be able to see the maps
|
* @param player {@link Player} that will be able to see the maps
|
||||||
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[width][height]</code>)
|
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[rows][columns]</code>)
|
||||||
* @see MapController#showInFrame(Player, int)
|
* @see MapController#showInFrame(Player, int)
|
||||||
*/
|
*/
|
||||||
void showInFrames(Player player, Integer[][] entityIdMatrix);
|
void showInFrames(Player player, Integer[][] entityIdMatrix);
|
||||||
|
@ -95,7 +95,7 @@ public interface MultiMapController extends IMapController {
|
||||||
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
||||||
*
|
*
|
||||||
* @param player {@link Player} that will be able to see the maps
|
* @param player {@link Player} that will be able to see the maps
|
||||||
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[width][height]</code>)
|
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[rows][columns]</code>)
|
||||||
* @param callable {@link DebugCallable} which will be called to display debug information, or <code>null</code>
|
* @param callable {@link DebugCallable} which will be called to display debug information, or <code>null</code>
|
||||||
* @see MapController#showInFrame(Player, int, String)
|
* @see MapController#showInFrame(Player, int, String)
|
||||||
*/
|
*/
|
||||||
|
@ -105,7 +105,7 @@ public interface MultiMapController extends IMapController {
|
||||||
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
||||||
*
|
*
|
||||||
* @param player {@link Player} that will be able to see the maps
|
* @param player {@link Player} that will be able to see the maps
|
||||||
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[width][height]</code>)
|
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[rows][columns]</code>)
|
||||||
* @param force if <code>false</code>, the map will not be shown if there is not Map-Item in the ItemFrames
|
* @param force if <code>false</code>, the map will not be shown if there is not Map-Item in the ItemFrames
|
||||||
* @see MapController#showInFrame(Player, ItemFrame, boolean)
|
* @see MapController#showInFrame(Player, ItemFrame, boolean)
|
||||||
*/
|
*/
|
||||||
|
@ -115,7 +115,7 @@ public interface MultiMapController extends IMapController {
|
||||||
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
* Show this {@link MultiMapController} in {@link ItemFrame}s
|
||||||
*
|
*
|
||||||
* @param player {@link Player} that will be able to see the maps
|
* @param player {@link Player} that will be able to see the maps
|
||||||
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[width][height]</code>)
|
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[rows][columns]</code>)
|
||||||
* @see MapController#showInFrame(Player, ItemFrame)
|
* @see MapController#showInFrame(Player, ItemFrame)
|
||||||
*/
|
*/
|
||||||
void showInFrames(Player player, ItemFrame[][] itemFrameMatrix);
|
void showInFrames(Player player, ItemFrame[][] itemFrameMatrix);
|
||||||
|
@ -124,7 +124,7 @@ public interface MultiMapController extends IMapController {
|
||||||
* Clear the frames
|
* Clear the frames
|
||||||
*
|
*
|
||||||
* @param player {@link Player} that will be able to see the cleared frames
|
* @param player {@link Player} that will be able to see the cleared frames
|
||||||
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[width][height]</code>)
|
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[rows][columns]</code>)
|
||||||
*/
|
*/
|
||||||
void clearFrames(Player player, Integer[][] entityIdMatrix);
|
void clearFrames(Player player, Integer[][] entityIdMatrix);
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ public interface MultiMapController extends IMapController {
|
||||||
* Clear the frames
|
* Clear the frames
|
||||||
*
|
*
|
||||||
* @param player {@link Player} that will be able to see the cleared frames
|
* @param player {@link Player} that will be able to see the cleared frames
|
||||||
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[width][height]</code>)
|
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[rows][columns]</code>)
|
||||||
*/
|
*/
|
||||||
void clearFrames(Player player, ItemFrame[][] itemFrameMatrix);
|
void clearFrames(Player player, ItemFrame[][] itemFrameMatrix);
|
||||||
|
|
||||||
|
@ -144,11 +144,11 @@ public interface MultiMapController extends IMapController {
|
||||||
* Called to get debug information for a frame
|
* Called to get debug information for a frame
|
||||||
*
|
*
|
||||||
* @param controller the {@link MapController}
|
* @param controller the {@link MapController}
|
||||||
* @param x X-Position of the current frame
|
* @param row Row of the current frame
|
||||||
* @param y Y-Position of the current frame
|
* @param column Column of the current frame
|
||||||
* @return {@link String} to show when a player looks at the map, or <code>null</code>
|
* @return {@link String} to show when a player looks at the map, or <code>null</code>
|
||||||
* @see MapController#showInFrame(Player, int, String)
|
* @see MapController#showInFrame(Player, int, String)
|
||||||
*/
|
*/
|
||||||
String call(MapController controller, int x, int y);
|
String call(MapController controller, int row, int column);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,52 +25,75 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static tech.sbdevelopment.mapreflectionapi.utils.MainUtil.validateArrayDimensions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MultiMapWrapper} wraps one image split in pieces.
|
* A {@link MultiMapWrapper} wraps one image split in pieces.
|
||||||
*/
|
*/
|
||||||
public class MultiMapWrapper extends AbstractMapWrapper {
|
public class MultiMapWrapper extends AbstractMapWrapper {
|
||||||
private final MapWrapper[][] wrapperMatrix;
|
private final MapWrapper[][] wrapperMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MultiMapWrapper} from the given image.
|
||||||
|
* The image will be split into the given amount of rows and columns.
|
||||||
|
*
|
||||||
|
* @param image The image to wrap
|
||||||
|
* @param rows The amount of rows
|
||||||
|
* @param columns The amount of columns
|
||||||
|
*/
|
||||||
public MultiMapWrapper(BufferedImage image, int rows, int columns) {
|
public MultiMapWrapper(BufferedImage image, int rows, int columns) {
|
||||||
this(splitImage(image, columns, rows));
|
this(splitImage(image, rows, columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MultiMapWrapper} from the given image.
|
||||||
|
* The image will be split into the given amount of rows and columns.
|
||||||
|
*
|
||||||
|
* @param image The image to wrap
|
||||||
|
* @param rows The amount of rows
|
||||||
|
* @param columns The amount of columns
|
||||||
|
*/
|
||||||
public MultiMapWrapper(ArrayImage image, int rows, int columns) {
|
public MultiMapWrapper(ArrayImage image, int rows, int columns) {
|
||||||
this(splitImage(image.toBuffered(), columns, rows));
|
this(splitImage(image.toBuffered(), rows, columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiMapWrapper(ArrayImage[][] imageMatrix) {
|
/**
|
||||||
|
* Creates a new {@link MultiMapWrapper} from the given image.
|
||||||
|
*
|
||||||
|
* @param imageMatrix The image matrix to wrap
|
||||||
|
*/
|
||||||
|
protected MultiMapWrapper(ArrayImage[][] imageMatrix) {
|
||||||
wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
|
wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
|
||||||
|
|
||||||
for (int x = 0; x < imageMatrix.length; x++) {
|
for (int row = 0; row < imageMatrix.length; row++) {
|
||||||
if (imageMatrix[x].length != imageMatrix[0].length) {
|
if (imageMatrix[row].length != imageMatrix[0].length) {
|
||||||
throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!");
|
throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < imageMatrix[x].length; y++) {
|
for (int column = 0; column < imageMatrix[row].length; column++) {
|
||||||
wrapperMatrix[x][y] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[x][y]);
|
wrapperMatrix[row][column] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[row][column]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiMapWrapper(BufferedImage[][] imageMatrix) {
|
/**
|
||||||
|
* Creates a new {@link MultiMapWrapper} from the given image.
|
||||||
|
*
|
||||||
|
* @param imageMatrix The image matrix to wrap
|
||||||
|
*/
|
||||||
|
protected MultiMapWrapper(BufferedImage[][] imageMatrix) {
|
||||||
wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
|
wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
|
||||||
|
|
||||||
for (int x = 0; x < imageMatrix.length; x++) {
|
for (int row = 0; row < imageMatrix.length; row++) {
|
||||||
if (imageMatrix[x].length != imageMatrix[0].length) {
|
if (imageMatrix[row].length != imageMatrix[0].length) {
|
||||||
throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!");
|
throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < imageMatrix[x].length; y++) {
|
for (int column = 0; column < imageMatrix[row].length; column++) {
|
||||||
wrapperMatrix[x][y] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[x][y]);
|
wrapperMatrix[row][column] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[row][column]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,10 +140,10 @@ public class MultiMapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(@NotNull ArrayImage content) {
|
public void update(@NotNull ArrayImage content) {
|
||||||
ArrayImage[][] split = splitImage(content.toBuffered(), wrapperMatrix[0].length, wrapperMatrix.length);
|
ArrayImage[][] split = splitImage(content.toBuffered(), wrapperMatrix.length, wrapperMatrix[0].length);
|
||||||
for (int x = 0; x < wrapperMatrix.length; x++) {
|
for (int row = 0; row < wrapperMatrix.length; row++) {
|
||||||
for (int y = 0; y < wrapperMatrix[x].length; y++) {
|
for (int column = 0; column < wrapperMatrix[row].length; column++) {
|
||||||
wrapperMatrix[x][y].getController().update(split[x][y]);
|
wrapperMatrix[row][column].getController().update(split[row][column]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,33 +173,27 @@ public class MultiMapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInFrames(Player player, Integer[][] entityIdMatrix) {
|
public void showInFrames(Player player, Integer[][] entityIdMatrix) {
|
||||||
validateArrayDimensions(wrapperMatrix, entityIdMatrix);
|
for (int row = 0; row < entityIdMatrix.length; row++) {
|
||||||
|
for (int column = 0; column < entityIdMatrix[row].length; column++) {
|
||||||
for (int x = 0; x < entityIdMatrix.length; x++) {
|
wrapperMatrix[row][column].getController().showInFrame(player, entityIdMatrix[row][column]);
|
||||||
for (int y = 0; y < entityIdMatrix[x].length; y++) {
|
|
||||||
wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInFrames(Player player, Integer[][] entityIdMatrix, DebugCallable callable) {
|
public void showInFrames(Player player, Integer[][] entityIdMatrix, DebugCallable callable) {
|
||||||
validateArrayDimensions(wrapperMatrix, entityIdMatrix);
|
for (int row = 0; row < entityIdMatrix.length; row++) {
|
||||||
|
for (int column = 0; column < entityIdMatrix[row].length; column++) {
|
||||||
for (int x = 0; x < entityIdMatrix.length; x++) {
|
wrapperMatrix[row][column].getController().showInFrame(player, entityIdMatrix[row][column], callable.call(wrapperMatrix[row][column].getController(), row, column));
|
||||||
for (int y = 0; y < entityIdMatrix[x].length; y++) {
|
|
||||||
wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y], callable.call(wrapperMatrix[y][x].getController(), x, y));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) {
|
public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) {
|
||||||
validateArrayDimensions(wrapperMatrix, itemFrameMatrix);
|
for (int row = 0; row < itemFrameMatrix.length; row++) {
|
||||||
|
for (int column = 0; column < itemFrameMatrix[row].length; column++) {
|
||||||
for (int x = 0; x < itemFrameMatrix.length; x++) {
|
wrapperMatrix[row][column].getController().showInFrame(player, itemFrameMatrix[row][column], force);
|
||||||
for (int y = 0; y < itemFrameMatrix[x].length; y++) {
|
|
||||||
wrapperMatrix[y][x].getController().showInFrame(player, itemFrameMatrix[x][wrapperMatrix.length - 1 - y], force);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,47 +205,47 @@ public class MultiMapWrapper extends AbstractMapWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearFrames(Player player, Integer[][] entityIdMatrix) {
|
public void clearFrames(Player player, Integer[][] entityIdMatrix) {
|
||||||
validateArrayDimensions(wrapperMatrix, entityIdMatrix);
|
for (int row = 0; row < entityIdMatrix.length; row++) {
|
||||||
|
for (int column = 0; column < entityIdMatrix[row].length; column++) {
|
||||||
for (int x = 0; x < entityIdMatrix.length; x++) {
|
wrapperMatrix[row][column].getController().clearFrame(player, entityIdMatrix[row][column]);
|
||||||
for (int y = 0; y < entityIdMatrix[x].length; y++) {
|
|
||||||
wrapperMatrix[y][x].getController().clearFrame(player, entityIdMatrix[x][y]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearFrames(Player player, ItemFrame[][] itemFrameMatrix) {
|
public void clearFrames(Player player, ItemFrame[][] itemFrameMatrix) {
|
||||||
validateArrayDimensions(wrapperMatrix, itemFrameMatrix);
|
for (int row = 0; row < itemFrameMatrix.length; row++) {
|
||||||
|
for (int column = 0; column < itemFrameMatrix[row].length; column++) {
|
||||||
for (int x = 0; x < itemFrameMatrix.length; x++) {
|
wrapperMatrix[row][column].getController().clearFrame(player, itemFrameMatrix[row][column]);
|
||||||
for (int y = 0; y < itemFrameMatrix[x].length; y++) {
|
|
||||||
wrapperMatrix[y][x].getController().clearFrame(player, itemFrameMatrix[x][y]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Modified Method from http://kalanir.blogspot.de/2010/02/how-to-split-image-into-chunks-java.html
|
* Splits a BufferedImage into a matrix of ArrayImages.
|
||||||
|
*
|
||||||
|
* @param image The image to split
|
||||||
|
* @param rows The number of rows
|
||||||
|
* @param columns The number of columns
|
||||||
|
* @return The matrix of ArrayImages
|
||||||
*/
|
*/
|
||||||
private static ArrayImage[][] splitImage(final BufferedImage image, final int columns, final int rows) {
|
private static ArrayImage[][] splitImage(final BufferedImage image, final int rows, final int columns) {
|
||||||
int chunkWidth = image.getWidth() / columns;
|
int chunkWidth = image.getWidth() / columns;
|
||||||
int chunkHeight = image.getHeight() / rows;
|
int chunkHeight = image.getHeight() / rows;
|
||||||
|
|
||||||
ArrayImage[][] images = new ArrayImage[rows][columns];
|
ArrayImage[][] images = new ArrayImage[rows][columns];
|
||||||
for (int x = 0; x < rows; x++) {
|
|
||||||
for (int y = 0; y < columns; y++) {
|
|
||||||
BufferedImage raw = new BufferedImage(chunkWidth, chunkHeight, image.getType());
|
|
||||||
|
|
||||||
Graphics2D gr = raw.createGraphics();
|
for (int i = 0; i < rows; i++) {
|
||||||
gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null);
|
for (int j = 0; j < columns; j++) {
|
||||||
gr.dispose();
|
int x = j * chunkWidth;
|
||||||
|
int y = i * chunkHeight;
|
||||||
|
|
||||||
images[x][y] = new ArrayImage(raw);
|
BufferedImage raw = image.getSubimage(x, y, chunkWidth, chunkHeight);
|
||||||
raw.flush();
|
images[i][j] = new ArrayImage(raw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return images;
|
return images;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of MapReflectionAPI.
|
* This file is part of MapReflectionAPI.
|
||||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,53 +20,40 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||||
|
import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
|
||||||
|
import tech.sbdevelopment.mapreflectionapi.utils.XMaterial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when a map in the creative inventory gets updated
|
* This event gets fired when a map in the creative inventory gets updated
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public class CreateInventoryMapUpdateEvent extends Event implements Cancellable {
|
public class CreativeInventoryMapUpdateEvent extends CancellableEvent {
|
||||||
private static final HandlerList handlerList = new HandlerList();
|
|
||||||
@Setter
|
|
||||||
private boolean cancelled;
|
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final int slot;
|
private final int slot;
|
||||||
private final ItemStack item;
|
private final ItemStack item;
|
||||||
private MapWrapper mapWrapper;
|
private MapWrapper mapWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new {@link CreateInventoryMapUpdateEvent}
|
* Construct a new {@link CreativeInventoryMapUpdateEvent}
|
||||||
*
|
*
|
||||||
* @param player The player whose inventory is updated
|
* @param player The player whose inventory is updated
|
||||||
* @param slot The new slot
|
* @param slot The new slot
|
||||||
* @param item The item in the new slot
|
* @param item The item in the new slot
|
||||||
* @param isAsync Is this event called async?
|
* @param isAsync Is this event called async?
|
||||||
*/
|
*/
|
||||||
public CreateInventoryMapUpdateEvent(Player player, int slot, ItemStack item, boolean isAsync) {
|
public CreativeInventoryMapUpdateEvent(Player player, int slot, ItemStack item, boolean isAsync) {
|
||||||
super(isAsync);
|
super(isAsync);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.slot = slot;
|
this.slot = slot;
|
||||||
this.item = item;
|
this.item = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull HandlerList getHandlers() {
|
|
||||||
return handlerList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link MapWrapper} of the map of this event
|
* Get the {@link MapWrapper} of the map of this event
|
||||||
*
|
*
|
||||||
|
@ -76,7 +63,7 @@ public class CreateInventoryMapUpdateEvent extends Event implements Cancellable
|
||||||
public MapWrapper getMapWrapper() {
|
public MapWrapper getMapWrapper() {
|
||||||
if (mapWrapper == null) {
|
if (mapWrapper == null) {
|
||||||
if (item == null) return null;
|
if (item == null) return null;
|
||||||
if (item.getType() != Material.MAP) return null;
|
if (!XMaterial.FILLED_MAP.isSimilar(item)) return null;
|
||||||
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, item.getDurability());
|
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, item.getDurability());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of MapReflectionAPI.
|
* This file is part of MapReflectionAPI.
|
||||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,23 +20,15 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when a map creation is cancelled
|
* This event gets fired when a map creation is cancelled
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public class MapCancelEvent extends Event implements Cancellable {
|
public class MapCancelEvent extends CancellableEvent {
|
||||||
private static final HandlerList handlerList = new HandlerList();
|
|
||||||
@Setter
|
|
||||||
private boolean cancelled;
|
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|
||||||
|
@ -52,9 +44,4 @@ public class MapCancelEvent extends Event implements Cancellable {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull HandlerList getHandlers() {
|
|
||||||
return handlerList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of MapReflectionAPI.
|
* This file is part of MapReflectionAPI.
|
||||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,11 +21,9 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
|
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||||
|
import tech.sbdevelopment.mapreflectionapi.api.events.types.Event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when the content of a {@link MapWrapper} is updated
|
* This event gets fired when the content of a {@link MapWrapper} is updated
|
||||||
|
@ -33,8 +31,6 @@ import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public class MapContentUpdateEvent extends Event {
|
public class MapContentUpdateEvent extends Event {
|
||||||
private static final HandlerList handlerList = new HandlerList();
|
|
||||||
|
|
||||||
private final MapWrapper wrapper;
|
private final MapWrapper wrapper;
|
||||||
private final ArrayImage content;
|
private final ArrayImage content;
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -52,9 +48,4 @@ public class MapContentUpdateEvent extends Event {
|
||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull HandlerList getHandlers() {
|
|
||||||
return handlerList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of MapReflectionAPI.
|
* This file is part of MapReflectionAPI.
|
||||||
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
|
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,28 +20,20 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.entity.ItemFrame;
|
import org.bukkit.entity.ItemFrame;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
|
||||||
|
import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when a player interact with a map
|
* This event gets fired when a player interact with a map
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public class MapInteractEvent extends Event implements Cancellable {
|
public class MapInteractEvent extends CancellableEvent {
|
||||||
private static final HandlerList handlerList = new HandlerList();
|
|
||||||
@Setter
|
|
||||||
private boolean cancelled;
|
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final int entityID;
|
private final int entityID;
|
||||||
private final int action;
|
private final int action;
|
||||||
|
@ -69,11 +61,6 @@ public class MapInteractEvent extends Event implements Cancellable {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull HandlerList getHandlers() {
|
|
||||||
return handlerList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link ItemFrame} the map is in
|
* Get the {@link ItemFrame} the map is in
|
||||||
*
|
*
|
||||||
|
@ -81,10 +68,8 @@ public class MapInteractEvent extends Event implements Cancellable {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public ItemFrame getFrame() {
|
public ItemFrame getFrame() {
|
||||||
if (getMapWrapper() == null) return null;
|
|
||||||
|
|
||||||
if (frame == null) {
|
if (frame == null) {
|
||||||
frame = getMapWrapper().getController().getItemFrameById(player.getWorld(), entityID);
|
frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityID);
|
||||||
}
|
}
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
@ -96,10 +81,11 @@ public class MapInteractEvent extends Event implements Cancellable {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public MapWrapper getMapWrapper() {
|
public MapWrapper getMapWrapper() {
|
||||||
|
if (getFrame() == null) return null;
|
||||||
if (mapWrapper == null) {
|
if (mapWrapper == null) {
|
||||||
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, entityID);
|
if (!frame.hasMetadata(MapWrapper.REFERENCE_METADATA)) return null;
|
||||||
|
mapWrapper = (MapWrapper) frame.getMetadata(MapWrapper.REFERENCE_METADATA).get(0).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapWrapper;
|
return mapWrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* This file is part of MapReflectionAPI.
|
||||||
|
* Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tech.sbdevelopment.mapreflectionapi.api.events.types;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CancellableEvent extends Event implements Cancellable {
|
||||||
|
/**
|
||||||
|
* If this event gets cancelled.
|
||||||
|
*/
|
||||||
|
private boolean cancelled;
|
||||||
|
|
||||||
|
public CancellableEvent(boolean isAsync) {
|
||||||
|
super(isAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this event gets cancelled.
|
||||||
|
*
|
||||||
|
* @return true if cancelled, false if not
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if this event gets cancelled.
|
||||||
|
*
|
||||||
|
* @param cancelled true if you wish to cancel this event
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancelled) {
|
||||||
|
this.cancelled = cancelled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* This file is part of MapReflectionAPI.
|
||||||
|
* Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tech.sbdevelopment.mapreflectionapi.api.events.types;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Event extends org.bukkit.event.Event {
|
||||||
|
public Event(boolean isAsync) {
|
||||||
|
super(isAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of EventHandlers listening to this event.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private static final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the EventHandlers listening to this event.
|
||||||
|
*
|
||||||
|
* @return The EventHandlers listening to this event.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HandlerList getHandlers() {
|
||||||
|
return handlerList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,14 +31,16 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
|
import tech.sbdevelopment.mapreflectionapi.api.events.CreativeInventoryMapUpdateEvent;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
|
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
|
||||||
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
|
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
|
||||||
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getConnection;
|
||||||
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
|
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
|
||||||
|
import static com.cryptomorin.xseries.reflection.XReflection.*;
|
||||||
|
|
||||||
public class PacketListener implements Listener {
|
public class PacketListener implements Listener {
|
||||||
private static final Class<?> packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap");
|
private static final Class<?> packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap");
|
||||||
|
@ -46,6 +48,16 @@ public class PacketListener implements Listener {
|
||||||
private static final Class<?> packetPlayInSetCreativeSlotClass = getNMSClass("network.protocol.game", "PacketPlayInSetCreativeSlot");
|
private static final Class<?> packetPlayInSetCreativeSlotClass = getNMSClass("network.protocol.game", "PacketPlayInSetCreativeSlot");
|
||||||
private static final Class<?> vec3DClass = getNMSClass("world.phys", "Vec3D");
|
private static final Class<?> vec3DClass = getNMSClass("world.phys", "Vec3D");
|
||||||
private static final Class<?> craftStackClass = getCraftClass("inventory.CraftItemStack");
|
private static final Class<?> craftStackClass = getCraftClass("inventory.CraftItemStack");
|
||||||
|
private static final Class<?> playerCommonConnection;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (supports(20, 2)) {
|
||||||
|
// The packet send method has been abstracted from ServerGamePacketListenerImpl to ServerCommonPacketListenerImpl in 1.20.2
|
||||||
|
playerCommonConnection = getNMSClass("server.network", "ServerCommonPacketListenerImpl");
|
||||||
|
} else {
|
||||||
|
playerCommonConnection = getNMSClass("server.network", "PlayerConnection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onJoin(PlayerJoinEvent e) {
|
public void onJoin(PlayerJoinEvent e) {
|
||||||
|
@ -61,46 +73,83 @@ public class PacketListener implements Listener {
|
||||||
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
|
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
|
||||||
|
boolean cancel = false;
|
||||||
|
|
||||||
if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) {
|
if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) {
|
||||||
Object packetPlayOutMap = packetPlayOutMapClass.cast(packet);
|
Object packetPlayOutMap = packetPlayOutMapClass.cast(packet);
|
||||||
|
|
||||||
int id = (int) getDeclaredField(packetPlayOutMap, "a");
|
int id;
|
||||||
if (id < 0) {
|
boolean inv = false;
|
||||||
int newId = -id;
|
if (supports(20, 4)) { //1.20.4 uses MapId class and record classes (final fields...)
|
||||||
setDeclaredField(packetPlayOutMap, "a", newId);
|
Object mapId = getDeclaredField(packetPlayOutMap, "b");
|
||||||
|
id = (int) getDeclaredField(mapId, "c");
|
||||||
|
|
||||||
|
if (id < 0) {
|
||||||
|
Object newMapid = callConstructor(mapId.getClass(), -id);
|
||||||
|
Object c = getDeclaredField(packetPlayOutMap, "c");
|
||||||
|
Object d = getDeclaredField(packetPlayOutMap, "d");
|
||||||
|
Object e = getDeclaredField(packetPlayOutMap, "e");
|
||||||
|
Object f = getDeclaredField(packetPlayOutMap, "f");
|
||||||
|
|
||||||
|
packetPlayOutMap = callConstructor(packetPlayOutMapClass, newMapid, c, d, e, f);
|
||||||
|
packet = packetPlayOutMap;
|
||||||
|
|
||||||
|
inv = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
id = (int) getDeclaredField(packetPlayOutMap, "a");
|
||||||
|
|
||||||
|
if (id < 0) {
|
||||||
|
setDeclaredField(packetPlayOutMap, "a", -id);
|
||||||
|
inv = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inv) {
|
||||||
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
|
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
|
||||||
MapCancelEvent event = new MapCancelEvent(player, id, async);
|
MapCancelEvent event = new MapCancelEvent(player, id, async);
|
||||||
if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true);
|
if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true);
|
||||||
if (event.getHandlers().getRegisteredListeners().length > 0)
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
|
||||||
|
|
||||||
if (event.isCancelled()) return;
|
if (event.isCancelled()) cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.write(ctx, packet, promise);
|
if (!cancel) super.write(ctx, packet, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
|
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
|
||||||
|
boolean cancel = false;
|
||||||
|
|
||||||
if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) {
|
if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) {
|
||||||
Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet);
|
Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet);
|
||||||
|
|
||||||
int entityId = (int) getDeclaredField(packetPlayInEntity, "a");
|
int entityId = (int) getDeclaredField(packetPlayInEntity, supports(20, 4) ? "b" : "a");
|
||||||
|
|
||||||
Enum<?> actionEnum;
|
Enum<?> actionEnum;
|
||||||
Enum<?> hand;
|
Enum<?> hand;
|
||||||
Object pos;
|
Object pos;
|
||||||
if (ReflectionUtil.supports(17)) {
|
if (supports(17)) {
|
||||||
Object action = getDeclaredField(packetPlayInEntity, "b");
|
Object action = getDeclaredField(packetPlayInEntity, supports(20, 4) ? "c" : "b");
|
||||||
actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type
|
actionEnum = (Enum<?>) callDeclaredMethod(action, "a");
|
||||||
hand = hasField(action, "a") ? (Enum<?>) getDeclaredField(action, "a") : null;
|
Class<?> d = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$d");
|
||||||
pos = hasField(action, "b") ? getDeclaredField(action, "b") : null;
|
Class<?> e = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$e");
|
||||||
|
if (action.getClass().isAssignableFrom(e)) {
|
||||||
|
hand = (Enum<?>) getDeclaredField(action, "a");
|
||||||
|
pos = getDeclaredField(action, "b");
|
||||||
|
} else {
|
||||||
|
pos = null;
|
||||||
|
if (action.getClass().isAssignableFrom(d)) {
|
||||||
|
hand = (Enum<?>) getDeclaredField(action, "a");
|
||||||
|
} else {
|
||||||
|
hand = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
actionEnum = (Enum<?>) callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "b" : "a"); //1.13 = b, 1.12 = a
|
actionEnum = (Enum<?>) callDeclaredMethod(packetPlayInEntity, supports(13) ? "b" : "a"); //1.13 = b, 1.12 = a
|
||||||
hand = (Enum<?>) callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "c" : "b"); //1.13 = c, 1.12 = b
|
hand = (Enum<?>) callDeclaredMethod(packetPlayInEntity, supports(13) ? "c" : "b"); //1.13 = c, 1.12 = b
|
||||||
pos = callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "d" : "c"); //1.13 = d, 1.12 = c
|
pos = callDeclaredMethod(packetPlayInEntity, supports(13) ? "d" : "c"); //1.13 = d, 1.12 = c
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Bukkit.getScheduler().callSyncMethod(MapReflectionAPI.getInstance(), () -> {
|
if (Bukkit.getScheduler().callSyncMethod(MapReflectionAPI.getInstance(), () -> {
|
||||||
|
@ -111,23 +160,28 @@ public class PacketListener implements Listener {
|
||||||
return event.isCancelled();
|
return event.isCancelled();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}).get(1, TimeUnit.SECONDS)) return;
|
}).get(1, TimeUnit.SECONDS)) cancel = true;
|
||||||
} else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) {
|
} else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) {
|
||||||
Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet);
|
Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet);
|
||||||
|
|
||||||
int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, ReflectionUtil.supports(19, 3) ? "a" : ReflectionUtil.supports(13) ? "b" : "a"); //1.19.4 = a, 1.19.3 - 1.13 = b, 1.12 = a
|
int slot;
|
||||||
Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, ReflectionUtil.supports(18) ? "c" : "getItemStack"); //1.18 = c, 1.17 = getItemStack
|
if (supports(20, 4)) { //1.20.4+ uses short
|
||||||
|
slot = (short) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, "b");
|
||||||
|
} else { //1.20.3 and lower uses int
|
||||||
|
slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a
|
||||||
|
}
|
||||||
|
Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.5 = e, 1.20.2-1.20.4 = d, >= 1.18 = c, 1.17 = getItemStack
|
||||||
ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack);
|
ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack);
|
||||||
|
|
||||||
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
|
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
|
||||||
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(player, slot, craftStack, async);
|
CreativeInventoryMapUpdateEvent event = new CreativeInventoryMapUpdateEvent(player, slot, craftStack, async);
|
||||||
if (event.getMapWrapper() != null) {
|
if (event.getMapWrapper() != null) {
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
if (event.isCancelled()) return;
|
if (event.isCancelled()) cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.channelRead(ctx, packet);
|
if (!cancel) super.channelRead(ctx, packet);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,19 +195,17 @@ public class PacketListener implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Channel getChannel(Player player) {
|
private Channel getChannel(Player player) {
|
||||||
Object playerHandle = getHandle(player);
|
Object networkManager = getDeclaredField(playerCommonConnection, getConnection(player), supports(21) ? "e" : supports(20, 2) ? "c" : supports(19, 4) ? "h" : supports(19) ? "b" : supports(17) ? "a" : "networkManager"); //1.20.2 = ServerCommonPacketListenerImpl#c, 1.20(.1) & 1.19.4 = h, >= 1.19.3 = b, 1.18 - 1.17 = a, 1.16 = networkManager
|
||||||
Object playerConnection = getDeclaredField(playerHandle, ReflectionUtil.supports(20) ? "c" : ReflectionUtil.supports(17) ? "b" : "playerConnection"); //1.20 = c, 1.17-1.19 = b, 1.16 = playerConnection
|
return (Channel) getDeclaredField(networkManager, supports(20, 2) ? "n" : supports(18) ? "m" : supports(17) ? "k" : "channel"); //1.20.2 = n, 1.20(.1), 1.19 & 1.18 = m, 1.17 = k, 1.16 = channel
|
||||||
Object networkManager = getDeclaredField(playerConnection, ReflectionUtil.supports(19, 3) ? "h" : ReflectionUtil.supports(19) ? "b" : ReflectionUtil.supports(17) ? "a" : "networkManager"); //1.19.4 = h, >= 1.19.3 = b, 1.18 = a, 1.16 = networkManager
|
|
||||||
return (Channel) getDeclaredField(networkManager, ReflectionUtil.supports(18) ? "m" : ReflectionUtil.supports(17) ? "k" : "channel"); //1.19 & 1.18 = m, 1.17 = k, 1.16 = channel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector vec3DToVector(Object vec3d) {
|
private Vector vec3DToVector(Object vec3d) {
|
||||||
if (!(vec3d.getClass().isAssignableFrom(vec3DClass))) return new Vector(0, 0, 0);
|
if (!(vec3d.getClass().isAssignableFrom(vec3DClass))) return new Vector(0, 0, 0);
|
||||||
|
|
||||||
Object vec3dNMS = vec3DClass.cast(vec3d);
|
Object vec3dNMS = vec3DClass.cast(vec3d);
|
||||||
double x = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "c" : ReflectionUtil.supports(17) ? "b" : "x"); //1.19 = c, 1.18 = b, 1.16 = x
|
double x = (double) getDeclaredField(vec3dNMS, supports(21, 2) ? "d" : supports(19) ? "c" : supports(17) ? "b" : "x"); //1.21.2+ = d, 1.19 = c, 1.18 = b, 1.16 = x
|
||||||
double y = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "d" : ReflectionUtil.supports(17) ? "c" : "y"); //1.19 = d, 1.18 = c, 1.16 = y
|
double y = (double) getDeclaredField(vec3dNMS, supports(21, 2) ? "e" : supports(19) ? "d" : supports(17) ? "c" : "y"); //1.21.2+ = e, 1.19 = d, 1.18 = c, 1.16 = y
|
||||||
double z = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "e" : ReflectionUtil.supports(17) ? "d" : "z"); //1.19 = e, 1.18 = d, 1.16 = z
|
double z = (double) getDeclaredField(vec3dNMS, supports(21, 2) ? "f" : supports(19) ? "e" : supports(17) ? "d" : "z"); //1.21.2+ = f, 1.19 = e, 1.18 = d, 1.16 = z
|
||||||
|
|
||||||
return new Vector(x, y, z);
|
return new Vector(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
|
|
||||||
package tech.sbdevelopment.mapreflectionapi.utils;
|
package tech.sbdevelopment.mapreflectionapi.utils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class MainUtil {
|
public class MainUtil {
|
||||||
private MainUtil() {
|
private MainUtil() {
|
||||||
}
|
}
|
||||||
|
@ -35,10 +38,4 @@ public class MainUtil {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <A, B> void validateArrayDimensions(A[][] arrayOne, B[][] arrayTwo) {
|
|
||||||
if (arrayOne.length != arrayTwo.length || arrayOne[0].length != arrayTwo[0].length) {
|
|
||||||
throw new IllegalArgumentException("The dimensions of two provided arrays (" + arrayOne.getClass().getName() + ", " + arrayTwo.getClass().getName() + ") do not match!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,186 +19,23 @@
|
||||||
package tech.sbdevelopment.mapreflectionapi.utils;
|
package tech.sbdevelopment.mapreflectionapi.utils;
|
||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
/**
|
import static com.cryptomorin.xseries.reflection.XReflection.getCraftClass;
|
||||||
* <b>ReflectionUtil</b> - Reflection handler for NMS and CraftBukkit.<br>
|
import static com.cryptomorin.xseries.reflection.XReflection.getNMSClass;
|
||||||
* Caches the packet related methods and is asynchronous.
|
|
||||||
* <p>
|
|
||||||
* This class does not handle null checks as most of the requests are from the
|
|
||||||
* other utility classes that already handle null checks.
|
|
||||||
* <p>
|
|
||||||
* <a href="https://wiki.vg/Protocol">Clientbound Packets</a> are considered fake
|
|
||||||
* updates to the client without changing the actual data. Since all the data is handled
|
|
||||||
* by the server.
|
|
||||||
*
|
|
||||||
* @author Crypto Morin, Stijn Bannink
|
|
||||||
* @version 2.1
|
|
||||||
*/
|
|
||||||
public class ReflectionUtil {
|
public class ReflectionUtil {
|
||||||
/**
|
private static final Map<String, Constructor<?>> constructorCache = new HashMap<>();
|
||||||
* We use reflection mainly to avoid writing a new class for version barrier.
|
private static final Map<String, Method> methodCache = new HashMap<>();
|
||||||
* The version barrier is for NMS that uses the Minecraft version as the main package name.
|
private static final Map<String, Field> fieldCache = new HashMap<>();
|
||||||
* <p>
|
private static final Class<?> craftWorld = getCraftClass("CraftWorld");
|
||||||
* E.g. EntityPlayer in 1.15 is in the class {@code net.minecraft.server.v1_15_R1}
|
|
||||||
* but in 1.14 it's in {@code net.minecraft.server.v1_14_R1}
|
|
||||||
* In order to maintain cross-version compatibility we cannot import these classes.
|
|
||||||
* <p>
|
|
||||||
* Performance is not a concern for these specific statically initialized values.
|
|
||||||
*/
|
|
||||||
public static final String VERSION;
|
|
||||||
|
|
||||||
static { // This needs to be right below VERSION because of initialization order.
|
|
||||||
// This package loop is used to avoid implementation-dependant strings like Bukkit.getVersion() or Bukkit.getBukkitVersion()
|
|
||||||
// which allows easier testing as well.
|
|
||||||
String found = null;
|
|
||||||
for (Package pack : Package.getPackages()) {
|
|
||||||
String name = pack.getName();
|
|
||||||
|
|
||||||
// .v because there are other packages.
|
|
||||||
if (name.startsWith("org.bukkit.craftbukkit.v")) {
|
|
||||||
found = pack.getName().split("\\.")[3];
|
|
||||||
|
|
||||||
// Just a final guard to make sure it finds this important class.
|
|
||||||
// As a protection for forge+bukkit implementation that tend to mix versions.
|
|
||||||
// The real CraftPlayer should exist in the package.
|
|
||||||
// Note: Doesn't seem to function properly. Will need to separate the version
|
|
||||||
// handler for NMS and CraftBukkit for softwares like catmc.
|
|
||||||
try {
|
|
||||||
Class.forName("org.bukkit.craftbukkit." + found + ".entity.CraftPlayer");
|
|
||||||
break;
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
found = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found == null)
|
|
||||||
throw new IllegalArgumentException("Failed to parse server version. Could not find any package starting with name: 'org.bukkit.craftbukkit.v'");
|
|
||||||
VERSION = found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The raw minor version number.
|
|
||||||
* E.g. {@code v1_17_R1} to {@code 17}
|
|
||||||
*
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
public static final int VER = Integer.parseInt(VERSION.substring(1).split("_")[1]);
|
|
||||||
/**
|
|
||||||
* The raw minor version number.
|
|
||||||
* E.g. {@code v1_18_R2} to {@code 2}
|
|
||||||
*
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
public static final int VER_MINOR = toInt(VERSION.substring(1).split("_")[2].substring(1), 0);
|
|
||||||
/**
|
|
||||||
* Mojang remapped their NMS in 1.17 https://www.spigotmc.org/threads/spigot-bungeecord-1-17.510208/#post-4184317
|
|
||||||
*/
|
|
||||||
public static final String
|
|
||||||
CRAFTBUKKIT = "org.bukkit.craftbukkit." + VERSION + '.',
|
|
||||||
NMS = v(17, "net.minecraft.").orElse("net.minecraft.server." + VERSION + '.');
|
|
||||||
/**
|
|
||||||
* A nullable public accessible field only available in {@code EntityPlayer}.
|
|
||||||
* This can be null if the player is offline.
|
|
||||||
*/
|
|
||||||
private static final MethodHandle PLAYER_CONNECTION;
|
|
||||||
/**
|
|
||||||
* Responsible for getting the NMS handler {@code EntityPlayer} object for the player.
|
|
||||||
* {@code CraftPlayer} is simply a wrapper for {@code EntityPlayer}.
|
|
||||||
* Used mainly for handling packet related operations.
|
|
||||||
* <p>
|
|
||||||
* This is also where the famous player {@code ping} field comes from!
|
|
||||||
*/
|
|
||||||
private static final MethodHandle GET_HANDLE;
|
|
||||||
private static final MethodHandle GET_HANDLE_WORLD;
|
|
||||||
/**
|
|
||||||
* Sends a packet to the player's client through a {@code NetworkManager} which
|
|
||||||
* is where {@code ProtocolLib} controls packets by injecting channels!
|
|
||||||
*/
|
|
||||||
private static final MethodHandle SEND_PACKET;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Class<?> entityPlayer = getNMSClass("server.level", "EntityPlayer");
|
|
||||||
Class<?> worldServer = getNMSClass("server.level", "WorldServer");
|
|
||||||
Class<?> craftPlayer = getCraftClass("entity.CraftPlayer");
|
|
||||||
Class<?> craftWorld = getCraftClass("CraftWorld");
|
|
||||||
Class<?> playerConnection = getNMSClass("server.network", "PlayerConnection");
|
|
||||||
|
|
||||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
||||||
MethodHandle sendPacket = null;
|
|
||||||
MethodHandle getHandle = null;
|
|
||||||
MethodHandle getHandleWorld = null;
|
|
||||||
MethodHandle connection = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
connection = lookup.findGetter(entityPlayer,
|
|
||||||
supports(20) ? "c" : supports(17) ? "b" : "playerConnection", playerConnection);
|
|
||||||
getHandle = lookup.findVirtual(craftPlayer, "getHandle", MethodType.methodType(entityPlayer));
|
|
||||||
getHandleWorld = lookup.findVirtual(craftWorld, "getHandle", MethodType.methodType(worldServer));
|
|
||||||
sendPacket = lookup.findVirtual(playerConnection,
|
|
||||||
v(18, "a").orElse("sendPacket"),
|
|
||||||
MethodType.methodType(void.class, getNMSClass("network.protocol", "Packet")));
|
|
||||||
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
PLAYER_CONNECTION = connection;
|
|
||||||
SEND_PACKET = sendPacket;
|
|
||||||
GET_HANDLE = getHandle;
|
|
||||||
GET_HANDLE_WORLD = getHandleWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReflectionUtil() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is purely for readability.
|
|
||||||
* No performance is gained.
|
|
||||||
*
|
|
||||||
* @since 5.0.0
|
|
||||||
*/
|
|
||||||
public static <T> VersionHandler<T> v(int version, T handle) {
|
|
||||||
return new VersionHandler<>(version, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the server version is equal or greater than the given version.
|
|
||||||
*
|
|
||||||
* @param version the version to compare the server version with.
|
|
||||||
* @return true if the version is equal or newer, otherwise false.
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
public static boolean supports(int version) {
|
|
||||||
return VER >= version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the server version is equal or greater than the given version.
|
|
||||||
* <p>
|
|
||||||
* PAY ATTENTION! The minor version is based on the NMS version.
|
|
||||||
* This means that v1_19_R3 has major version 19 and minor version 3.
|
|
||||||
*
|
|
||||||
* @param major the major version to compare the server version with.
|
|
||||||
* @param minor the minor version to compare the server version with.
|
|
||||||
* @return true if the version is equal or newer, otherwise false.
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
public static boolean supports(int major, int minor) {
|
|
||||||
return VER >= major && VER_MINOR >= minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class converted to {@link List}
|
* Helper class converted to {@link List}
|
||||||
|
@ -206,7 +43,6 @@ public class ReflectionUtil {
|
||||||
* @param <E> The storage type
|
* @param <E> The storage type
|
||||||
*/
|
*/
|
||||||
public static class ListParam<E> extends ArrayList<E> {
|
public static class ListParam<E> extends ArrayList<E> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,7 +51,6 @@ public class ReflectionUtil {
|
||||||
* @param <E> The storage type
|
* @param <E> The storage type
|
||||||
*/
|
*/
|
||||||
public static class CollectionParam<E> extends ArrayList<E> {
|
public static class CollectionParam<E> extends ArrayList<E> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> wrapperToPrimitive(Class<?> clazz) {
|
private static Class<?> wrapperToPrimitive(Class<?> clazz) {
|
||||||
|
@ -241,6 +76,11 @@ public class ReflectionUtil {
|
||||||
.toArray(Class<?>[]::new);
|
.toArray(Class<?>[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Object getHandle(@NotNull World world) {;
|
||||||
|
return callDeclaredMethod(craftWorld, world, "getHandle");
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Class<?> getClass(@NotNull String name) {
|
public static Class<?> getClass(@NotNull String name) {
|
||||||
try {
|
try {
|
||||||
|
@ -254,9 +94,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callConstructorNull(Class<?> clazz, Class<?> paramClass) {
|
public static Object callConstructorNull(Class<?> clazz, Class<?> paramClass) {
|
||||||
try {
|
try {
|
||||||
Constructor<?> con = clazz.getConstructor(paramClass);
|
String cacheKey = "ConstructorNull:" + clazz.getName() + ":" + paramClass.getName();
|
||||||
con.setAccessible(true);
|
|
||||||
return con.newInstance(clazz.cast(null));
|
if (constructorCache.containsKey(cacheKey)) {
|
||||||
|
Constructor<?> cachedConstructor = constructorCache.get(cacheKey);
|
||||||
|
return cachedConstructor.newInstance(clazz.cast(null));
|
||||||
|
} else {
|
||||||
|
Constructor<?> con = clazz.getConstructor(paramClass);
|
||||||
|
con.setAccessible(true);
|
||||||
|
constructorCache.put(cacheKey, con);
|
||||||
|
return con.newInstance(clazz.cast(null));
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||||
InvocationTargetException ex) {
|
InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
@ -267,9 +115,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callFirstConstructor(Class<?> clazz, Object... params) {
|
public static Object callFirstConstructor(Class<?> clazz, Object... params) {
|
||||||
try {
|
try {
|
||||||
Constructor<?> con = clazz.getConstructors()[0];
|
String cacheKey = "FirstConstructor:" + clazz.getName();
|
||||||
con.setAccessible(true);
|
|
||||||
return con.newInstance(params);
|
if (constructorCache.containsKey(cacheKey)) {
|
||||||
|
Constructor<?> cachedConstructor = constructorCache.get(cacheKey);
|
||||||
|
return cachedConstructor.newInstance(params);
|
||||||
|
} else {
|
||||||
|
Constructor<?> con = clazz.getConstructors()[0];
|
||||||
|
con.setAccessible(true);
|
||||||
|
constructorCache.put(cacheKey, con);
|
||||||
|
return con.newInstance(params);
|
||||||
|
}
|
||||||
} catch (IllegalAccessException | InstantiationException |
|
} catch (IllegalAccessException | InstantiationException |
|
||||||
InvocationTargetException ex) {
|
InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
@ -280,9 +136,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callConstructor(Class<?> clazz, Object... params) {
|
public static Object callConstructor(Class<?> clazz, Object... params) {
|
||||||
try {
|
try {
|
||||||
Constructor<?> con = clazz.getConstructor(toParamTypes(params));
|
String cacheKey = "Constructor:" + clazz.getName() + ":" + Arrays.hashCode(params);
|
||||||
con.setAccessible(true);
|
|
||||||
return con.newInstance(params);
|
if (constructorCache.containsKey(cacheKey)) {
|
||||||
|
Constructor<?> cachedConstructor = constructorCache.get(cacheKey);
|
||||||
|
return cachedConstructor.newInstance(params);
|
||||||
|
} else {
|
||||||
|
Constructor<?> con = clazz.getConstructor(toParamTypes(params));
|
||||||
|
con.setAccessible(true);
|
||||||
|
constructorCache.put(cacheKey, con);
|
||||||
|
return con.newInstance(params);
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||||
InvocationTargetException ex) {
|
InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
@ -293,9 +157,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callDeclaredConstructor(Class<?> clazz, Object... params) {
|
public static Object callDeclaredConstructor(Class<?> clazz, Object... params) {
|
||||||
try {
|
try {
|
||||||
Constructor<?> con = clazz.getDeclaredConstructor(toParamTypes(params));
|
String cacheKey = "DeclaredConstructor:" + clazz.getName() + ":" + Arrays.hashCode(params);
|
||||||
con.setAccessible(true);
|
|
||||||
return con.newInstance(params);
|
if (constructorCache.containsKey(cacheKey)) {
|
||||||
|
Constructor<?> cachedConstructor = constructorCache.get(cacheKey);
|
||||||
|
return cachedConstructor.newInstance(params);
|
||||||
|
} else {
|
||||||
|
Constructor<?> con = clazz.getDeclaredConstructor(toParamTypes(params));
|
||||||
|
con.setAccessible(true);
|
||||||
|
constructorCache.put(cacheKey, con);
|
||||||
|
return con.newInstance(params);
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
|
||||||
InvocationTargetException ex) {
|
InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
@ -306,9 +178,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callMethod(Class<?> clazz, String method, Object... params) {
|
public static Object callMethod(Class<?> clazz, String method, Object... params) {
|
||||||
try {
|
try {
|
||||||
Method m = clazz.getMethod(method, toParamTypes(params));
|
String cacheKey = "Method:" + clazz.getName() + ":" + method + ":" + Arrays.hashCode(params);
|
||||||
m.setAccessible(true);
|
|
||||||
return m.invoke(null, params);
|
if (methodCache.containsKey(cacheKey)) {
|
||||||
|
Method cachedMethod = methodCache.get(cacheKey);
|
||||||
|
return cachedMethod.invoke(null, params);
|
||||||
|
} else {
|
||||||
|
Method m = clazz.getMethod(method, toParamTypes(params));
|
||||||
|
m.setAccessible(true);
|
||||||
|
methodCache.put(cacheKey, m);
|
||||||
|
return m.invoke(null, params);
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
|
@ -318,9 +198,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callMethod(Object obj, String method, Object... params) {
|
public static Object callMethod(Object obj, String method, Object... params) {
|
||||||
try {
|
try {
|
||||||
Method m = obj.getClass().getMethod(method, toParamTypes(params));
|
String cacheKey = "Method:" + obj.getClass().getName() + ":" + method + ":" + Arrays.hashCode(params);
|
||||||
m.setAccessible(true);
|
|
||||||
return m.invoke(obj, params);
|
if (methodCache.containsKey(cacheKey)) {
|
||||||
|
Method cachedMethod = methodCache.get(cacheKey);
|
||||||
|
return cachedMethod.invoke(obj, params);
|
||||||
|
} else {
|
||||||
|
Method m = obj.getClass().getMethod(method, toParamTypes(params));
|
||||||
|
m.setAccessible(true);
|
||||||
|
methodCache.put(cacheKey, m);
|
||||||
|
return m.invoke(obj, params);
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
|
@ -330,9 +218,37 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object callDeclaredMethod(Object obj, String method, Object... params) {
|
public static Object callDeclaredMethod(Object obj, String method, Object... params) {
|
||||||
try {
|
try {
|
||||||
Method m = obj.getClass().getDeclaredMethod(method, toParamTypes(params));
|
String cacheKey = "DeclaredMethod:" + obj.getClass().getName() + ":" + method + ":" + Arrays.hashCode(params);
|
||||||
m.setAccessible(true);
|
|
||||||
return m.invoke(obj, params);
|
if (methodCache.containsKey(cacheKey)) {
|
||||||
|
Method cachedMethod = methodCache.get(cacheKey);
|
||||||
|
return cachedMethod.invoke(obj, params);
|
||||||
|
} else {
|
||||||
|
Method m = obj.getClass().getDeclaredMethod(method, toParamTypes(params));
|
||||||
|
m.setAccessible(true);
|
||||||
|
methodCache.put(cacheKey, m);
|
||||||
|
return m.invoke(obj, params);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Object callDeclaredMethod(Class<?> clazz, Object obj, String method, Object... params) {
|
||||||
|
try {
|
||||||
|
String cacheKey = "DeclaredMethod:" + clazz.getName() + ":" + method + ":" + Arrays.hashCode(params);
|
||||||
|
|
||||||
|
if (methodCache.containsKey(cacheKey)) {
|
||||||
|
Method cachedMethod = methodCache.get(cacheKey);
|
||||||
|
return cachedMethod.invoke(obj, params);
|
||||||
|
} else {
|
||||||
|
Method m = clazz.getDeclaredMethod(method, toParamTypes(params));
|
||||||
|
m.setAccessible(true);
|
||||||
|
methodCache.put(cacheKey, m);
|
||||||
|
return m.invoke(obj, params);
|
||||||
|
}
|
||||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
|
@ -341,8 +257,15 @@ public class ReflectionUtil {
|
||||||
|
|
||||||
public static boolean hasField(Object packet, String field) {
|
public static boolean hasField(Object packet, String field) {
|
||||||
try {
|
try {
|
||||||
packet.getClass().getDeclaredField(field);
|
String cacheKey = "HasField:" + packet.getClass().getName() + ":" + field;
|
||||||
return true;
|
|
||||||
|
if (fieldCache.containsKey(cacheKey)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
packet.getClass().getDeclaredField(field);
|
||||||
|
fieldCache.put(cacheKey, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} catch (NoSuchFieldException ex) {
|
} catch (NoSuchFieldException ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -351,9 +274,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object getField(Object object, String field) {
|
public static Object getField(Object object, String field) {
|
||||||
try {
|
try {
|
||||||
Field f = object.getClass().getField(field);
|
String cacheKey = "Field:" + object.getClass().getName() + ":" + field;
|
||||||
f.setAccessible(true);
|
|
||||||
return f.get(object);
|
if (fieldCache.containsKey(cacheKey)) {
|
||||||
|
Field cachedField = fieldCache.get(cacheKey);
|
||||||
|
return cachedField.get(object);
|
||||||
|
} else {
|
||||||
|
Field f = object.getClass().getField(field);
|
||||||
|
f.setAccessible(true);
|
||||||
|
fieldCache.put(cacheKey, f);
|
||||||
|
return f.get(object);
|
||||||
|
}
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
|
@ -363,9 +294,17 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object getDeclaredField(Class<?> clazz, String field) {
|
public static Object getDeclaredField(Class<?> clazz, String field) {
|
||||||
try {
|
try {
|
||||||
Field f = clazz.getDeclaredField(field);
|
String cacheKey = "DeclaredField:" + clazz.getName() + ":" + field;
|
||||||
f.setAccessible(true);
|
|
||||||
return f.get(null);
|
if (fieldCache.containsKey(cacheKey)) {
|
||||||
|
Field cachedField = fieldCache.get(cacheKey);
|
||||||
|
return cachedField.get(null);
|
||||||
|
} else {
|
||||||
|
Field f = clazz.getDeclaredField(field);
|
||||||
|
f.setAccessible(true);
|
||||||
|
fieldCache.put(cacheKey, f);
|
||||||
|
return f.get(null);
|
||||||
|
}
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
|
@ -375,9 +314,37 @@ public class ReflectionUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object getDeclaredField(Object object, String field) {
|
public static Object getDeclaredField(Object object, String field) {
|
||||||
try {
|
try {
|
||||||
Field f = object.getClass().getDeclaredField(field);
|
String cacheKey = "DeclaredField:" + object.getClass().getName() + ":" + field;
|
||||||
f.setAccessible(true);
|
|
||||||
return f.get(object);
|
if (fieldCache.containsKey(cacheKey)) {
|
||||||
|
Field cachedField = fieldCache.get(cacheKey);
|
||||||
|
return cachedField.get(object);
|
||||||
|
} else {
|
||||||
|
Field f = object.getClass().getDeclaredField(field);
|
||||||
|
f.setAccessible(true);
|
||||||
|
fieldCache.put(cacheKey, f);
|
||||||
|
return f.get(object);
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Object getDeclaredField(Class<?> clazz, Object object, String field) {
|
||||||
|
try {
|
||||||
|
String cacheKey = "DeclaredField:" + clazz.getName() + ":" + field;
|
||||||
|
|
||||||
|
if (fieldCache.containsKey(cacheKey)) {
|
||||||
|
Field cachedField = fieldCache.get(cacheKey);
|
||||||
|
return cachedField.get(object);
|
||||||
|
} else {
|
||||||
|
Field f = clazz.getDeclaredField(field);
|
||||||
|
f.setAccessible(true);
|
||||||
|
fieldCache.put(cacheKey, f);
|
||||||
|
return f.get(object);
|
||||||
|
}
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
|
@ -386,152 +353,19 @@ public class ReflectionUtil {
|
||||||
|
|
||||||
public static void setDeclaredField(Object object, String field, Object value) {
|
public static void setDeclaredField(Object object, String field, Object value) {
|
||||||
try {
|
try {
|
||||||
Field f = object.getClass().getDeclaredField(field);
|
String cacheKey = "DeclaredField:" + object.getClass().getName() + ":" + field;
|
||||||
f.setAccessible(true);
|
|
||||||
f.set(object, value);
|
if (fieldCache.containsKey(cacheKey)) {
|
||||||
|
Field cachedField = fieldCache.get(cacheKey);
|
||||||
|
cachedField.set(object, value);
|
||||||
|
} else {
|
||||||
|
Field f = object.getClass().getDeclaredField(field);
|
||||||
|
f.setAccessible(true);
|
||||||
|
fieldCache.put(cacheKey, f);
|
||||||
|
f.set(object, value);
|
||||||
|
}
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility.
|
|
||||||
*
|
|
||||||
* @param newPackage the 1.17 package name.
|
|
||||||
* @param name the name of the class.
|
|
||||||
* @return the NMS class or null if not found.
|
|
||||||
* @since 4.0.0
|
|
||||||
*/
|
|
||||||
@javax.annotation.Nullable
|
|
||||||
public static Class<?> getNMSClass(@Nonnull String newPackage, @Nonnull String name) {
|
|
||||||
if (supports(17)) name = newPackage + '.' + name;
|
|
||||||
return getNMSClass(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a NMS (net.minecraft.server) class.
|
|
||||||
*
|
|
||||||
* @param name the name of the class.
|
|
||||||
* @return the NMS class or null if not found.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
@javax.annotation.Nullable
|
|
||||||
public static Class<?> getNMSClass(@Nonnull String name) {
|
|
||||||
try {
|
|
||||||
return Class.forName(NMS + name);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a packet to the player asynchronously if they're online.
|
|
||||||
* Packets are thread-safe.
|
|
||||||
*
|
|
||||||
* @param player the player to send the packet to.
|
|
||||||
* @param packets the packets to send.
|
|
||||||
* @return the async thread handling the packet.
|
|
||||||
* @see #sendPacketSync(Player, Object...)
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
@Nonnull
|
|
||||||
public static CompletableFuture<Void> sendPacket(@Nonnull Player player, @Nonnull Object... packets) {
|
|
||||||
return CompletableFuture.runAsync(() -> sendPacketSync(player, packets))
|
|
||||||
.exceptionally(ex -> {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a packet to the player synchronously if they're online.
|
|
||||||
*
|
|
||||||
* @param player the player to send the packet to.
|
|
||||||
* @param packets the packets to send.
|
|
||||||
* @see #sendPacket(Player, Object...)
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
public static void sendPacketSync(@Nonnull Player player, @Nonnull Object... packets) {
|
|
||||||
try {
|
|
||||||
Object handle = GET_HANDLE.invoke(player);
|
|
||||||
Object connection = PLAYER_CONNECTION.invoke(handle);
|
|
||||||
|
|
||||||
// Checking if the connection is not null is enough. There is no need to check if the player is online.
|
|
||||||
if (connection != null) {
|
|
||||||
for (Object packet : packets) SEND_PACKET.invoke(connection, packet);
|
|
||||||
}
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@javax.annotation.Nullable
|
|
||||||
public static Object getHandle(@Nonnull Player player) {
|
|
||||||
Objects.requireNonNull(player, "Cannot get handle of null player");
|
|
||||||
try {
|
|
||||||
return GET_HANDLE.invoke(player);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@javax.annotation.Nullable
|
|
||||||
public static Object getHandle(@Nonnull World world) {
|
|
||||||
Objects.requireNonNull(world, "Cannot get handle of null world");
|
|
||||||
try {
|
|
||||||
return GET_HANDLE_WORLD.invoke(world);
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a CraftBukkit (org.bukkit.craftbukkit) class.
|
|
||||||
*
|
|
||||||
* @param name the name of the class to load.
|
|
||||||
* @return the CraftBukkit class or null if not found.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
@javax.annotation.Nullable
|
|
||||||
public static Class<?> getCraftClass(@Nonnull String name) {
|
|
||||||
try {
|
|
||||||
return Class.forName(CRAFTBUKKIT + name);
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class VersionHandler<T> {
|
|
||||||
private int version;
|
|
||||||
private T handle;
|
|
||||||
|
|
||||||
private VersionHandler(int version, T handle) {
|
|
||||||
if (supports(version)) {
|
|
||||||
this.version = version;
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VersionHandler<T> v(int version, T handle) {
|
|
||||||
if (version == this.version)
|
|
||||||
throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version);
|
|
||||||
if (version > this.version && supports(version)) {
|
|
||||||
this.version = version;
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T orElse(T handle) {
|
|
||||||
return this.version == 0 ? handle : this.handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int toInt(String string, int def) {
|
|
||||||
return string.isBlank() ? def : Integer.parseInt(string);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -143,14 +143,12 @@ public class UpdateManager {
|
||||||
File pluginFile = getPluginFile(); // /plugins/XXX.jar
|
File pluginFile = getPluginFile(); // /plugins/XXX.jar
|
||||||
if (pluginFile == null) {
|
if (pluginFile == null) {
|
||||||
this.downloadResponse.accept(DownloadResponse.ERROR, null);
|
this.downloadResponse.accept(DownloadResponse.ERROR, null);
|
||||||
Bukkit.getLogger().info("Pluginfile is null");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File updateFolder = Bukkit.getUpdateFolderFile();
|
File updateFolder = Bukkit.getUpdateFolderFile();
|
||||||
if (!updateFolder.exists()) {
|
if (!updateFolder.exists()) {
|
||||||
if (!updateFolder.mkdirs()) {
|
if (!updateFolder.mkdirs()) {
|
||||||
this.downloadResponse.accept(DownloadResponse.ERROR, null);
|
this.downloadResponse.accept(DownloadResponse.ERROR, null);
|
||||||
Bukkit.getLogger().info("Updatefolder doesn't exists, and can't be made");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
Loading…
Add table
Reference in a new issue