diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml deleted file mode 100644 index c9caadf..0000000 --- a/.github/workflows/maven.yml +++ /dev/null @@ -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 diff --git a/.idea/.gitignore b/.idea/.gitignore index 13566b8..ecca007 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -5,4 +5,4 @@ /httpRequests/ # Datasource local storage ignored files /dataSources/ -/dataSources.local.xml +/dataSources.local.xml \ No newline at end of file diff --git a/.idea/copyright/SBDevelopment.xml b/.idea/copyright/SBDevelopment.xml index 7471535..9a257e5 100644 --- a/.idea/copyright/SBDevelopment.xml +++ b/.idea/copyright/SBDevelopment.xml @@ -1,6 +1,6 @@ <component name="CopyrightManager"> - <copyright> - <option name="notice" value="This file is part of &#36;project.name. Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-", "&#36;today.year")&#36;today.year 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/>." /> - <option name="myName" value="SBDevelopment" /> - </copyright> + <copyright> + <option name="notice" value="This file is part of &#36;project.name. Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-", "&#36;today.year")&#36;today.year 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/>." /> + <option name="myName" value="SBDevelopment" /> + </copyright> </component> \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..26f52f6 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ +<component name="CopyrightManager"> + <settings default="SBDevelopment"> + <module2copyright> + <element module="Project Files" copyright="SBDevelopment" /> + </module2copyright> + </settings> +</component> \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml index d8e9561..a672138 100644 --- a/.idea/discord.xml +++ b/.idea/discord.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="DiscordProjectSettings"> - <option name="show" value="PROJECT_FILES" /> - <option name="description" value="" /> - </component> + <component name="DiscordProjectSettings"> + <option name="show" value="PROJECT_FILES" /> + <option name="description" value="" /> + </component> </project> \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 337d139..67b333b 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="Encoding"> + <file url="file://$PROJECT_DIR$/API/src/main/java" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/API/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Dist/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Dist/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_12_R1/src/main/java" charset="UTF-8" /> @@ -17,9 +19,9 @@ <file url="file://$PROJECT_DIR$/NMS-v1_17_R1/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_18_R2/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_18_R2/src/main/resources" charset="UTF-8" /> - <file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/java" charset="UTF-8" /> - <file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/resources" charset="UTF-8" /> - <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> - <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/NMS-v1_19_R3/src/main/java" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/NMS-v1_19_R3/src/main/resources" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/NMS-v1_20_R1/src/main/java" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/NMS-v1_20_R1/src/main/resources" charset="UTF-8" /> </component> </project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 200af21..fadb731 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,8 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="ComposerSettings"> - <execution /> - </component> <component name="EntryPointsManager"> <list size="1"> <item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" /> @@ -15,22 +12,6 @@ <option value="$PROJECT_DIR$/pom.xml" /> </list> </option> - <option name="ignoredFiles"> - <set> - <option value="$PROJECT_DIR$/API/pom.xml" /> - <option value="$PROJECT_DIR$/Dist/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_12_R1/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_13_R2/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_14_R1/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_15_R1/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_16_R3/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_17_R1/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_18_R2/pom.xml" /> - <option value="$PROJECT_DIR$/NMS-v1_19_R1/pom.xml" /> - </set> - </option> - </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/out" /> </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="temurin-11" project-jdk-type="JavaSDK" /> </project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="VcsDirectoryMappings"> - <mapping directory="$PROJECT_DIR$" vcs="Git" /> + <mapping directory="" vcs="Git" /> </component> </project> \ No newline at end of file diff --git a/API/pom.xml b/API/pom.xml new file mode 100644 index 0000000..a276161 --- /dev/null +++ b/API/pom.xml @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ This file is part of MapReflectionAPI. + ~ Copyright (c) 2022-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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-API</artifactId> + + <properties> + <jdk.version>11</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + <annotationProcessorPaths> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.28</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.5.0</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <relocations> + <relocation> + <pattern>com.bergerkiller.bukkit.common</pattern> + <shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bkcommonlib</shadedPattern> + </relocation> + <relocation> + <pattern>org.bstats</pattern> + <shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bstats</shadedPattern> + </relocation> + </relocations> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.projectlombok</groupId> + <artifactId>lombok-maven-plugin</artifactId> + <version>1.18.20.0</version> + <configuration> + <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> + <outputDirectory>${maven.lombok.delombok-target}</outputDirectory> + <addOutputDirectory>false</addOutputDirectory> + </configuration> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>delombok</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.5.0</version> + <configuration> + <release>${jdk.version}</release> + <sourcepath>${maven.lombok.delombok-target}</sourcepath> + <sourceFileExcludes> + <sourceFileExclude>**/com/bergerkiller/bukkit/common/io/*.java</sourceFileExclude> + <sourceFileExclude>**/com/bergerkiller/bukkit/common/map/*.java</sourceFileExclude> + <sourceFileExclude>**/com/bergerkiller/bukkit/common/map/color/*.java</sourceFileExclude> + <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/*.java</sourceFileExclude> + <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/cmd/*.java</sourceFileExclude> + <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/managers/*.java</sourceFileExclude> + <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/utils/*.java</sourceFileExclude> + <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/listeners/*.java</sourceFileExclude> + </sourceFileExcludes> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + <includes> + <include>plugin.yml</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <excludes> + <exclude>plugin.yml</exclude> + </excludes> + </resource> + </resources> + </build> + + <repositories> + <repository> + <id>spigot-repo</id> + <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> + </repository> + <repository> + <id>MG-Dev Jenkins CI Maven Repository</id> + <url>https://ci.mg-dev.eu/plugin/repository/everything</url> + </repository> + <repository> + <id>dmulloy2-repo</id> + <url>https://repo.dmulloy2.net/repository/public/</url> + </repository> + </repositories> + + <dependencies> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.28</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.bstats</groupId> + <artifactId>bstats-bukkit</artifactId> + <version>3.0.2</version> + <scope>compile</scope> + </dependency> + + <!-- Libraries below are provided by Spigot --> + <dependency> + <groupId>org.jetbrains</groupId> + <artifactId>annotations-java5</artifactId> + <version>23.0.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-transport</artifactId> + <version>4.1.77.Final</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/com/bergerkiller/bukkit/common/LICENSE b/API/src/main/java/com/bergerkiller/bukkit/common/LICENSE similarity index 100% rename from src/main/java/com/bergerkiller/bukkit/common/LICENSE rename to API/src/main/java/com/bergerkiller/bukkit/common/LICENSE diff --git a/src/main/java/com/bergerkiller/bukkit/common/README.md b/API/src/main/java/com/bergerkiller/bukkit/common/README.md similarity index 100% rename from src/main/java/com/bergerkiller/bukkit/common/README.md rename to API/src/main/java/com/bergerkiller/bukkit/common/README.md diff --git a/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java b/API/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java similarity index 97% rename from src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java rename to API/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java index 4b89417..90631c8 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java +++ b/API/src/main/java/com/bergerkiller/bukkit/common/io/BitInputStream.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java b/API/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java similarity index 93% rename from src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java rename to API/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java index f841f39..fcc5516 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java +++ b/API/src/main/java/com/bergerkiller/bukkit/common/io/BitPacket.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java b/API/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java similarity index 98% rename from src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java rename to API/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java index f0777e9..affdef6 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java +++ b/API/src/main/java/com/bergerkiller/bukkit/common/map/MapColorPalette.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java similarity index 100% rename from src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java rename to API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDBubbleFormat.java diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java similarity index 94% rename from src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java rename to API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java index b4a4e22..ea06d57 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java +++ b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDGenBukkit.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java similarity index 98% rename from src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java rename to API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java index 57f1710..1331937 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java +++ b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MCSDWebbingCodec.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java similarity index 98% rename from src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java rename to API/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java index 1e09f4d..c6185d6 100644 --- a/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java +++ b/API/src/main/java/com/bergerkiller/bukkit/common/map/color/MapColorSpaceData.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java similarity index 88% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java index 70b7368..ac18791 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/MapReflectionAPI.java @@ -38,6 +38,7 @@ import java.util.logging.Level; public class MapReflectionAPI extends JavaPlugin { private static MapReflectionAPI instance; private static MapManager mapManager; + private static PacketListener packetListener; /** * Get the plugin instance @@ -89,11 +90,26 @@ public class MapReflectionAPI extends JavaPlugin { getLogger().info("Loading the commands..."); getCommand("mapmanager").setExecutor(new MapManagerCMD()); - getLogger().info("Loading the map manager..."); - mapManager = new MapManager(); + try { + packetListener = PacketListener.construct(this); + } catch (IllegalStateException e) { + getLogger().log(Level.SEVERE, e.getMessage(), e); + Bukkit.getPluginManager().disablePlugin(this); + return; + } + packetListener.init(this); + + try { + mapManager = new MapManager(this); + } catch (IllegalStateException e) { + getLogger().log(Level.SEVERE, e.getMessage(), e); + Bukkit.getPluginManager().disablePlugin(this); + return; + } if (Configuration.getInstance().isAllowVanilla()) { - getLogger().info("Vanilla Maps are allowed. Discovering occupied Map IDs..."); + getLogger().info("Vanilla Maps are allowed!"); + getLogger().info("Discovering occupied Map IDs..."); int occupiedIDs = 0; for (int s = 0; s < Short.MAX_VALUE; s++) { try { @@ -113,9 +129,8 @@ public class MapReflectionAPI extends JavaPlugin { getLogger().info("Registering the listeners..."); Bukkit.getPluginManager().registerEvents(new MapListener(), this); - Bukkit.getPluginManager().registerEvents(new PacketListener(), this); - getLogger().info("Loading metrics..."); + getLogger().info("Enabling metrics..."); Metrics metrics = new Metrics(this, 16033); metrics.addCustomChart(new SingleLineChart("managed_maps", () -> mapManager.getManagedMapsCount())); @@ -167,6 +182,9 @@ public class MapReflectionAPI extends JavaPlugin { @Override public void onDisable() { + getLogger().info("Disabling the packet handler..."); + if (packetListener != null) Bukkit.getOnlinePlayers().forEach(p -> packetListener.removePlayer(p)); + getLogger().info("MapReflectionAPI is disabled!"); instance = null; diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java similarity index 93% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java index fc44a5b..b6e56c8 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/AbstractMapWrapper.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by @@ -33,4 +33,4 @@ public abstract class AbstractMapWrapper { getController().cancelSend(); getController().clearViewers(); } -} +} \ No newline at end of file diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/ArrayImage.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/ArrayImage.java similarity index 97% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/ArrayImage.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/ArrayImage.java index d58cfb2..c32abdf 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/ArrayImage.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/ArrayImage.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java similarity index 96% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java index ca0927f..773c4a9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/IMapController.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java similarity index 100% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapController.java diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java similarity index 84% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java index 7120429..00b906d 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapManager.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by @@ -18,13 +18,17 @@ package tech.sbdevelopment.mapreflectionapi.api; +import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.Nullable; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; import tech.sbdevelopment.mapreflectionapi.managers.Configuration; import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -36,6 +40,25 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MapManager { protected final Set<Integer> occupiedIds = new HashSet<>(); protected final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>(); + private final Class<?> wrapperClass; + + public MapManager(JavaPlugin plugin) throws IllegalStateException { + String packageName = Bukkit.getServer().getClass().getPackage().getName(); + String version = packageName.substring(packageName.lastIndexOf('.') + 1); + + plugin.getLogger().info("Enabling MapManager for " + version + "..."); + + try { + final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.MapWrapper_" + version); + if (MapWrapper.class.isAssignableFrom(clazz)) { + wrapperClass = clazz; + } else { + throw new IllegalStateException("Plugin corrupted! Detected invalid MapWrapper class."); + } + } catch (Exception ex) { + throw new IllegalStateException("This Spigot version (" + version + ") is not supported! Contact the developer to get support."); + } + } /** * Get the amount of maps managed by the plugin @@ -124,9 +147,15 @@ public class MapManager { * @return The wrapper */ private MapWrapper wrapNewImage(ArrayImage image) { - MapWrapper wrapper = new MapWrapper(image); - managedMaps.add(wrapper); - return wrapper; + try { + MapWrapper wrapper = (MapWrapper) wrapperClass.getDeclaredConstructor(ArrayImage.class).newInstance(image); + managedMaps.add(wrapper); + return wrapper; + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException e) { + e.printStackTrace(); + return null; + } } /** diff --git a/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java new file mode 100644 index 0000000..2d7f9cb --- /dev/null +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java @@ -0,0 +1,42 @@ +/* + * 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; + +/** + * A {@link MapWrapper} wraps one image. + */ +public abstract class MapWrapper extends AbstractMapWrapper { + protected ArrayImage content; + + /** + * Construct a new {@link MapWrapper} + * + * @param image The {@link ArrayImage} to wrap + */ + public MapWrapper(ArrayImage image) { + this.content = image; + } + + public ArrayImage getContent() { + return content; + } + + @Override + public abstract MapController getController(); +} \ No newline at end of file diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java similarity index 100% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapController.java diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java similarity index 100% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MultiMapWrapper.java diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreateInventoryMapUpdateEvent.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreateInventoryMapUpdateEvent.java similarity index 97% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreateInventoryMapUpdateEvent.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreateInventoryMapUpdateEvent.java index 6629266..a003ddc 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreateInventoryMapUpdateEvent.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/CreateInventoryMapUpdateEvent.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java similarity index 95% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java index 4cc282f..b7aa6c9 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapCancelEvent.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java similarity index 96% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java index d106f06..65b9a6e 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapContentUpdateEvent.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java similarity index 97% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java index 6e2dbb4..6bbcb4a 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/MapInteractEvent.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/package-info.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/package-info.java similarity index 91% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/package-info.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/package-info.java index 96733cc..56c07cd 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/package-info.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/events/package-info.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/MapLimitExceededException.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/MapLimitExceededException.java similarity index 92% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/MapLimitExceededException.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/MapLimitExceededException.java index 55a2cc7..9aa6672 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/MapLimitExceededException.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/MapLimitExceededException.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/package-info.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/package-info.java similarity index 91% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/package-info.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/package-info.java index bff336f..7fb11d0 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/package-info.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/exceptions/package-info.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/package-info.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/package-info.java similarity index 91% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/package-info.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/package-info.java index b3d3b45..6c697b6 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/package-info.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/api/package-info.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/cmd/MapManagerCMD.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/cmd/MapManagerCMD.java similarity index 96% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/cmd/MapManagerCMD.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/cmd/MapManagerCMD.java index 4284c2a..5fe9c42 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/cmd/MapManagerCMD.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/cmd/MapManagerCMD.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/MapListener.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/MapListener.java similarity index 95% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/MapListener.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/MapListener.java index 33ee0b3..4c8729e 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/MapListener.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/MapListener.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/API/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java new file mode 100644 index 0000000..d56a89e --- /dev/null +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java @@ -0,0 +1,80 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.listeners; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.Vector; + +public abstract class PacketListener implements Listener { + protected JavaPlugin plugin; + + public static PacketListener construct(JavaPlugin plugin) throws IllegalStateException { + String packageName = Bukkit.getServer().getClass().getPackage().getName(); + String version = packageName.substring(packageName.lastIndexOf('.') + 1); + + plugin.getLogger().info("Enabling PacketListener for " + version + "..."); + + try { + final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.PacketListener_" + version); + if (PacketListener.class.isAssignableFrom(clazz)) { + return (PacketListener) clazz.getDeclaredConstructor().newInstance(); + } else { + throw new IllegalStateException("Plugin corrupted! Detected invalid PacketListener class."); + } + } catch (Exception ex) { + throw new IllegalStateException("This Minecraft version (" + version + ") is not supported! Contact the developer to get support."); + } + } + + public void init(JavaPlugin plugin) { + this.plugin = plugin; + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + injectPlayer(e.getPlayer()); + } + + @EventHandler + public void onQuit(PlayerQuitEvent e) { + removePlayer(e.getPlayer()); + } + + protected abstract void injectPlayer(Player p); + + public abstract void removePlayer(Player p); + + protected abstract Vector vec3DToVector(Object vec3d); + + protected boolean hasField(Object packet, String field) { + try { + packet.getClass().getDeclaredField(field); + return true; + } catch (NoSuchFieldException ex) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/managers/Configuration.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/managers/Configuration.java similarity index 95% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/managers/Configuration.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/managers/Configuration.java index f5acc72..ab55bef 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/managers/Configuration.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/managers/Configuration.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java similarity index 100% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/MainUtil.java diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java similarity index 100% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/ReflectionUtil.java diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java similarity index 99% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java index 7acc5f3..631c663 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/UpdateManager.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/YamlFile.java b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/YamlFile.java similarity index 97% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/utils/YamlFile.java rename to API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/YamlFile.java index 50154a5..bb2db31 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/YamlFile.java +++ b/API/src/main/java/tech/sbdevelopment/mapreflectionapi/utils/YamlFile.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub b/API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub similarity index 100% rename from src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub rename to API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_12.bub diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub b/API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub similarity index 100% rename from src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub rename to API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_16.bub diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub b/API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub similarity index 100% rename from src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub rename to API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_17.bub diff --git a/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub b/API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub similarity index 100% rename from src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub rename to API/src/main/resources/com/bergerkiller/bukkit/common/internal/resources/map/map_1_8_8.bub diff --git a/src/main/resources/config.yml b/API/src/main/resources/config.yml similarity index 100% rename from src/main/resources/config.yml rename to API/src/main/resources/config.yml diff --git a/src/main/resources/plugin.yml b/API/src/main/resources/plugin.yml similarity index 100% rename from src/main/resources/plugin.yml rename to API/src/main/resources/plugin.yml diff --git a/Dist/pom.xml b/Dist/pom.xml new file mode 100644 index 0000000..0af26ba --- /dev/null +++ b/Dist/pom.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <packaging>jar</packaging> + + <artifactId>MapReflectionAPI-Dist</artifactId> + + <build> + <directory>../target</directory> + <finalName>${project.parent.name}-${project.version}</finalName> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.0.0-M2</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.3.0</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + + <!-- Include all the dependencies required by the plugin --> + <artifactSet> + <includes> + <include>tech.sbdevelopment:MapReflectionAPI*</include> + </includes> + </artifactSet> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_20_R1</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_19_R3</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_18_R2</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_16_R3</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_17_R1</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_15_R1</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_14_R1</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_13_R2</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-NMS-v1_12_R1</artifactId> + <version>${project.parent.version}</version> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_12_R1/pom.xml b/NMS-v1_12_R1/pom.xml new file mode 100644 index 0000000..adaa593 --- /dev/null +++ b/NMS-v1_12_R1/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_12_R1</artifactId> + + <properties> + <NMSVersion>1.12.2-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>11</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java b/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_12_R1.java similarity index 57% rename from src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java rename to NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_12_R1.java index 56fcaed..f24528d 100644 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapSender.java +++ b/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_12_R1.java @@ -1,6 +1,6 @@ /* * 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 * it under the terms of the GNU General Public License as published by @@ -16,25 +16,27 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package tech.sbdevelopment.mapreflectionapi.api; +package tech.sbdevelopment.mapreflectionapi.nms; -import lombok.Data; +import net.minecraft.server.v1_12_R1.PacketPlayOutMap; import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; -import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** - * The {@link MapSender} sends the Map packets to players. + * The {@link MapSender_v1_12_R1} sends the Map packets to players. */ -public class MapSender { +public class MapSender_v1_12_R1 { private static final List<QueuedMap> sendQueue = new ArrayList<>(); private static int senderID = -1; - private MapSender() { + private MapSender_v1_12_R1() { } /** @@ -82,9 +84,6 @@ public class MapSender { }, 0, 2); } - private static final Class<?> packetPlayOutMapClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutMap"); - private static final Class<?> worldMapData = ReflectionUtil.supports(17) ? ReflectionUtil.getNMSClass("world.level.saveddata.maps", "WorldMap$b") : null; - /** * Send a map to a player * @@ -109,57 +108,59 @@ public class MapSender { } final int id = -id0; - Object packet; - if (ReflectionUtil.supports(17)) { //1.17+ - 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 - ); + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //??? + new ArrayList<>(), //Icons + content.array, //Data + content.minX, //X pos + content.minY, //Y pos + content.maxX, //X size (2nd X pos) + content.maxY //Y size (2nd Y pos) + ); - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Show icons - new ReflectionUtil.CollectionParam<>(), //Icons - updateData - ); - } else if (ReflectionUtil.supports(14)) { //1.16-1.14 - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //Tracking position - false, //Locked - new ReflectionUtil.CollectionParam<>(), //Icons - content.array, //Data - content.minX, //X pos - content.minY, //Y pos - content.maxX, //X size (2nd X pos) - content.maxY //Y size (2nd Y pos) - ); - } else { //1.13- - packet = ReflectionUtil.callConstructor(packetPlayOutMapClass, - id, //ID - (byte) 0, //Scale, 0 = 1 block per pixel - false, //??? - new ReflectionUtil.CollectionParam<>(), //Icons - content.array, //Data - content.minX, //X pos - content.minY, //Y pos - content.maxX, //X size (2nd X pos) - content.maxY //Y size (2nd Y pos) - ); - } - - ReflectionUtil.sendPacket(player, packet); + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } catch (Exception e) { + e.printStackTrace(); + } + }); } - @Data static final class QueuedMap { private final int id; private final ArrayImage image; private final Player player; + + QueuedMap(int id, ArrayImage image, Player player) { + this.id = id; + this.image = image; + this.player = player; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + QueuedMap that = (QueuedMap) obj; + return this.id == that.id && + Objects.equals(this.image, that.image) && + Objects.equals(this.player, that.player); + } + + @Override + public int hashCode() { + return Objects.hash(id, image, player); + } + + @Override + public String toString() { + return "QueuedMap[" + + "id=" + id + ", " + + "image=" + image + ", " + + "player=" + player + ']'; + } } } diff --git a/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_12_R1.java b/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_12_R1.java new file mode 100644 index 0000000..4d710ab --- /dev/null +++ b/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_12_R1.java @@ -0,0 +1,249 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_12_R1.*; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_12_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_12_R1 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_12_R1.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_12_R1.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_12_R1.sendMap(id, MapWrapper_v1_12_R1.this.content, player); + } else { + MapSender_v1_12_R1.addToQueue(id, MapWrapper_v1_12_R1.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_12_R1.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().defaultContainer.windowId; + + ItemStack stack = new ItemStack(Material.MAP, 1); + net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack); + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_12_R1.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().getEntity(entityId); + if (entity == null) return null; + + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + if (nmsStack.getTag() == null) nmsStack.setTag(new NBTTagCompound()); //No orCreate on 1.12.2! + nmsStack.getTag().setInt("map", mapId); //getTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.server.v1_12_R1.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_12_R1.ItemStack>) getDeclaredField(EntityItemFrame.class, "c"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + }; + + public MapWrapper_v1_12_R1(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_12_R1.java b/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_12_R1.java new file mode 100644 index 0000000..f2a6924 --- /dev/null +++ b/NMS-v1_12_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_12_R1.java @@ -0,0 +1,123 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.server.v1_12_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class PacketListener_v1_12_R1 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap) { + PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet; + + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity) { + PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet; + + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.a(); //action + EnumHand hand = packetPlayInUseEntity.b(); //hand + Vec3D pos = packetPlayInUseEntity.c(); //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot) { + PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet; + + int slot = packetPlayInSetCreativeSlot.a(); + ItemStack item = packetPlayInSetCreativeSlot.getItemStack(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline(); + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel; + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0); + + Vec3D vec3dObj = (Vec3D) vec3d; + return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z); + } +} diff --git a/NMS-v1_13_R2/pom.xml b/NMS-v1_13_R2/pom.xml new file mode 100644 index 0000000..17b0098 --- /dev/null +++ b/NMS-v1_13_R2/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_13_R2</artifactId> + + <properties> + <NMSVersion>1.13.2-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>11</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_13_R2.java b/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_13_R2.java new file mode 100644 index 0000000..d82d5fc --- /dev/null +++ b/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_13_R2.java @@ -0,0 +1,166 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_13_R2.PacketPlayOutMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The {@link MapSender_v1_13_R2} sends the Map packets to players. + */ +public class MapSender_v1_13_R2 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_13_R2() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //??? + new ArrayList<>(), //Icons + content.array, //Data + content.minX, //X pos + content.minY, //Y pos + content.maxX, //X size (2nd X pos) + content.maxY //Y size (2nd Y pos) + ); + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + static final class QueuedMap { + private final int id; + private final ArrayImage image; + private final Player player; + + QueuedMap(int id, ArrayImage image, Player player) { + this.id = id; + this.image = image; + this.player = player; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (QueuedMap) obj; + return this.id == that.id && + Objects.equals(this.image, that.image) && + Objects.equals(this.player, that.player); + } + + @Override + public int hashCode() { + return Objects.hash(id, image, player); + } + + @Override + public String toString() { + return "QueuedMap[" + + "id=" + id + ", " + + "image=" + image + ", " + + "player=" + player + ']'; + } + } +} diff --git a/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_13_R2.java b/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_13_R2.java new file mode 100644 index 0000000..145dc66 --- /dev/null +++ b/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_13_R2.java @@ -0,0 +1,248 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_13_R2.*; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_13_R2 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_13_R2.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_13_R2.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_13_R2.sendMap(id, MapWrapper_v1_13_R2.this.content, player); + } else { + MapSender_v1_13_R2.addToQueue(id, MapWrapper_v1_13_R2.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_13_R2.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().defaultContainer.windowId; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.server.v1_13_R2.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack); + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_13_R2.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().getEntity(entityId); + if (entity == null) return null; + + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.server.v1_13_R2.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.server.v1_13_R2.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_13_R2.ItemStack>) getDeclaredField(EntityItemFrame.class, "e"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + }; + + public MapWrapper_v1_13_R2(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_13_R2.java b/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_13_R2.java new file mode 100644 index 0000000..2180ab0 --- /dev/null +++ b/NMS-v1_13_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_13_R2.java @@ -0,0 +1,123 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.server.v1_13_R2.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class PacketListener_v1_13_R2 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap) { + PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet; + + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity) { + PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet; + + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action + EnumHand hand = packetPlayInUseEntity.c(); //hand + Vec3D pos = packetPlayInUseEntity.d(); //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot) { + PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet; + + int slot = packetPlayInSetCreativeSlot.b(); + ItemStack item = packetPlayInSetCreativeSlot.getItemStack(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline(); + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel; + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0); + + Vec3D vec3dObj = (Vec3D) vec3d; + return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z); + } +} diff --git a/NMS-v1_14_R1/pom.xml b/NMS-v1_14_R1/pom.xml new file mode 100644 index 0000000..9e67e14 --- /dev/null +++ b/NMS-v1_14_R1/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_14_R1</artifactId> + + <properties> + <NMSVersion>1.14.4-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>11</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_14_R1.java b/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_14_R1.java new file mode 100644 index 0000000..49ebfae --- /dev/null +++ b/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_14_R1.java @@ -0,0 +1,167 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_14_R1.PacketPlayOutMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The {@link MapSender_v1_14_R1} sends the Map packets to players. + */ +public class MapSender_v1_14_R1 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_14_R1() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Tracking position + false, //Locked + new ArrayList<>(), //Icons + content.array, //Data + content.minX, //X pos + content.minY, //Y pos + content.maxX, //X size (2nd X pos) + content.maxY //Y size (2nd Y pos) + ); + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + static final class QueuedMap { + private final int id; + private final ArrayImage image; + private final Player player; + + QueuedMap(int id, ArrayImage image, Player player) { + this.id = id; + this.image = image; + this.player = player; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (QueuedMap) obj; + return this.id == that.id && + Objects.equals(this.image, that.image) && + Objects.equals(this.player, that.player); + } + + @Override + public int hashCode() { + return Objects.hash(id, image, player); + } + + @Override + public String toString() { + return "QueuedMap[" + + "id=" + id + ", " + + "image=" + image + ", " + + "player=" + player + ']'; + } + } +} diff --git a/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_14_R1.java b/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_14_R1.java new file mode 100644 index 0000000..4a73b9f --- /dev/null +++ b/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_14_R1.java @@ -0,0 +1,248 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_14_R1 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_14_R1.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_14_R1.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_14_R1.sendMap(id, MapWrapper_v1_14_R1.this.content, player); + } else { + MapSender_v1_14_R1.addToQueue(id, MapWrapper_v1_14_R1.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_14_R1.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().defaultContainer.windowId; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.server.v1_14_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack); + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_14_R1.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().getEntity(entityId); + if (entity == null) return null; + + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.server.v1_14_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.server.v1_14_R1.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_14_R1.ItemStack>) getDeclaredField(EntityItemFrame.class, "ITEM"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + }; + + public MapWrapper_v1_14_R1(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_14_R1.java b/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_14_R1.java new file mode 100644 index 0000000..551a54b --- /dev/null +++ b/NMS-v1_14_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_14_R1.java @@ -0,0 +1,123 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class PacketListener_v1_14_R1 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap) { + PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet; + + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity) { + PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet; + + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action + EnumHand hand = packetPlayInUseEntity.c(); //hand + Vec3D pos = packetPlayInUseEntity.d(); //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot) { + PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet; + + int slot = packetPlayInSetCreativeSlot.b(); + ItemStack item = packetPlayInSetCreativeSlot.getItemStack(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline(); + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel; + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0); + + Vec3D vec3dObj = (Vec3D) vec3d; + return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z); + } +} diff --git a/NMS-v1_15_R1/pom.xml b/NMS-v1_15_R1/pom.xml new file mode 100644 index 0000000..4f6f9df --- /dev/null +++ b/NMS-v1_15_R1/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_15_R1</artifactId> + + <properties> + <NMSVersion>1.15.2-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>11</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_15_R1.java b/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_15_R1.java new file mode 100644 index 0000000..a7b83d9 --- /dev/null +++ b/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_15_R1.java @@ -0,0 +1,167 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_15_R1.PacketPlayOutMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The {@link MapSender_v1_15_R1} sends the Map packets to players. + */ +public class MapSender_v1_15_R1 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_15_R1() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Tracking position + false, //Locked + new ArrayList<>(), //Icons + content.array, //Data + content.minX, //X pos + content.minY, //Y pos + content.maxX, //X size (2nd X pos) + content.maxY //Y size (2nd Y pos) + ); + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + static final class QueuedMap { + private final int id; + private final ArrayImage image; + private final Player player; + + QueuedMap(int id, ArrayImage image, Player player) { + this.id = id; + this.image = image; + this.player = player; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (QueuedMap) obj; + return this.id == that.id && + Objects.equals(this.image, that.image) && + Objects.equals(this.player, that.player); + } + + @Override + public int hashCode() { + return Objects.hash(id, image, player); + } + + @Override + public String toString() { + return "QueuedMap[" + + "id=" + id + ", " + + "image=" + image + ", " + + "player=" + player + ']'; + } + } +} diff --git a/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_15_R1.java b/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_15_R1.java new file mode 100644 index 0000000..7308ace --- /dev/null +++ b/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_15_R1.java @@ -0,0 +1,249 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_15_R1 extends MapWrapper { + + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_15_R1.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_15_R1.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_15_R1.sendMap(id, MapWrapper_v1_15_R1.this.content, player); + } else { + MapSender_v1_15_R1.addToQueue(id, MapWrapper_v1_15_R1.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_15_R1.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().defaultContainer.windowId; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.server.v1_15_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack); + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_15_R1.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().getEntity(entityId); + if (entity == null) return null; + + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.server.v1_15_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.server.v1_15_R1.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_15_R1.ItemStack>) getDeclaredField(EntityItemFrame.class, "ITEM"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + }; + + public MapWrapper_v1_15_R1(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_15_R1.java b/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_15_R1.java new file mode 100644 index 0000000..a73dd04 --- /dev/null +++ b/NMS-v1_15_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_15_R1.java @@ -0,0 +1,123 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class PacketListener_v1_15_R1 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap) { + PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet; + + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity) { + PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet; + + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action + EnumHand hand = packetPlayInUseEntity.c(); //hand + Vec3D pos = packetPlayInUseEntity.d(); //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot) { + PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet; + + int slot = packetPlayInSetCreativeSlot.b(); + ItemStack item = packetPlayInSetCreativeSlot.getItemStack(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline(); + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel; + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0); + + Vec3D vec3dObj = (Vec3D) vec3d; + return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z); + } +} diff --git a/NMS-v1_16_R3/pom.xml b/NMS-v1_16_R3/pom.xml new file mode 100644 index 0000000..46edaa9 --- /dev/null +++ b/NMS-v1_16_R3/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_16_R3</artifactId> + + <properties> + <NMSVersion>1.16.4-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>11</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_16_R3.java b/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_16_R3.java new file mode 100644 index 0000000..e4ed5cf --- /dev/null +++ b/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_16_R3.java @@ -0,0 +1,167 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_16_R3.PacketPlayOutMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The {@link MapSender_v1_16_R3} sends the Map packets to players. + */ +public class MapSender_v1_16_R3 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_16_R3() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Tracking position + false, //Locked + new ArrayList<>(), //Icons + content.array, //Data + content.minX, //X pos + content.minY, //Y pos + content.maxX, //X size (2nd X pos) + content.maxY //Y size (2nd Y pos) + ); + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + static final class QueuedMap { + private final int id; + private final ArrayImage image; + private final Player player; + + QueuedMap(int id, ArrayImage image, Player player) { + this.id = id; + this.image = image; + this.player = player; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (QueuedMap) obj; + return this.id == that.id && + Objects.equals(this.image, that.image) && + Objects.equals(this.player, that.player); + } + + @Override + public int hashCode() { + return Objects.hash(id, image, player); + } + + @Override + public String toString() { + return "QueuedMap[" + + "id=" + id + ", " + + "image=" + image + ", " + + "player=" + player + ']'; + } + } +} diff --git a/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_16_R3.java b/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_16_R3.java new file mode 100644 index 0000000..492fd29 --- /dev/null +++ b/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_16_R3.java @@ -0,0 +1,248 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_16_R3 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_16_R3.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_16_R3.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_16_R3.sendMap(id, MapWrapper_v1_16_R3.this.content, player); + } else { + MapSender_v1_16_R3.addToQueue(id, MapWrapper_v1_16_R3.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_16_R3.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().defaultContainer.windowId; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.server.v1_16_R3.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack); + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_16_R3.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().getEntity(entityId); + if (entity == null) return null; + + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.server.v1_16_R3.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.server.v1_16_R3.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_16_R3.ItemStack>) getDeclaredField(EntityItemFrame.class, "ITEM"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + }; + + public MapWrapper_v1_16_R3(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_16_R3.java b/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_16_R3.java new file mode 100644 index 0000000..5f6fa98 --- /dev/null +++ b/NMS-v1_16_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_16_R3.java @@ -0,0 +1,123 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class PacketListener_v1_16_R3 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap) { + PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet; + + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity) { + PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet; + + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action + EnumHand hand = packetPlayInUseEntity.c(); //hand + Vec3D pos = packetPlayInUseEntity.d(); //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot) { + PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet; + + int slot = packetPlayInSetCreativeSlot.b(); + ItemStack item = packetPlayInSetCreativeSlot.getItemStack(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline(); + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel; + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0); + + Vec3D vec3dObj = (Vec3D) vec3d; + return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z); + } +} diff --git a/NMS-v1_17_R1/pom.xml b/NMS-v1_17_R1/pom.xml new file mode 100644 index 0000000..2608809 --- /dev/null +++ b/NMS-v1_17_R1/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ This file is part of MapReflectionAPI. + ~ Copyright (c) 2022-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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_17_R1</artifactId> + + <properties> + <NMSVersion>1.17.1-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>17</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_17_R1.java b/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_17_R1.java new file mode 100644 index 0000000..050df5b --- /dev/null +++ b/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_17_R1.java @@ -0,0 +1,138 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.level.saveddata.maps.WorldMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; + +/** + * The {@link MapSender_v1_17_R1} sends the Map packets to players. + */ +public class MapSender_v1_17_R1 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_17_R1() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + WorldMap.b updateData = new WorldMap.b( + 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 + ); + + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Show icons + new ArrayList<>(), //Icons + updateData + ); + + ((CraftPlayer) player).getHandle().b.sendPacket(packet); //connection send() + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + record QueuedMap(int id, ArrayImage image, Player player) { + } +} \ No newline at end of file diff --git a/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_17_R1.java b/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_17_R1.java new file mode 100644 index 0000000..8ba9201 --- /dev/null +++ b/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_17_R1.java @@ -0,0 +1,251 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; +import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.EntityItemFrame; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_17_R1 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_17_R1.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_17_R1.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_17_R1.sendMap(id, MapWrapper_v1_17_R1.this.content, player); + } else { + MapSender_v1_17_R1.addToQueue(id, MapWrapper_v1_17_R1.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_17_R1.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().bU.j; //inventoryMenu containerId + int stateId = craftPlayer.getHandle().bU.getStateId(); //inventoryMenu getStateId() + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack); + ((CraftPlayer) player).getHandle().b.sendPacket(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_17_R1.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().getEntity(entityId); + if (entity == null) return null; + + if (entity instanceof ItemFrame) return (ItemFrame) entity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.world.item.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.world.item.ItemStack>) getDeclaredField(EntityItemFrame.class, "ao"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().b.sendPacket(packet); + } + }; + + public MapWrapper_v1_17_R1(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} \ No newline at end of file diff --git a/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_17_R1.java b/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_17_R1.java new file mode 100644 index 0000000..f06f3f5 --- /dev/null +++ b/NMS-v1_17_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_17_R1.java @@ -0,0 +1,121 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot; +import net.minecraft.network.protocol.game.PacketPlayInUseEntity; +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.EnumHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3D; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; + +public class PacketListener_v1_17_R1 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap packetPlayOutMap) { + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) { + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action + Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type + EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand + Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) { + int slot = packetPlayInSetCreativeSlot.b(); + ItemStack item = packetPlayInSetCreativeSlot.getItemStack(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().b.a.k.pipeline(); //connection connection channel + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().b.a.k; //connection connection channel + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0); + return new Vector(vec3dObj.b, vec3dObj.c, vec3dObj.d); //x, y, z + } +} \ No newline at end of file diff --git a/NMS-v1_18_R2/pom.xml b/NMS-v1_18_R2/pom.xml new file mode 100644 index 0000000..5dfc581 --- /dev/null +++ b/NMS-v1_18_R2/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ This file is part of MapReflectionAPI. + ~ Copyright (c) 2022-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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_18_R2</artifactId> + + <properties> + <NMSVersion>1.18.2-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>17</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_18_R2.java b/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_18_R2.java new file mode 100644 index 0000000..ebe34fe --- /dev/null +++ b/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_18_R2.java @@ -0,0 +1,138 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.level.saveddata.maps.WorldMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; + +/** + * The {@link MapSender_v1_18_R2} sends the Map packets to players. + */ +public class MapSender_v1_18_R2 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_18_R2() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + WorldMap.b updateData = new WorldMap.b( + 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 + ); + + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Show icons + new ArrayList<>(), //Icons + updateData + ); + + ((CraftPlayer) player).getHandle().b.a(packet); //connection send() + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + record QueuedMap(int id, ArrayImage image, Player player) { + } +} \ No newline at end of file diff --git a/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_18_R2.java b/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_18_R2.java new file mode 100644 index 0000000..1ab2a22 --- /dev/null +++ b/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_18_R2.java @@ -0,0 +1,251 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; +import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.network.syncher.DataWatcherObject; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.EntityItemFrame; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField; +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField; + +public class MapWrapper_v1_18_R2 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_18_R2.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_18_R2.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_18_R2.sendMap(id, MapWrapper_v1_18_R2.this.content, player); + } else { + MapSender_v1_18_R2.addToQueue(id, MapWrapper_v1_18_R2.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_18_R2.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().bU.j; //inventoryMenu containerId + int stateId = craftPlayer.getHandle().bU.j(); //inventoryMenu getStateId() + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack); + ((CraftPlayer) player).getHandle().b.a(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_18_R2.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().a(entityId); + if (entity == null) return null; + + if (entity instanceof ItemFrame) return (ItemFrame) entity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.u().a("map", mapId); //getOrCreateTag putInt + + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true); + + try { + List<DataWatcher.Item<?>> list = new ArrayList<>(); + DataWatcherObject<net.minecraft.world.item.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.world.item.ItemStack>) getDeclaredField(EntityItemFrame.class, "ao"); + DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack); + list.add(dataWatcherItem); + setDeclaredField(packet, "b", list); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + ((CraftPlayer) player).getHandle().b.a(packet); + } + }; + + public MapWrapper_v1_18_R2(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_18_R2.java b/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_18_R2.java new file mode 100644 index 0000000..7524a29 --- /dev/null +++ b/NMS-v1_18_R2/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_18_R2.java @@ -0,0 +1,121 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot; +import net.minecraft.network.protocol.game.PacketPlayInUseEntity; +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.EnumHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3D; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; + +public class PacketListener_v1_18_R2 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap packetPlayOutMap) { + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) { + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action + Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type + EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand + Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) { + int slot = packetPlayInSetCreativeSlot.b(); + ItemStack item = packetPlayInSetCreativeSlot.c(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().b.a.m.pipeline(); //connection connection channel + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + Channel channel = ((CraftPlayer) p).getHandle().b.a.m; //connection connection channel + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0); + return new Vector(vec3dObj.b, vec3dObj.c, vec3dObj.d); //x, y, z + } +} diff --git a/NMS-v1_19_R3/pom.xml b/NMS-v1_19_R3/pom.xml new file mode 100644 index 0000000..08f0828 --- /dev/null +++ b/NMS-v1_19_R3/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ This file is part of MapReflectionAPI. + ~ Copyright (c) 2022-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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_19_R3</artifactId> + + <properties> + <NMSVersion>1.19.4-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>17</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_19_R3.java b/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_19_R3.java new file mode 100644 index 0000000..50d5bbd --- /dev/null +++ b/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_19_R3.java @@ -0,0 +1,138 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.level.saveddata.maps.WorldMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; + +/** + * The {@link MapSender_v1_19_R3} sends the Map packets to players. + */ +public class MapSender_v1_19_R3 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_19_R3() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + WorldMap.b updateData = new WorldMap.b( + 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 + ); + + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Show icons + new ArrayList<>(), //Icons + updateData + ); + + ((CraftPlayer) player).getHandle().b.a(packet); //connection send() + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + record QueuedMap(int id, ArrayImage image, Player player) { + } +} diff --git a/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_19_R3.java b/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_19_R3.java new file mode 100644 index 0000000..e0e0615 --- /dev/null +++ b/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_19_R3.java @@ -0,0 +1,239 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; +import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.EntityItemFrame; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +public class MapWrapper_v1_19_R3 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_19_R3.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_19_R3.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_19_R3.sendMap(id, MapWrapper_v1_19_R3.this.content, player); + } else { + MapSender_v1_19_R3.addToQueue(id, MapWrapper_v1_19_R3.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_19_R3.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().bO.j; //inventoryMenu containerId + int stateId = craftPlayer.getHandle().bO.j(); //inventoryMenu getStateId() + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack); + ((CraftPlayer) player).getHandle().b.a(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_19_R3.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().a(entityId); + if (entity == null) return null; + + if (entity instanceof ItemFrame) return (ItemFrame) entity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.v().a("map", mapId); //getOrCreateTag putInt + + List<DataWatcher.b<?>> list = new ArrayList<>(); + DataWatcher.b<?> dataWatcherItem = DataWatcher.b.a(EntityItemFrame.g, nmsStack); + list.add(dataWatcherItem); + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, list); + + ((CraftPlayer) player).getHandle().b.a(packet); + } + }; + + public MapWrapper_v1_19_R3(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_19_R3.java b/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_19_R3.java new file mode 100644 index 0000000..c437542 --- /dev/null +++ b/NMS-v1_19_R3/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_19_R3.java @@ -0,0 +1,126 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot; +import net.minecraft.network.protocol.game.PacketPlayInUseEntity; +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.EnumHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3D; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; + +public class PacketListener_v1_19_R3 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap packetPlayOutMap) { + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) { + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action + Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type + EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand + Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) { + int slot = packetPlayInSetCreativeSlot.a(); + ItemStack item = packetPlayInSetCreativeSlot.c(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + //The connection is private since 1.19.4 :| + NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().b, "h"); + ChannelPipeline pipeline = networkManager.m.pipeline(); //connection channel + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + //The connection is private since 1.19.4 :| + NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().b, "h"); + Channel channel = networkManager.m; //connection channel + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0); + return new Vector(vec3dObj.c, vec3dObj.d, vec3dObj.e); //x, y, z + } +} diff --git a/NMS-v1_20_R1/pom.xml b/NMS-v1_20_R1/pom.xml new file mode 100644 index 0000000..6f5cdde --- /dev/null +++ b/NMS-v1_20_R1/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ This file is part of MapReflectionAPI. + ~ Copyright (c) 2022-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/>. + --> + +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>MapReflectionAPI</artifactId> + <groupId>tech.sbdevelopment</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>MapReflectionAPI-NMS-v1_20_R1</artifactId> + + <properties> + <NMSVersion>1.20.1-R0.1-SNAPSHOT</NMSVersion> + <jdk.version>17</jdk.version> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <release>${jdk.version}</release> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.bukkit</groupId> + <artifactId>craftbukkit</artifactId> + <version>${NMSVersion}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>tech.sbdevelopment</groupId> + <artifactId>MapReflectionAPI-API</artifactId> + <version>${revision}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_20_R1.java b/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_20_R1.java new file mode 100644 index 0000000..3f523f7 --- /dev/null +++ b/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapSender_v1_20_R1.java @@ -0,0 +1,138 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.level.saveddata.maps.WorldMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; + +import java.util.ArrayList; +import java.util.List; + +/** + * The {@link MapSender_v1_20_R1} sends the Map packets to players. + */ +public class MapSender_v1_20_R1 { + private static final List<QueuedMap> sendQueue = new ArrayList<>(); + private static int senderID = -1; + + private MapSender_v1_20_R1() { + } + + /** + * Add a map to the send queue + * + * @param id The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void addToQueue(final int id, final ArrayImage content, final Player player) { + QueuedMap toSend = new QueuedMap(id, content, player); + if (sendQueue.contains(toSend)) return; + sendQueue.add(toSend); + + runSender(); + } + + /** + * Cancels a senderID in the sender queue + * + * @param s The senderID to cancel + */ + public static void cancelID(int s) { + sendQueue.removeIf(queuedMap -> queuedMap.id == s); + } + + /** + * Run the sender task + */ + private static void runSender() { + if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty()) + return; + + senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> { + if (sendQueue.isEmpty()) return; + + for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) { + QueuedMap current = sendQueue.get(0); + if (current == null) return; + + sendMap(current.id, current.image, current.player); + + if (!sendQueue.isEmpty()) sendQueue.remove(0); + } + }, 0, 2); + } + + /** + * Send a map to a player + * + * @param id0 The ID of the map + * @param content The {@link ArrayImage} to view on the map + * @param player The {@link Player} to view for + */ + public static void sendMap(final int id0, final ArrayImage content, final Player player) { + if (player == null || !player.isOnline()) { + List<QueuedMap> toRemove = new ArrayList<>(); + for (QueuedMap qMap : sendQueue) { + if (qMap == null) continue; + + if (qMap.player == null || !qMap.player.isOnline()) { + toRemove.add(qMap); + } + } + Bukkit.getScheduler().cancelTask(senderID); + sendQueue.removeAll(toRemove); + + return; + } + + final int id = -id0; + Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> { + try { + WorldMap.b updateData = new WorldMap.b( + 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 + ); + + PacketPlayOutMap packet = new PacketPlayOutMap( + id, //ID + (byte) 0, //Scale + false, //Show icons + new ArrayList<>(), //Icons + updateData + ); + + ((CraftPlayer) player).getHandle().c.a(packet); //connection send() + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + record QueuedMap(int id, ArrayImage image, Player player) { + } +} diff --git a/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_20_R1.java b/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_20_R1.java new file mode 100644 index 0000000..fcdf607 --- /dev/null +++ b/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/MapWrapper_v1_20_R1.java @@ -0,0 +1,239 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; +import net.minecraft.network.protocol.game.PacketPlayOutSetSlot; +import net.minecraft.network.syncher.DataWatcher; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.decoration.EntityItemFrame; +import org.bukkit.*; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; +import tech.sbdevelopment.mapreflectionapi.api.MapController; +import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; +import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; + +import java.util.*; + +public class MapWrapper_v1_20_R1 extends MapWrapper { + protected MapController controller = new MapController() { + private final Map<UUID, Integer> viewers = new HashMap<>(); + + @Override + public void addViewer(Player player) throws MapLimitExceededException { + if (!isViewing(player)) { + viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); + } + } + + @Override + public void removeViewer(OfflinePlayer player) { + viewers.remove(player.getUniqueId()); + } + + @Override + public void clearViewers() { + for (UUID uuid : viewers.keySet()) { + viewers.remove(uuid); + } + } + + @Override + public boolean isViewing(OfflinePlayer player) { + if (player == null) return false; + return viewers.containsKey(player.getUniqueId()); + } + + @Override + public int getMapId(OfflinePlayer player) { + if (isViewing(player)) { + return viewers.get(player.getUniqueId()); + } + return -1; + } + + @Override + public void update(ArrayImage content) { + MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); + if (duplicate != null) { + MapWrapper_v1_20_R1.this.content = duplicate.getContent(); + return; + } + + MapWrapper_v1_20_R1.this.content = content; + + for (UUID id : viewers.keySet()) { + sendContent(Bukkit.getPlayer(id)); + } + } + + @Override + public void sendContent(Player player) { + sendContent(player, false); + } + + @Override + public void sendContent(Player player, boolean withoutQueue) { + if (!isViewing(player)) return; + + int id = getMapId(player); + if (withoutQueue) { + MapSender_v1_20_R1.sendMap(id, MapWrapper_v1_20_R1.this.content, player); + } else { + MapSender_v1_20_R1.addToQueue(id, MapWrapper_v1_20_R1.this.content, player); + } + } + + @Override + public void cancelSend() { + for (int s : viewers.values()) { + MapSender_v1_20_R1.cancelID(s); + } + } + + @Override + public void showInInventory(Player player, int slot, boolean force) { + if (!isViewing(player)) return; + + if (player.getGameMode() == GameMode.CREATIVE && !force) return; + + if (slot < 9) { + slot += 36; + } else if (slot > 35 && slot != 45) { + slot = 8 - (slot - 36); + } + + CraftPlayer craftPlayer = (CraftPlayer) player; + int windowId = craftPlayer.getHandle().bQ.j; //inventoryMenu containerId + int stateId = craftPlayer.getHandle().bQ.j(); //inventoryMenu getStateId() + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack); + ((CraftPlayer) player).getHandle().c.a(packet); + } + + @Override + public void showInInventory(Player player, int slot) { + showInInventory(player, slot, false); + } + + @Override + public void showInHand(Player player, boolean force) { + if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return; + showInInventory(player, player.getInventory().getHeldItemSlot(), force); + } + + @Override + public void showInHand(Player player) { + showInHand(player, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame) { + showInFrame(player, frame, false); + } + + @Override + public void showInFrame(Player player, ItemFrame frame, boolean force) { + if (frame.getItem().getType() != Material.FILLED_MAP && !force) return; + showInFrame(player, frame.getEntityId()); + } + + @Override + public void showInFrame(Player player, int entityId) { + showInFrame(player, entityId, null); + } + + @Override + public void showInFrame(Player player, int entityId, String debugInfo) { + if (!isViewing(player)) return; + + ItemStack stack = new ItemStack(Material.FILLED_MAP, 1); + if (debugInfo != null) { + ItemMeta itemMeta = stack.getItemMeta(); + itemMeta.setDisplayName(debugInfo); + stack.setItemMeta(itemMeta); + } + + Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { + ItemFrame frame = getItemFrameById(player.getWorld(), entityId); + if (frame != null) { + frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance()); + frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_20_R1.this)); + } + + sendItemFramePacket(player, entityId, stack, getMapId(player)); + }); + } + + @Override + public void clearFrame(Player player, int entityId) { + + } + + @Override + public void clearFrame(Player player, ItemFrame frame) { + + } + + @Override + public ItemFrame getItemFrameById(World world, int entityId) { + CraftWorld craftWorld = (CraftWorld) world; + + Entity entity = craftWorld.getHandle().a(entityId); + if (entity == null) return null; + + if (entity instanceof ItemFrame) return (ItemFrame) entity; + + return null; + } + + private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + nmsStack.w().a("map", mapId); //getOrCreateTag putInt + + List<DataWatcher.b<?>> list = new ArrayList<>(); + DataWatcher.b<?> dataWatcherItem = DataWatcher.b.a(EntityItemFrame.g, nmsStack); + list.add(dataWatcherItem); + PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, list); + + ((CraftPlayer) player).getHandle().c.a(packet); + } + }; + + public MapWrapper_v1_20_R1(ArrayImage image) { + super(image); + } + + @Override + public MapController getController() { + return controller; + } +} diff --git a/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_20_R1.java b/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_20_R1.java new file mode 100644 index 0000000..a07cde1 --- /dev/null +++ b/NMS-v1_20_R1/src/main/java/tech/sbdevelopment/mapreflectionapi/nms/PacketListener_v1_20_R1.java @@ -0,0 +1,126 @@ +/* + * This file is part of MapReflectionAPI. + * Copyright (c) 2022-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.nms; + +import io.netty.channel.*; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot; +import net.minecraft.network.protocol.game.PacketPlayInUseEntity; +import net.minecraft.network.protocol.game.PacketPlayOutMap; +import net.minecraft.world.EnumHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3D; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; +import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; +import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; +import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; + +import java.util.concurrent.TimeUnit; + +import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; + +public class PacketListener_v1_20_R1 extends PacketListener { + @Override + protected void injectPlayer(Player p) { + ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + //On send packet + public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { + if (packet instanceof PacketPlayOutMap packetPlayOutMap) { + int id = (int) getDeclaredField(packetPlayOutMap, "a"); + + if (id < 0) { + //It's one of our maps, invert ID and let through! + int newId = -id; + setDeclaredField(packetPlayOutMap, "a", newId); //mapId + } else { + boolean async = !plugin.getServer().isPrimaryThread(); + MapCancelEvent event = new MapCancelEvent(p, id, async); + if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true); + if (event.getHandlers().getRegisteredListeners().length > 0) + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + } + } + + super.write(ctx, packet, promise); + } + + @Override + //On receive packet + public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { + if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) { + int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId + Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action + Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type + EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand + Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos + + if (Bukkit.getScheduler().callSyncMethod(plugin, () -> { + boolean async = !plugin.getServer().isPrimaryThread(); + MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); + if (event.getFrame() != null && event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + return event.isCancelled(); + } + return false; + }).get(1, TimeUnit.SECONDS)) return; + } else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) { + int slot = packetPlayInSetCreativeSlot.a(); + ItemStack item = packetPlayInSetCreativeSlot.c(); + + boolean async = !plugin.getServer().isPrimaryThread(); + CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async); + if (event.getMapWrapper() != null) { + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) return; + } + } + + super.channelRead(ctx, packet); + } + }; + + //The connection is private since 1.19.4 :| + NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().c, "h"); + ChannelPipeline pipeline = networkManager.m.pipeline(); //connection channel + pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler); + } + + @Override + public void removePlayer(Player p) { + //The connection is private since 1.19.4 :| + NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().c, "h"); + Channel channel = networkManager.m; //connection channel + channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName())); + } + + @Override + protected Vector vec3DToVector(Object vec3d) { + if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0); + return new Vector(vec3dObj.c, vec3dObj.d, vec3dObj.e); //x, y, z + } +} diff --git a/pom.xml b/pom.xml index 361046c..aa639dd 100644 --- a/pom.xml +++ b/pom.xml @@ -24,18 +24,33 @@ <groupId>tech.sbdevelopment</groupId> <artifactId>MapReflectionAPI</artifactId> - <version>1.4.4</version> - <packaging>jar</packaging> + <packaging>pom</packaging> + <version>${revision}</version> <name>MapReflectionAPI</name> <description>This API helps developer with viewing images on maps.</description> <url>https://sbdplugins.nl</url> <properties> + <revision>1.5</revision> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <maven.lombok.delombok-target>${project.build.directory}/javadoc-delombok</maven.lombok.delombok-target> + <jdk.version>11</jdk.version> </properties> + <modules> + <module>API</module> + <module>Dist</module> + <module>NMS-v1_20_R1</module> + <module>NMS-v1_19_R3</module> + <module>NMS-v1_18_R2</module> + <module>NMS-v1_17_R1</module> + <module>NMS-v1_16_R3</module> + <module>NMS-v1_15_R1</module> + <module>NMS-v1_14_R1</module> + <module>NMS-v1_13_R2</module> + <module>NMS-v1_12_R1</module> + </modules> + <distributionManagement> <repository> <id>sbdevelopment-repo</id> @@ -44,102 +59,46 @@ </distributionManagement> <build> + <defaultGoal>clean package</defaultGoal> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <version>3.10.1</version> - <configuration> - <release>11</release> - <annotationProcessorPaths> - <path> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>1.18.24</version> - </path> - </annotationProcessorPaths> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>3.3.0</version> + <artifactId>maven-toolchains-plugin</artifactId> + <version>3.1.0</version> <executions> <execution> - <phase>package</phase> <goals> - <goal>shade</goal> - </goals> - <configuration> - <createDependencyReducedPom>false</createDependencyReducedPom> - <relocations> - <relocation> - <pattern>com.bergerkiller.bukkit.common</pattern> - <shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bkcommonlib</shadedPattern> - </relocation> - <relocation> - <pattern>org.bstats</pattern> - <shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bstats</shadedPattern> - </relocation> - </relocations> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.projectlombok</groupId> - <artifactId>lombok-maven-plugin</artifactId> - <version>1.18.20.0</version> - <configuration> - <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> - <outputDirectory>${maven.lombok.delombok-target}</outputDirectory> - <addOutputDirectory>false</addOutputDirectory> - </configuration> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>delombok</goal> + <goal>toolchain</goal> </goals> </execution> </executions> + <configuration> + <toolchains> + <jdk> + <version>${jdk.version}</version> + </jdk> + </toolchains> + </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> - <version>3.4.0</version> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> <configuration> - <release>11</release> - <sourcepath>${maven.lombok.delombok-target}</sourcepath> - <sourceFileExcludes> - <sourceFileExclude>**/com/bergerkiller/bukkit/common/io/*.java</sourceFileExclude> - <sourceFileExclude>**/com/bergerkiller/bukkit/common/map/*.java</sourceFileExclude> - <sourceFileExclude>**/com/bergerkiller/bukkit/common/map/color/*.java</sourceFileExclude> - <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/*.java</sourceFileExclude> - <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/cmd/*.java</sourceFileExclude> - <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/managers/*.java</sourceFileExclude> - <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/utils/*.java</sourceFileExclude> - <sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/listeners/*.java</sourceFileExclude> - </sourceFileExcludes> + <release>${jdk.version}</release> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.1</version> + <configuration> + <skip>true</skip> </configuration> </plugin> </plugins> - <resources> - <resource> - <directory>src/main/resources</directory> - <filtering>true</filtering> - <includes> - <include>plugin.yml</include> - </includes> - </resource> - <resource> - <directory>src/main/resources</directory> - <excludes> - <exclude>plugin.yml</exclude> - </excludes> - </resource> - </resources> </build> <repositories> @@ -147,49 +106,13 @@ <id>spigot-repo</id> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> </repository> - <repository> - <id>MG-Dev Jenkins CI Maven Repository</id> - <url>https://ci.mg-dev.eu/plugin/repository/everything</url> - </repository> - <repository> - <id>dmulloy2-repo</id> - <url>https://repo.dmulloy2.net/repository/public/</url> - </repository> </repositories> <dependencies> <dependency> <groupId>org.spigotmc</groupId> <artifactId>spigot-api</artifactId> - <version>1.19.4-R0.1-SNAPSHOT</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>1.18.24</version> - <scope>provided</scope> - </dependency> - - <dependency> - <groupId>org.bstats</groupId> - <artifactId>bstats-bukkit</artifactId> - <version>3.0.0</version> - <scope>compile</scope> - </dependency> - - <!-- Libraries below are provided by Spigot --> - <dependency> - <groupId>org.jetbrains</groupId> - <artifactId>annotations-java5</artifactId> - <version>23.0.0</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>io.netty</groupId> - <artifactId>netty-transport</artifactId> - <version>4.1.77.Final</version> - <scope>provided</scope> + <version>1.20.1-R0.1-SNAPSHOT</version> </dependency> </dependencies> -</project> \ No newline at end of file +</project> diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java deleted file mode 100644 index 76d828a..0000000 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * This file is part of MapReflectionAPI. - * Copyright (c) 2022-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; - -import org.bukkit.*; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.metadata.FixedMetadataValue; -import org.jetbrains.annotations.NotNull; -import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; -import tech.sbdevelopment.mapreflectionapi.api.events.MapContentUpdateEvent; -import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; -import tech.sbdevelopment.mapreflectionapi.managers.Configuration; -import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * A {@link MapWrapper} wraps one image. - */ -public class MapWrapper extends AbstractMapWrapper { - private static final String REFERENCE_METADATA = "MAP_WRAPPER_REF"; - 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} - * - * @param image The {@link ArrayImage} to wrap - */ - public MapWrapper(ArrayImage image) { - this.content = image; - } - - private static final Class<?> craftStackClass = ReflectionUtil.getCraftClass("inventory.CraftItemStack"); - private static final Class<?> setSlotPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutSetSlot"); - private static final Class<?> entityClass = ReflectionUtil.getNMSClass("world.entity", "Entity"); - private static final Class<?> dataWatcherClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher"); - private static final Class<?> entityMetadataPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata"); - private static final Class<?> entityItemFrameClass = ReflectionUtil.getNMSClass("world.entity.decoration", "EntityItemFrame"); - private static final Class<?> dataWatcherItemClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$Item"); - - protected MapController controller = new MapController() { - private final Map<UUID, Integer> viewers = new HashMap<>(); - - @Override - public void addViewer(Player player) throws MapLimitExceededException { - if (!isViewing(player)) { - viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player)); - } - } - - @Override - public void removeViewer(OfflinePlayer player) { - viewers.remove(player.getUniqueId()); - } - - @Override - public void clearViewers() { - for (UUID uuid : viewers.keySet()) { - viewers.remove(uuid); - } - } - - @Override - public boolean isViewing(OfflinePlayer player) { - if (player == null) return false; - return viewers.containsKey(player.getUniqueId()); - } - - @Override - public int getMapId(OfflinePlayer player) { - if (isViewing(player)) { - return viewers.get(player.getUniqueId()); - } - return -1; - } - - @Override - public void update(@NotNull ArrayImage content) { - MapContentUpdateEvent event = new MapContentUpdateEvent(MapWrapper.this, content); - Bukkit.getPluginManager().callEvent(event); - - if (Configuration.getInstance().isImageCache()) { - MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content); - if (duplicate != null) { - MapWrapper.this.content = duplicate.getContent(); - return; - } - } - - MapWrapper.this.content = content; - - if (event.isSendContent()) { - for (UUID id : viewers.keySet()) { - sendContent(Bukkit.getPlayer(id)); - } - } - } - - @Override - public void sendContent(Player player) { - sendContent(player, false); - } - - @Override - public void sendContent(Player player, boolean withoutQueue) { - if (!isViewing(player)) return; - - int id = getMapId(player); - if (withoutQueue) { - MapSender.sendMap(id, MapWrapper.this.content, player); - } else { - MapSender.addToQueue(id, MapWrapper.this.content, player); - } - } - - @Override - public void cancelSend() { - for (int s : viewers.values()) { - MapSender.cancelID(s); - } - } - - @Override - public void showInInventory(Player player, int slot, boolean force) { - if (!isViewing(player)) return; - - if (player.getGameMode() == GameMode.CREATIVE && !force) return; - - if (slot < 9) { - slot += 36; - } else if (slot > 35 && slot != 45) { - slot = 8 - (slot - 36); - } - - String inventoryMenuName; - if (ReflectionUtil.supports(20)) { //1.20 - inventoryMenuName = "bQ"; - } else if (ReflectionUtil.supports(19)) { //1.19 - inventoryMenuName = ReflectionUtil.VER_MINOR == 3 ? "bO" : "bT"; //1.19.4 = bO, >= 1.19.3 = bT - } else if (ReflectionUtil.supports(18)) { //1.18 - inventoryMenuName = ReflectionUtil.VER_MINOR == 1 ? "bV" : "bU"; //1.18.1 = ap, 1.18(.2) = ao - } else if (ReflectionUtil.supports(17)) { //1.17, same as 1.18(.2) - inventoryMenuName = "bU"; - } else { //1.12-1.16 - inventoryMenuName = "defaultContainer"; - } - Object inventoryMenu = ReflectionUtil.getField(ReflectionUtil.getHandle(player), inventoryMenuName); - - ItemStack stack; - 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; - if (ReflectionUtil.supports(17)) { //1.17+ - int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, ReflectionUtil.supports(18) ? "j" : "getStateId"); - - packet = ReflectionUtil.callConstructor(setSlotPacketClass, - 0, //0 = Player inventory - stateId, - slot, - nmsStack - ); - } else { //1.16- - packet = ReflectionUtil.callConstructor(setSlotPacketClass, - 0, //0 = Player inventory - slot, - nmsStack - ); - } - - ReflectionUtil.sendPacketSync(player, packet); - } - - @Override - public void showInInventory(Player player, int slot) { - showInInventory(player, slot, false); - } - - @Override - public void showInHand(Player player, boolean force) { - if (player.getInventory().getItemInMainHand().getType() != MAP_MATERIAL && !force) - return; - - showInInventory(player, player.getInventory().getHeldItemSlot(), force); - } - - @Override - public void showInHand(Player player) { - showInHand(player, false); - } - - @Override - public void showInFrame(Player player, ItemFrame frame) { - showInFrame(player, frame, false); - } - - @Override - public void showInFrame(Player player, ItemFrame frame, boolean force) { - if (frame.getItem().getType() != MAP_MATERIAL && !force) - return; - - showInFrame(player, frame.getEntityId()); - } - - @Override - public void showInFrame(Player player, int entityId) { - showInFrame(player, entityId, null); - } - - @Override - public void showInFrame(Player player, int entityId, String debugInfo) { - if (!isViewing(player)) return; - - ItemStack stack = new ItemStack(MAP_MATERIAL, 1); - if (debugInfo != null) { - ItemMeta itemMeta = stack.getItemMeta(); - itemMeta.setDisplayName(debugInfo); - stack.setItemMeta(itemMeta); - } - - Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { - ItemFrame frame = getItemFrameById(player.getWorld(), entityId); - if (frame != null) { - frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance()); - frame.setMetadata(REFERENCE_METADATA, new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper.this)); - } - - sendItemFramePacket(player, entityId, stack, getMapId(player)); - }); - } - - @Override - public void clearFrame(Player player, int entityId) { - sendItemFramePacket(player, entityId, null, -1); - Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> { - ItemFrame frame = getItemFrameById(player.getWorld(), entityId); - if (frame != null) frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance()); - }); - } - - @Override - public void clearFrame(Player player, ItemFrame frame) { - clearFrame(player, frame.getEntityId()); - } - - @Override - public ItemFrame getItemFrameById(World world, int entityId) { - 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) { - if (mapId < 0) return null; - - Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack); - - if (ReflectionUtil.supports(13)) { - String nbtObjectName; - if (ReflectionUtil.supports(19)) { //1.19 - nbtObjectName = "v"; - } else if (ReflectionUtil.supports(18)) { //1.18 - nbtObjectName = ReflectionUtil.VER_MINOR == 1 ? "t" : "u"; //1.18.1 = t, 1.18(.2) = u - } else { //1.13 - 1.17 - nbtObjectName = "getOrCreateTag"; - } - Object nbtObject = ReflectionUtil.callMethod(nmsStack, nbtObjectName); - ReflectionUtil.callMethod(nbtObject, ReflectionUtil.supports(18) ? "a" : "setInt", "map", mapId); - } - return nmsStack; - } - - private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) { - Object nmsStack = createCraftItemStack(stack, mapId); - - String dataWatcherObjectName; - if (ReflectionUtil.supports(19)) { //1.19 - dataWatcherObjectName = ReflectionUtil.VER_MINOR == 3 ? "g" : "ao"; //1.19.4 = g, >= 1.19.3 = ao - } else if (ReflectionUtil.supports(18)) { //1.18 - dataWatcherObjectName = ReflectionUtil.VER_MINOR == 1 ? "ap" : "ao"; //1.18.1 = ap, 1.18(.2) = ao - } else if (ReflectionUtil.supports(17)) { //1.17 - dataWatcherObjectName = "ao"; - } else if (ReflectionUtil.supports(14)) { //1.14 - 1.16 - dataWatcherObjectName = "ITEM"; - } else if (ReflectionUtil.supports(13)) { //1.13 - dataWatcherObjectName = "e"; - } else { //1.12 - dataWatcherObjectName = "c"; - } - Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, dataWatcherObjectName); - - ReflectionUtil.ListParam list = new ReflectionUtil.ListParam<>(); - - Object packet; - if (ReflectionUtil.supports(19, 2)) { //1.19.3 - Class<?> dataWatcherRecordClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$b"); - // Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter - Object dataWatcherItem; - try { - Method m = dataWatcherRecordClass.getMethod("a", dataWatcherObject.getClass(), Object.class); - m.setAccessible(true); - dataWatcherItem = m.invoke(null, dataWatcherObject, nmsStack); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); - return; - } - list.add(dataWatcherItem); - - packet = ReflectionUtil.callConstructor(entityMetadataPacketClass, - entityId, - list - ); - } else { //1.19.2 or lower - Object dataWatcher = ReflectionUtil.callConstructorNull(dataWatcherClass, entityClass); - - packet = ReflectionUtil.callConstructor(entityMetadataPacketClass, - entityId, - dataWatcher, //dummy watcher! - true - ); - - Object dataWatcherItem = ReflectionUtil.callFirstConstructor(dataWatcherItemClass, dataWatcherObject, nmsStack); - list.add(dataWatcherItem); - ReflectionUtil.setDeclaredField(packet, "b", list); - } - - ReflectionUtil.sendPacketSync(player, packet); - } - }; - - public ArrayImage getContent() { - return content; - } - - @Override - public MapController getController() { - return controller; - } -} diff --git a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java b/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java deleted file mode 100644 index 3785599..0000000 --- a/src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * This file is part of MapReflectionAPI. - * Copyright (c) 2022-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.listeners; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.util.Vector; -import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; -import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent; -import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent; -import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent; -import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil; - -import java.util.concurrent.TimeUnit; - -import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*; - -public class PacketListener implements Listener { - private static final Class<?> packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap"); - private static final Class<?> packetPlayInUseEntityClass = getNMSClass("network.protocol.game", "PacketPlayInUseEntity"); - private static final Class<?> packetPlayInSetCreativeSlotClass = getNMSClass("network.protocol.game", "PacketPlayInSetCreativeSlot"); - private static final Class<?> vec3DClass = getNMSClass("world.phys", "Vec3D"); - private static final Class<?> craftStackClass = getCraftClass("inventory.CraftItemStack"); - - @EventHandler - public void onJoin(PlayerJoinEvent e) { - injectPlayer(e.getPlayer()); - } - - @EventHandler - public void onQuit(PlayerQuitEvent e) { - removePlayer(e.getPlayer()); - } - - private void injectPlayer(Player player) { - ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { - @Override - public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception { - if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) { - Object packetPlayOutMap = packetPlayOutMapClass.cast(packet); - - int id = (int) getDeclaredField(packetPlayOutMap, "a"); - if (id < 0) { - int newId = -id; - setDeclaredField(packetPlayOutMap, "a", newId); - } else { - boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); - MapCancelEvent event = new MapCancelEvent(player, id, async); - if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true); - if (event.getHandlers().getRegisteredListeners().length > 0) - Bukkit.getPluginManager().callEvent(event); - - if (event.isCancelled()) return; - } - } - - super.write(ctx, packet, promise); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception { - if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) { - Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet); - - int entityId = (int) getDeclaredField(packetPlayInEntity, "a"); - - Enum<?> actionEnum; - Enum<?> hand; - Object pos; - if (ReflectionUtil.supports(17)) { - Object action = getDeclaredField(packetPlayInEntity, "b"); - actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type - hand = hasField(action, "a") ? (Enum<?>) getDeclaredField(action, "a") : null; - pos = hasField(action, "b") ? getDeclaredField(action, "b") : null; - } else { - actionEnum = (Enum<?>) callDeclaredMethod(packetPlayInEntity, ReflectionUtil.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 - pos = callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "d" : "c"); //1.13 = d, 1.12 = c - } - - if (Bukkit.getScheduler().callSyncMethod(MapReflectionAPI.getInstance(), () -> { - boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); - MapInteractEvent event = new MapInteractEvent(player, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async); - if (event.getFrame() != null && event.getMapWrapper() != null) { - Bukkit.getPluginManager().callEvent(event); - return event.isCancelled(); - } - return false; - }).get(1, TimeUnit.SECONDS)) return; - } else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) { - 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 - Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, ReflectionUtil.supports(18) ? "c" : "getItemStack"); //1.18 = c, 1.17 = getItemStack - ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack); - - boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread(); - CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(player, slot, craftStack, async); - if (event.getMapWrapper() != null) { - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) return; - } - } - - super.channelRead(ctx, packet); - } - }; - - Channel channel = getChannel(player); - channel.pipeline().addBefore("packet_handler", player.getName(), channelDuplexHandler); - } - - private void removePlayer(Player player) { - Channel channel = getChannel(player); - channel.eventLoop().submit(() -> channel.pipeline().remove(player.getName())); - } - - private Channel getChannel(Player player) { - Object playerHandle = getHandle(player); - Object playerConnection = getDeclaredField(playerHandle, ReflectionUtil.supports(20) ? "c" : ReflectionUtil.supports(17) ? "b" : "playerConnection"); //1.20 = c, 1.17-1.19 = b, 1.16 = playerConnection - 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) { - if (!(vec3d.getClass().isAssignableFrom(vec3DClass))) return new Vector(0, 0, 0); - - 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 y = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "d" : ReflectionUtil.supports(17) ? "c" : "y"); //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 - - return new Vector(x, y, z); - } -}