Compare commits

..

44 commits

Author SHA1 Message Date
14a6c330d2 v1.6.6: 1.21.5 support (#50)
Reviewed-on: #50
Co-authored-by: SBDeveloper <support@sbdevelopment.tech>
Co-committed-by: SBDeveloper <support@sbdevelopment.tech>
2025-03-27 19:55:31 +01:00
aaecbf447d Delete .github/dependabot.yml 2025-03-27 19:54:36 +01:00
e142050b7a Delete .github/workflows/maven.yml 2025-03-27 19:54:31 +01:00
Stijn Bannink
cc59b911a5
Merge pull request #36 from SBDPlugins/dependabot/maven/org.bstats-bstats-bukkit-3.1.0
Bump org.bstats:bstats-bukkit from 3.0.2 to 3.1.0
2024-11-07 19:44:14 +01:00
Stijn Bannink
7651be3f14
Merge pull request #38 from SBDPlugins/dependabot/maven/io.netty-netty-transport-4.1.114.Final
Bump io.netty:netty-transport from 4.1.97.Final to 4.1.114.Final
2024-11-07 19:44:05 +01:00
Stijn Bannink
c74684a7a8
Merge pull request #39 from SBDPlugins/dependabot/maven/com.github.cryptomorin-XSeries-11.3.0
Bump com.github.cryptomorin:XSeries from 11.2.0 to 11.3.0
2024-11-07 19:43:57 +01:00
Stijn Bannink
0c61f5c517
Merge pull request #40 from SBDPlugins/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.11.1
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.7.0 to 3.11.1
2024-11-07 19:43:45 +01:00
dependabot[bot]
fa5e26ffd8
Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.7.0 to 3.11.1
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.7.0 to 3.11.1.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.7.0...maven-javadoc-plugin-3.11.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 16:29:25 +00:00
dependabot[bot]
7e392eb0ec
Bump com.github.cryptomorin:XSeries from 11.2.0 to 11.3.0
Bumps [com.github.cryptomorin:XSeries](https://github.com/CryptoMorin/XSeries) from 11.2.0 to 11.3.0.
- [Release notes](https://github.com/CryptoMorin/XSeries/releases)
- [Commits](https://github.com/CryptoMorin/XSeries/compare/v11.2.0...v11.3.0)

---
updated-dependencies:
- dependency-name: com.github.cryptomorin:XSeries
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-03 16:50:37 +00:00
dependabot[bot]
48762f9703
Bump io.netty:netty-transport from 4.1.97.Final to 4.1.114.Final
Bumps [io.netty:netty-transport](https://github.com/netty/netty) from 4.1.97.Final to 4.1.114.Final.
- [Commits](https://github.com/netty/netty/compare/netty-4.1.97.Final...netty-4.1.114.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-transport
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-03 16:50:30 +00:00
dependabot[bot]
59d4f84625
Bump org.bstats:bstats-bukkit from 3.0.2 to 3.1.0
Bumps [org.bstats:bstats-bukkit](https://github.com/Bastian/bStats-Metrics) from 3.0.2 to 3.1.0.
- [Release notes](https://github.com/Bastian/bStats-Metrics/releases)
- [Commits](https://github.com/Bastian/bStats-Metrics/compare/v3.0.2...v3.1.0)

---
updated-dependencies:
- dependency-name: org.bstats:bstats-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 16:54:17 +00:00
Stijn Bannink
c2074e4504
Update README.md 2024-06-30 22:56:04 +02:00
Stijn Bannink
7586ba5502
Merge pull request #30 from SBDPlugins/development
v1.6.4: Added support for 1.20.5, 1.20.6 and 1.21
2024-06-30 22:46:01 +02:00
3f382583a7
Fixed all stupid NMS changes for 1.20.6+ 2024-06-30 22:37:24 +02:00
bd6a24a242
Bumped XSeries, moved to new reflection API of XSeries 2024-06-30 20:49:09 +02:00
603f144d26
Merge remote-tracking branch 'origin/development' into development
# Conflicts:
#	.idea/misc.xml
#	pom.xml
#	src/main/java/tech/sbdevelopment/mapreflectionapi/api/MapWrapper.java
#	src/main/java/tech/sbdevelopment/mapreflectionapi/listeners/PacketListener.java
#	src/main/java/tech/sbdevelopment/mapreflectionapi/utils/XMaterial.java
2024-06-30 20:29:50 +02:00
445dc1d2e9
Added 1.21 support (untested) 2024-06-30 20:26:53 +02:00
Stijn Bannink
cba7cbf6e5 Fixed compilation 2024-04-29 18:11:13 +02:00
Stijn Bannink
f25c727a15 Added 1.20.5 support finished 2024-04-29 18:01:28 +02:00
Stijn Bannink
ef49048ee1 Started with 1.20.5 support 2024-04-29 16:50:10 +02:00
Stijn Bannink
89d4234a8c Cleanup 2023-12-24 15:39:25 +01:00
Stijn Bannink
b5df2bb80c Fixed multiple events, still WIP for Interact event 2023-12-24 15:38:40 +01:00
Stijn Bannink
5557a76976 Fixed typo in event name 2023-12-22 20:45:36 +01:00
Stijn Bannink
89cbc9b8be Added Spigot 1.20.4 support 2023-12-22 20:37:28 +01:00
Stijn Bannink
8206cb403b Added render distance info to README 2023-12-09 20:19:49 +01:00
Stijn Bannink
293c239fb8 v1.6.2: More version fixes (1.20.2 and 1.19.2) 2023-11-13 21:30:32 +01:00
Stijn Bannink
2af12469be
Update README.md 2023-10-23 19:05:25 +02:00
Stijn Bannink
74635f22e3
Merge pull request #29 from StanByes/master
Patch reflection item getter at PacketPlayInSetCreativeSlot
2023-10-23 19:04:19 +02:00
StanByes
eff2fb016f Patch reflection item getter at PacketPlayInSetCreativeSlot 2023-10-23 18:52:14 +02:00
Stijn Bannink
886fba1822
Merge pull request #28 from StanByes/master
Patch reflection channel getter at PlayerJoinEvent (1.20.2)
2023-10-23 13:26:33 +02:00
StanByes
e9039eb56d Patch reflection channel getter at PlayerJoinEvent 2023-10-23 13:18:40 +02:00
Stijn Bannink
a5e7a4afdc Updated deps 2023-10-02 13:15:55 +02:00
Stijn Bannink
fa08e1b5ff Bumped to 1.20.2 2023-09-22 16:37:43 +02:00
Stijn Bannink
d7429c301e
Merge pull request #25 from SBDPlugins/dependabot/maven/org.bstats-bstats-bukkit-3.0.2
⬆️ Bump org.bstats:bstats-bukkit from 3.0.0 to 3.0.2
2023-09-22 14:37:59 +02:00
dependabot[bot]
8a2daf1580
⬆️ Bump org.bstats:bstats-bukkit from 3.0.0 to 3.0.2
Bumps [org.bstats:bstats-bukkit](https://github.com/Bastian/bStats-Metrics) from 3.0.0 to 3.0.2.
- [Release notes](https://github.com/Bastian/bStats-Metrics/releases)
- [Commits](https://github.com/Bastian/bStats-Metrics/compare/v3.0.0...v3.0.2)

---
updated-dependencies:
- dependency-name: org.bstats:bstats-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-22 12:37:50 +00:00
Stijn Bannink
4bb68cc5d2
Create dependabot.yml 2023-09-22 14:37:01 +02:00
Stijn Bannink
616e215797 Implemented Reflection caching, closes #8 2023-08-13 12:44:39 +02:00
Stijn Bannink
8d11483afe Fixed multi example for v1.6 2023-08-12 21:41:11 +02:00
Stijn Bannink
31ff370dc0 Bumped to v1.6 2023-08-12 21:35:52 +02:00
Stijn Bannink
19fdd6ff66 Merge remote-tracking branch 'origin/legacy/reflection' into legacy/reflection 2023-08-12 21:35:24 +02:00
Stijn Bannink
63ac9bb38e [BREAKING]: Improved [row][col] naming; now always accepts [row][col], breaks old multi-map plugins 2023-08-12 21:35:18 +02:00
Stijn Bannink
a5acef0666
Fixed typo in README 2023-08-10 19:32:12 +02:00
Stijn Bannink
c390ebbd5e Hotfix: Fixed supports() function detecting invalid major/minor version combination for newer versions; closes #22 2023-08-09 10:48:37 +02:00
Stijn Bannink
15640e5886 Hotfix for the show in hand feature, closes #20 2023-07-05 19:36:41 +02:00
110 changed files with 3979 additions and 8975 deletions

1
.gitignore vendored
View file

@ -86,7 +86,6 @@ buildNumber.properties
.mvn/timing.properties .mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar # https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar .mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# Eclipse m2e generated files # Eclipse m2e generated files
# Eclipse Core # Eclipse Core

View file

@ -1,6 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value="This file is part of &amp;#36;project.name.&#10;Copyright (c) &amp;#36;originalComment.match(&quot;Copyright \(c\) (\d+)&quot;, 1, &quot;-&quot;, &quot;&amp;#36;today.year&quot;)&amp;#36;today.year inventivetalent / SBDevelopment - All Rights Reserved&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;." /> <option name="notice" value="This file is part of &amp;#36;project.name.&#10;Copyright (c) &amp;#36;originalComment.match(&quot;Copyright \(c\) (\d+)&quot;, 1, &quot;-&quot;, &quot;&amp;#36;today.year&quot;)&amp;#36;today.year inventivetalent / SBDevelopment - All Rights Reserved&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;." />
<option name="myName" value="SBDevelopment" /> <option name="myName" value="SBDevelopment" />
</copyright> </copyright>
</component> </component>

View file

@ -1,7 +0,0 @@
<component name="CopyrightManager">
<settings default="SBDevelopment">
<module2copyright>
<element module="Project Files" copyright="SBDevelopment" />
</module2copyright>
</settings>
</component>

8
.idea/discord.xml generated
View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DiscordProjectSettings"> <component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" /> <option name="show" value="PROJECT_FILES" />
<option name="description" value="" /> <option name="description" value="" />
</component> </component>
</project> </project>

20
.idea/encodings.xml generated
View file

@ -1,10 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Encoding"> <component name="Encoding">
<file url="file://$PROJECT_DIR$/${project.basedir}/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/${project.basedir}/src/main/resources" charset="UTF-8" />
<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/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Dist/src/main/resources" 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" /> <file url="file://$PROJECT_DIR$/NMS-v1_12_R1/src/main/java" charset="UTF-8" />
@ -21,20 +17,8 @@
<file url="file://$PROJECT_DIR$/NMS-v1_17_R1/src/main/resources" charset="UTF-8" /> <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/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_18_R2/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_R1/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_19_R1/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" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R2/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R2/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R3/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R3/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R4/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R4/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_21_R1/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_21_R1/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_21_R2/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_21_R2/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" 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$/src/main/resources" charset="UTF-8" />
</component> </component>

16
.idea/misc.xml generated
View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ComposerSettings">
<execution />
</component>
<component name="EntryPointsManager"> <component name="EntryPointsManager">
<list size="1"> <list size="1">
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" /> <item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
@ -14,11 +17,20 @@
</option> </option>
<option name="ignoredFiles"> <option name="ignoredFiles">
<set> <set>
<option value="$PROJECT_DIR$/NMS-v1_20_R1/pom.xml" /> <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> </set>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="temurin-11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

12
.idea/ros.xml generated
View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ROSSettings">
<option name="rosPath" value="" />
<option name="workspacePath" value="" />
<option name="additionalSources" value="" />
<option name="excludedXmls" value="" />
<option name="licenseLinkType" value="Summary" />
<option name="knownKeys" value="" />
<option name="depSources" value="https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/base.yaml&quot;https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/python.yaml&quot;https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/ruby.yaml" />
</component>
</project>

View file

@ -1,178 +0,0 @@
<?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>

View file

@ -1,80 +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 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;
}
}
}

View file

@ -1,537 +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.utils;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CompletableFuture;
/**
* <b>ReflectionUtil</b> - Reflection handler for NMS and CraftBukkit.<br>
* Caches the packet related methods and is asynchronous.
* <p>
* This class does not handle null checks as most of the requests are from the
* other utility classes that already handle null checks.
* <p>
* <a href="https://wiki.vg/Protocol">Clientbound Packets</a> are considered fake
* updates to the client without changing the actual data. Since all the data is handled
* by the server.
*
* @author Crypto Morin, Stijn Bannink
* @version 2.1
*/
public class ReflectionUtil {
/**
* We use reflection mainly to avoid writing a new class for version barrier.
* The version barrier is for NMS that uses the Minecraft version as the main package name.
* <p>
* E.g. EntityPlayer in 1.15 is in the class {@code net.minecraft.server.v1_15_R1}
* but in 1.14 it's in {@code net.minecraft.server.v1_14_R1}
* In order to maintain cross-version compatibility we cannot import these classes.
* <p>
* Performance is not a concern for these specific statically initialized values.
*/
public static final String VERSION;
static { // This needs to be right below VERSION because of initialization order.
// This package loop is used to avoid implementation-dependant strings like Bukkit.getVersion() or Bukkit.getBukkitVersion()
// which allows easier testing as well.
String found = null;
for (Package pack : Package.getPackages()) {
String name = pack.getName();
// .v because there are other packages.
if (name.startsWith("org.bukkit.craftbukkit.v")) {
found = pack.getName().split("\\.")[3];
// Just a final guard to make sure it finds this important class.
// As a protection for forge+bukkit implementation that tend to mix versions.
// The real CraftPlayer should exist in the package.
// Note: Doesn't seem to function properly. Will need to separate the version
// handler for NMS and CraftBukkit for softwares like catmc.
try {
Class.forName("org.bukkit.craftbukkit." + found + ".entity.CraftPlayer");
break;
} catch (ClassNotFoundException e) {
found = null;
}
}
}
if (found == null)
throw new IllegalArgumentException("Failed to parse server version. Could not find any package starting with name: 'org.bukkit.craftbukkit.v'");
VERSION = found;
}
/**
* The raw minor version number.
* E.g. {@code v1_17_R1} to {@code 17}
*
* @since 4.0.0
*/
public static final int VER = Integer.parseInt(VERSION.substring(1).split("_")[1]);
/**
* The raw minor version number.
* E.g. {@code v1_18_R2} to {@code 2}
*
* @since 4.0.0
*/
public static final int VER_MINOR = toInt(VERSION.substring(1).split("_")[2].substring(1), 0);
/**
* Mojang remapped their NMS in 1.17 https://www.spigotmc.org/threads/spigot-bungeecord-1-17.510208/#post-4184317
*/
public static final String
CRAFTBUKKIT = "org.bukkit.craftbukkit." + VERSION + '.',
NMS = v(17, "net.minecraft.").orElse("net.minecraft.server." + VERSION + '.');
/**
* A nullable public accessible field only available in {@code EntityPlayer}.
* This can be null if the player is offline.
*/
private static final MethodHandle PLAYER_CONNECTION;
/**
* Responsible for getting the NMS handler {@code EntityPlayer} object for the player.
* {@code CraftPlayer} is simply a wrapper for {@code EntityPlayer}.
* Used mainly for handling packet related operations.
* <p>
* This is also where the famous player {@code ping} field comes from!
*/
private static final MethodHandle GET_HANDLE;
private static final MethodHandle GET_HANDLE_WORLD;
/**
* Sends a packet to the player's client through a {@code NetworkManager} which
* is where {@code ProtocolLib} controls packets by injecting channels!
*/
private static final MethodHandle SEND_PACKET;
static {
Class<?> entityPlayer = getNMSClass("server.level", "EntityPlayer");
Class<?> worldServer = getNMSClass("server.level", "WorldServer");
Class<?> craftPlayer = getCraftClass("entity.CraftPlayer");
Class<?> craftWorld = getCraftClass("CraftWorld");
Class<?> playerConnection = getNMSClass("server.network", "PlayerConnection");
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle sendPacket = null;
MethodHandle getHandle = null;
MethodHandle getHandleWorld = null;
MethodHandle connection = null;
try {
connection = lookup.findGetter(entityPlayer,
supports(20) ? "c" : supports(17) ? "b" : "playerConnection", playerConnection);
getHandle = lookup.findVirtual(craftPlayer, "getHandle", MethodType.methodType(entityPlayer));
getHandleWorld = lookup.findVirtual(craftWorld, "getHandle", MethodType.methodType(worldServer));
sendPacket = lookup.findVirtual(playerConnection,
v(18, "a").orElse("sendPacket"),
MethodType.methodType(void.class, getNMSClass("network.protocol", "Packet")));
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
PLAYER_CONNECTION = connection;
SEND_PACKET = sendPacket;
GET_HANDLE = getHandle;
GET_HANDLE_WORLD = getHandleWorld;
}
private ReflectionUtil() {
}
/**
* This method is purely for readability.
* No performance is gained.
*
* @since 5.0.0
*/
public static <T> VersionHandler<T> v(int version, T handle) {
return new VersionHandler<>(version, handle);
}
/**
* Checks whether the server version is equal or greater than the given version.
*
* @param version the version to compare the server version with.
* @return true if the version is equal or newer, otherwise false.
* @since 4.0.0
*/
public static boolean supports(int version) {
return VER >= version;
}
/**
* Checks whether the server version is equal or greater than the given version.
* <p>
* PAY ATTENTION! The minor version is based on the NMS version.
* This means that v1_19_R3 has major version 19 and minor version 3.
*
* @param major the major version to compare the server version with.
* @param minor the minor version to compare the server version with.
* @return true if the version is equal or newer, otherwise false.
* @since 4.0.0
*/
public static boolean supports(int major, int minor) {
return VER >= major && VER_MINOR >= minor;
}
/**
* Helper class converted to {@link List}
*
* @param <E> The storage type
*/
public static class ListParam<E> extends ArrayList<E> {
}
/**
* Helper class converted to {@link Collection}
*
* @param <E> The storage type
*/
public static class CollectionParam<E> extends ArrayList<E> {
}
private static Class<?> wrapperToPrimitive(Class<?> clazz) {
if (clazz == Boolean.class) return boolean.class;
if (clazz == Integer.class) return int.class;
if (clazz == Double.class) return double.class;
if (clazz == Float.class) return float.class;
if (clazz == Long.class) return long.class;
if (clazz == Short.class) return short.class;
if (clazz == Byte.class) return byte.class;
if (clazz == Void.class) return void.class;
if (clazz == Character.class) return char.class;
if (clazz == CollectionParam.class) return Collection.class;
if (clazz == ListParam.class) return List.class;
if (clazz == ArrayList.class) return Collection.class; //LEGACY!
if (clazz == HashMap.class) return Map.class;
return clazz;
}
private static Class<?>[] toParamTypes(Object... params) {
return Arrays.stream(params)
.map(obj -> obj != null ? wrapperToPrimitive(obj.getClass()) : null)
.toArray(Class<?>[]::new);
}
@Nullable
public static Class<?> getClass(@NotNull String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callConstructorNull(Class<?> clazz, Class<?> paramClass) {
try {
Constructor<?> con = clazz.getConstructor(paramClass);
con.setAccessible(true);
return con.newInstance(clazz.cast(null));
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callFirstConstructor(Class<?> clazz, Object... params) {
try {
Constructor<?> con = clazz.getConstructors()[0];
con.setAccessible(true);
return con.newInstance(params);
} catch (IllegalAccessException | InstantiationException |
InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callConstructor(Class<?> clazz, Object... params) {
try {
Constructor<?> con = clazz.getConstructor(toParamTypes(params));
con.setAccessible(true);
return con.newInstance(params);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callDeclaredConstructor(Class<?> clazz, Object... params) {
try {
Constructor<?> con = clazz.getDeclaredConstructor(toParamTypes(params));
con.setAccessible(true);
return con.newInstance(params);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callMethod(Class<?> clazz, String method, Object... params) {
try {
Method m = clazz.getMethod(method, toParamTypes(params));
m.setAccessible(true);
return m.invoke(null, params);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callMethod(Object obj, String method, Object... params) {
try {
Method m = obj.getClass().getMethod(method, toParamTypes(params));
m.setAccessible(true);
return m.invoke(obj, params);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object callDeclaredMethod(Object obj, String method, Object... params) {
try {
Method m = obj.getClass().getDeclaredMethod(method, toParamTypes(params));
m.setAccessible(true);
return m.invoke(obj, params);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
}
public static boolean hasField(Object packet, String field) {
try {
packet.getClass().getDeclaredField(field);
return true;
} catch (NoSuchFieldException ex) {
return false;
}
}
@Nullable
public static Object getField(Object object, String field) {
try {
Field f = object.getClass().getField(field);
f.setAccessible(true);
return f.get(object);
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object getDeclaredField(Class<?> clazz, String field) {
try {
Field f = clazz.getDeclaredField(field);
f.setAccessible(true);
return f.get(null);
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
return null;
}
}
@Nullable
public static Object getDeclaredField(Object object, String field) {
try {
Field f = object.getClass().getDeclaredField(field);
f.setAccessible(true);
return f.get(object);
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
return null;
}
}
public static void setDeclaredField(Object object, String field, Object value) {
try {
Field f = object.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(object, value);
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
}
/**
* Get a NMS (net.minecraft.server) class which accepts a package for 1.17 compatibility.
*
* @param newPackage the 1.17 package name.
* @param name the name of the class.
* @return the NMS class or null if not found.
* @since 4.0.0
*/
@javax.annotation.Nullable
public static Class<?> getNMSClass(@Nonnull String newPackage, @Nonnull String name) {
if (supports(17)) name = newPackage + '.' + name;
return getNMSClass(name);
}
/**
* Get a NMS (net.minecraft.server) class.
*
* @param name the name of the class.
* @return the NMS class or null if not found.
* @since 1.0.0
*/
@javax.annotation.Nullable
public static Class<?> getNMSClass(@Nonnull String name) {
try {
return Class.forName(NMS + name);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}
/**
* Sends a packet to the player asynchronously if they're online.
* Packets are thread-safe.
*
* @param player the player to send the packet to.
* @param packets the packets to send.
* @return the async thread handling the packet.
* @see #sendPacketSync(Player, Object...)
* @since 1.0.0
*/
@Nonnull
public static CompletableFuture<Void> sendPacket(@Nonnull Player player, @Nonnull Object... packets) {
return CompletableFuture.runAsync(() -> sendPacketSync(player, packets))
.exceptionally(ex -> {
ex.printStackTrace();
return null;
});
}
/**
* Sends a packet to the player synchronously if they're online.
*
* @param player the player to send the packet to.
* @param packets the packets to send.
* @see #sendPacket(Player, Object...)
* @since 2.0.0
*/
public static void sendPacketSync(@Nonnull Player player, @Nonnull Object... packets) {
try {
Object handle = GET_HANDLE.invoke(player);
Object connection = PLAYER_CONNECTION.invoke(handle);
// Checking if the connection is not null is enough. There is no need to check if the player is online.
if (connection != null) {
for (Object packet : packets) SEND_PACKET.invoke(connection, packet);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@javax.annotation.Nullable
public static Object getHandle(@Nonnull Player player) {
Objects.requireNonNull(player, "Cannot get handle of null player");
try {
return GET_HANDLE.invoke(player);
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
@javax.annotation.Nullable
public static Object getHandle(@Nonnull World world) {
Objects.requireNonNull(world, "Cannot get handle of null world");
try {
return GET_HANDLE_WORLD.invoke(world);
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
/**
* Get a CraftBukkit (org.bukkit.craftbukkit) class.
*
* @param name the name of the class to load.
* @return the CraftBukkit class or null if not found.
* @since 1.0.0
*/
@javax.annotation.Nullable
public static Class<?> getCraftClass(@Nonnull String name) {
try {
return Class.forName(CRAFTBUKKIT + name);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
return null;
}
}
public static final class VersionHandler<T> {
private int version;
private T handle;
private VersionHandler(int version, T handle) {
if (supports(version)) {
this.version = version;
this.handle = handle;
}
}
public VersionHandler<T> v(int version, T handle) {
if (version == this.version)
throw new IllegalArgumentException("Cannot have duplicate version handles for version: " + version);
if (version > this.version && supports(version)) {
this.version = version;
this.handle = handle;
}
return this;
}
public T orElse(T handle) {
return this.version == 0 ? handle : this.handle;
}
}
private static int toInt(String string, int def) {
return string.isBlank() ? def : Integer.parseInt(string);
}
}

View file

@ -1,124 +0,0 @@
<?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_R4</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>

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,166 +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.nms;
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.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The {@link MapSender_v1_12_R1} sends the Map packets to players.
*/
public class MapSender_v1_12_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_12_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, //???
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;
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 + ']';
}
}
}

View file

@ -1,249 +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.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;
}
}

View file

@ -1,123 +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.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);
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,166 +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.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 + ']';
}
}
}

View file

@ -1,248 +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.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;
}
}

View file

@ -1,123 +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.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);
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,167 +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.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 + ']';
}
}
}

View file

@ -1,248 +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.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;
}
}

View file

@ -1,123 +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.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);
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,167 +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.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 + ']';
}
}
}

View file

@ -1,249 +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.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;
}
}

View file

@ -1,123 +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.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);
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,167 +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.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 + ']';
}
}
}

View file

@ -1,248 +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.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;
}
}

View file

@ -1,123 +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.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);
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,138 +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.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) {
}
}

View file

@ -1,251 +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.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;
}
}

View file

@ -1,121 +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.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
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,138 +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.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) {
}
}

View file

@ -1,251 +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.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;
}
}

View file

@ -1,121 +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.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
}
}

View file

@ -1,72 +0,0 @@
<?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>

View file

@ -1,138 +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.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) {
}
}

View file

@ -1,239 +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.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;
}
}

View file

@ -1,125 +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.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 = packetPlayOutMap.a(); //mapId
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
}
}

View file

@ -1,72 +0,0 @@
<?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_R2</artifactId>
<properties>
<NMSVersion>1.20.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>

View file

@ -1,138 +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.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_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_20_R2} sends the Map packets to players.
*/
public class MapSender_v1_20_R2 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_20_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().c.a(packet); //connection send()
} catch (Exception e) {
e.printStackTrace();
}
});
}
record QueuedMap(int id, ArrayImage image, Player player) {
}
}

View file

@ -1,239 +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.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_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_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.*;
public class MapWrapper_v1_20_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_20_R2.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_20_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_20_R2.sendMap(id, MapWrapper_v1_20_R2.this.content, player);
} else {
MapSender_v1_20_R2.addToQueue(id, MapWrapper_v1_20_R2.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_20_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().bR.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().bR.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_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.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_R2(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -1,126 +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.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_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_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_20_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.a();
ItemStack item = packetPlayInSetCreativeSlot.d();
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.n.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.n; //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
}
}

View file

@ -1,72 +0,0 @@
<?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_R3</artifactId>
<properties>
<NMSVersion>1.20.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>

View file

@ -1,138 +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.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_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_20_R3} sends the Map packets to players.
*/
public class MapSender_v1_20_R3 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_20_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().c.a(packet); //connection send()
} catch (Exception e) {
e.printStackTrace();
}
});
}
record QueuedMap(int id, ArrayImage image, Player player) {
}
}

View file

@ -1,239 +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.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_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_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_20_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_20_R3.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_20_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_20_R3.sendMap(id, MapWrapper_v1_20_R3.this.content, player);
} else {
MapSender_v1_20_R3.addToQueue(id, MapWrapper_v1_20_R3.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_20_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().bR.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().bR.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_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.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_R3(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -1,126 +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.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_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_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_20_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.d();
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.n.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.n; //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
}
}

View file

@ -1,72 +0,0 @@
<?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_R4</artifactId>
<properties>
<NMSVersion>1.20.6-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>21</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>

View file

@ -1,141 +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.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.MapId;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R4.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_R4} sends the Map packets to players.
*/
public class MapSender_v1_20_R4 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_20_R4() {
}
/**
* 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
);
MapId mapId = new MapId(id);
PacketPlayOutMap packet = new PacketPlayOutMap(
mapId, //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) {
}
}

View file

@ -1,243 +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.nms;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
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 net.minecraft.world.level.saveddata.maps.MapId;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R4.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_R4 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_R4.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_20_R4.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_R4.sendMap(id, MapWrapper_v1_20_R4.this.content, player);
} else {
MapSender_v1_20_R4.addToQueue(id, MapWrapper_v1_20_R4.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_20_R4.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().cb.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().cb.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_R4.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);
MapId mapId1 = new MapId(mapId);
nmsStack.b(DataComponents.B, mapId1); //set
List<DataWatcher.c<?>> list = new ArrayList<>();
DataWatcher.c<?> dataWatcherItem = DataWatcher.c.a(EntityItemFrame.g, nmsStack);
list.add(dataWatcherItem);
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, list);
((CraftPlayer) player).getHandle().c.a(packet);
}
};
public MapWrapper_v1_20_R4(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -1,126 +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.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_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R4.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_R4 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 = (int) getDeclaredField(packetPlayInSetCreativeSlot, supports(20, 4) ? "b" : "a"); //slot, 1.20.5 = b, lower is a
ItemStack item = (ItemStack) getDeclaredField(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : "d"); //item, 1.20.5 = e, lower is d
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.n.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.n; //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
}
}

View file

@ -1,72 +0,0 @@
<?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_21_R1</artifactId>
<properties>
<NMSVersion>1.21-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>21</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>

View file

@ -1,141 +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.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.MapId;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_21_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_21_R1} sends the Map packets to players.
*/
public class MapSender_v1_21_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_21_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
);
MapId mapId = new MapId(id);
PacketPlayOutMap packet = new PacketPlayOutMap(
mapId, //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) {
}
}

View file

@ -1,243 +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.nms;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
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 net.minecraft.world.level.saveddata.maps.MapId;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_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_21_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_21_R1.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_21_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_21_R1.sendMap(id, MapWrapper_v1_21_R1.this.content, player);
} else {
MapSender_v1_21_R1.addToQueue(id, MapWrapper_v1_21_R1.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_21_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().cc.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().cc.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_21_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);
MapId mapId1 = new MapId(mapId);
nmsStack.b(DataComponents.B, mapId1); //set
List<DataWatcher.c<?>> list = new ArrayList<>();
DataWatcher.c<?> dataWatcherItem = DataWatcher.c.a(EntityItemFrame.f, nmsStack);
list.add(dataWatcherItem);
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, list);
((CraftPlayer) player).getHandle().c.a(packet);
}
};
public MapWrapper_v1_21_R1(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -1,126 +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.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_21_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_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_21_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 = (int) getDeclaredField(packetPlayInSetCreativeSlot, supports(20, 4) ? "b" : "a"); //slot, 1.20.5 = b, lower is a
ItemStack item = (ItemStack) getDeclaredField(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : "d"); //item, 1.20.5 = e, lower is d
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.n.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.n; //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
}
}

View file

@ -1,72 +0,0 @@
<?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_21_R2</artifactId>
<properties>
<NMSVersion>1.21.3-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>21</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>

View file

@ -1,141 +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.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.MapId;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_21_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_21_R2} sends the Map packets to players.
*/
public class MapSender_v1_21_R2 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_21_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.c updateData = new WorldMap.c(
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
);
MapId mapId = new MapId(id);
PacketPlayOutMap packet = new PacketPlayOutMap(
mapId, //ID
(byte) 0, //Scale
false, //Show icons
new ArrayList<>(), //Icons
updateData
);
((CraftPlayer) player).getHandle().f.a(packet); //connection send()
} catch (Exception e) {
e.printStackTrace();
}
});
}
record QueuedMap(int id, ArrayImage image, Player player) {
}
}

View file

@ -1,242 +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.nms;
import net.minecraft.core.component.DataComponents;
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 net.minecraft.world.level.saveddata.maps.MapId;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_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.*;
public class MapWrapper_v1_21_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_21_R2.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_21_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_21_R2.sendMap(id, MapWrapper_v1_21_R2.this.content, player);
} else {
MapSender_v1_21_R2.addToQueue(id, MapWrapper_v1_21_R2.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_21_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().cc.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().cc.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().f.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_21_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);
MapId mapId1 = new MapId(mapId);
nmsStack.b(DataComponents.L, mapId1); //set
List<DataWatcher.c<?>> list = new ArrayList<>();
DataWatcher.c<?> dataWatcherItem = DataWatcher.c.a(EntityItemFrame.e, nmsStack);
list.add(dataWatcherItem);
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, list);
((CraftPlayer) player).getHandle().f.a(packet);
}
};
public MapWrapper_v1_21_R2(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -1,126 +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.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_21_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_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_21_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 = (int) getDeclaredField(packetPlayInSetCreativeSlot, supports(20, 4) ? "b" : "a"); //slot, 1.20.5 = b, lower is a
ItemStack item = (ItemStack) getDeclaredField(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : "d"); //item, 1.20.5 = e, lower is d
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.n.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.n; //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.d, vec3dObj.e, vec3dObj.f); //x, y, z
}
}

106
README.md
View file

@ -1,9 +1,11 @@
# MapReflectionAPI # MapReflectionAPI
This plugin helps developer with viewing images on maps. It supports Spigot 1.12 - 1.19. This plugin helps developer with displaying images on maps. It supports Spigot 1.12 - 1.21.
## Usage: ## Usage:
### Using the API:
First, include the API using Maven: First, include the API using Maven:
```xml ```xml
@ -15,8 +17,8 @@ First, include the API using Maven:
<dependency> <dependency>
<groupId>tech.sbdevelopment</groupId> <groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId> <artifactId>MapReflectionAPI</artifactId>
<version>1.5</version> <version>1.6.4</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
``` ```
@ -50,17 +52,8 @@ controller.showInHand(p, true);
It's also possible to split one image onto multiple itemframes. For example using the following code. It's also possible to split one image onto multiple itemframes. For example using the following code.
```java ```java
BufferedImage leftTopFrame = ...; //--- Wrap image (into 2 rows and 2 columns) ---
BufferedImage leftBottomFrame = ...; MultiMapWrapper wrapper = MapReflectionAPI.getMapManager().wrapMultiImage(ImageIO.read(new File("image.png")), 2, 2);
BufferedImage rightTopFrame = ...;
BufferedImage rightBottomFrame = ...;
BufferedImage[][] images = {
{leftBottomFrame, leftTopFrame},
{rightBottomFrame, rightTopFrame}
};
//--- Wrap image ---
MultiMapWrapper wrapper = MapReflectionAPI.getMapManager().wrapMultiImage(images);
MultiMapController controller = wrapper.getController(); MultiMapController controller = wrapper.getController();
final Player p = Bukkit.getPlayer("SBDeveloper"); final Player p = Bukkit.getPlayer("SBDeveloper");
@ -81,17 +74,94 @@ ItemFrame leftBottomFrame = ...;
ItemFrame rightTopFrame = ...; ItemFrame rightTopFrame = ...;
ItemFrame rightBottomFrame = ...; ItemFrame rightBottomFrame = ...;
ItemFrame[][] frames = { ItemFrame[][] frames = {
{leftBottomFrame, leftTopFrame}, {leftTopFrame, rightTopFrame},
{rightBottomFrame, rightTopFrame} {leftBottomFrame, rightBottomFrame}
}; };
controller.showInFrames(p, frames, true); controller.showInFrames(p, frames, true);
``` ```
More information can be found on the [JavaDoc](https://sbdevelopment.tech/javadoc/mapreflectionapi/). More information can be found on the [JavaDoc](https://sbdevelopment.tech/javadoc/mapreflectionapi/).
### Notes on map render distance:
MapReflectionAPI does not implement render distance to images shown on maps. This should be implemented by yourself. An example of this is below.
```java
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
public class MapRenderDistanceListener implements Listener {
private static final int MAP_RENDER_DISTANCE_SQUARED = 1024;
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
//Show the maps to the player which are within distance
});
}
@EventHandler
public void onPlayerLeave(PlayerQuitEvent e) {
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
//Hide the maps to the player which are within distance
});
}
@EventHandler
public void onPlayerChangeWorld(PlayerChangedWorldEvent e) {
//Hide all the maps in the e.getFrom() world
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
//Show the maps to the player which are within distance in e.getPlayer().getWorld()
}, 20);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerDeath(PlayerDeathEvent e) {
//Hide all the maps in the e.getEntity().getWorld()
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerRespawn(PlayerRespawnEvent e) {
Bukkit.getScheduler().runTaskLaterAsynchronously(MyPluginInstance, () -> {
//Show the maps to the player which are within distance in e.getPlayer().getWorld()
}, 20);
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent e) {
//Hide all the maps in the e.getFrom() world
//Show the maps to the player which are within distance in e.getTo().getWorld()
//FOR EXAMPLE:
if (e.getTo() == null) return;
if (e.getFrom().getChunk().equals(e.getTo().getChunk())) return;
for (Frame frame : API.getFramesInWorld(e.getPlayer().getWorld())) {
double distanceSquared = e.getTo().distanceSquared(frame.getLocation());
if (distanceSquared > MAP_RENDER_DISTANCE_SQUARED) {
API.hideFrame(e.getPlayer(), frame);
} else {
API.showFrame(e.getPlayer(), frame);
}
}
}
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent e) {
//Hide all the maps in the e.getFrom() world
//Show the maps to the player which are within distance in e.getTo().getWorld()
//SEE EXAMPLE ABOVE
}
}
```
## Credits: ## Credits:
This is a fork of [MapManager](https://github.com/InventivetalentDev/MapManager). It updates the API to 1.19 and uses This is a fork of [MapManager](https://github.com/InventivetalentDev/MapManager). It updates the API to the latest version of Minecraft and uses other dependencies.
other dependencies.
This plugin includes classes from BKCommonLib. Please checkout the README in that package for more information. This plugin includes classes from BKCommonLib. Please checkout the README in that package for more information.

193
pom.xml
View file

@ -24,37 +24,18 @@
<groupId>tech.sbdevelopment</groupId> <groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI</artifactId> <artifactId>MapReflectionAPI</artifactId>
<packaging>pom</packaging> <version>1.6.6</version>
<version>${revision}</version> <packaging>jar</packaging>
<name>MapReflectionAPI</name> <name>MapReflectionAPI</name>
<description>This API helps developer with viewing images on maps.</description> <description>This API helps developer with viewing images on maps.</description>
<url>https://sbdplugins.nl</url> <url>https://sbdplugins.nl</url>
<properties> <properties>
<revision>1.5</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>11</jdk.version> <maven.lombok.delombok-target>${project.build.directory}/javadoc-delombok</maven.lombok.delombok-target>
</properties> </properties>
<modules>
<module>API</module>
<module>Dist</module>
<module>NMS-v1_21_R1</module>
<module>NMS-v1_20_R4</module>
<module>NMS-v1_20_R3</module>
<module>NMS-v1_20_R2</module>
<module>NMS-v1_21_R2</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> <distributionManagement>
<repository> <repository>
<id>sbdevelopment-repo</id> <id>sbdevelopment-repo</id>
@ -63,60 +44,113 @@
</distributionManagement> </distributionManagement>
<build> <build>
<defaultGoal>clean package</defaultGoal>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version> <version>3.14.0</version>
<configuration> <configuration>
<release>${jdk.version}</release> <release>11</release>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
</annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.5.0</version>
<configuration>
<updatePomFile>true</updatePomFile>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version> <version>3.6.0</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase>
<goals> <goals>
<goal>toolchain</goal> <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>
<relocation>
<pattern>com.cryptomorin.xseries</pattern>
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.xseries</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> </goals>
</execution> </execution>
</executions> </executions>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.2</version>
<configuration> <configuration>
<toolchains> <release>11</release>
<jdk> <sourcepath>${maven.lombok.delombok-target}</sourcepath>
<version>${jdk.version}</version> <sourceFileExcludes>
</jdk> <sourceFileExclude>**/com/bergerkiller/bukkit/common/io/*.java</sourceFileExclude>
</toolchains> <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> </configuration>
</plugin> </plugin>
</plugins> </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> </build>
<repositories> <repositories>
@ -124,13 +158,56 @@
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository> </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>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version> <version>1.21.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.cryptomorin</groupId>
<artifactId>XSeries</artifactId>
<version>13.1.0</version>
</dependency>
<!-- Libraries below are provided by CraftBukkit -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>24.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>4.1.118.Final</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -27,6 +27,8 @@ import java.awt.*;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import static com.cryptomorin.xseries.reflection.XReflection.supports;
/** /**
* Additional functionality on top of Bukkit's MapPalette * Additional functionality on top of Bukkit's MapPalette
*/ */
@ -50,14 +52,12 @@ public class MapColorPalette {
MCSDBubbleFormat bubbleData = new MCSDBubbleFormat(); MCSDBubbleFormat bubbleData = new MCSDBubbleFormat();
try { try {
String bub_path_postfix; String bub_path_postfix;
if (ReflectionUtil.supports(17)) { if (supports(17)) {
bub_path_postfix = "map_1_17.bub"; bub_path_postfix = "map_1_17.bub";
} else if (ReflectionUtil.supports(16)) { } else if (supports(16)) {
bub_path_postfix = "map_1_16.bub"; bub_path_postfix = "map_1_16.bub";
} else if (ReflectionUtil.supports(12)) {
bub_path_postfix = "map_1_12.bub";
} else { } else {
bub_path_postfix = "map_1_8_8.bub"; bub_path_postfix = "map_1_12.bub";
} }
String bub_path = "/tech/sbdevelopment/mapreflectionapi/libs/bkcommonlib/internal/resources/map/" + bub_path_postfix; String bub_path = "/tech/sbdevelopment/mapreflectionapi/libs/bkcommonlib/internal/resources/map/" + bub_path_postfix;
InputStream input = MapColorPalette.class.getResourceAsStream(bub_path); InputStream input = MapColorPalette.class.getResourceAsStream(bub_path);

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -30,15 +30,15 @@ import tech.sbdevelopment.mapreflectionapi.listeners.MapListener;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener; import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import tech.sbdevelopment.mapreflectionapi.managers.Configuration; import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
import tech.sbdevelopment.mapreflectionapi.utils.MainUtil; import tech.sbdevelopment.mapreflectionapi.utils.MainUtil;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager; import tech.sbdevelopment.mapreflectionapi.utils.UpdateManager;
import java.util.logging.Level; import java.util.logging.Level;
import static com.cryptomorin.xseries.reflection.XReflection.supports;
public class MapReflectionAPI extends JavaPlugin { public class MapReflectionAPI extends JavaPlugin {
private static MapReflectionAPI instance; private static MapReflectionAPI instance;
private static MapManager mapManager; private static MapManager mapManager;
private static PacketListener packetListener;
/** /**
* Get the plugin instance * Get the plugin instance
@ -68,8 +68,8 @@ public class MapReflectionAPI extends JavaPlugin {
getLogger().info("MapReflectionAPI v" + getDescription().getVersion()); getLogger().info("MapReflectionAPI v" + getDescription().getVersion());
getLogger().info("Made by © Copyright SBDevelopment 2023"); getLogger().info("Made by © Copyright SBDevelopment 2023");
if (!ReflectionUtil.supports(12)) { if (!supports(12)) {
getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.19.4!"); getLogger().severe("MapReflectionAPI only supports Minecraft 1.12 - 1.20.5!");
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
return; return;
} }
@ -90,26 +90,11 @@ public class MapReflectionAPI extends JavaPlugin {
getLogger().info("Loading the commands..."); getLogger().info("Loading the commands...");
getCommand("mapmanager").setExecutor(new MapManagerCMD()); getCommand("mapmanager").setExecutor(new MapManagerCMD());
try { getLogger().info("Loading the map manager...");
packetListener = PacketListener.construct(this); mapManager = new MapManager();
} 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()) { if (Configuration.getInstance().isAllowVanilla()) {
getLogger().info("Vanilla Maps are allowed!"); getLogger().info("Vanilla Maps are allowed. Discovering occupied Map IDs...");
getLogger().info("Discovering occupied Map IDs...");
int occupiedIDs = 0; int occupiedIDs = 0;
for (int s = 0; s < Short.MAX_VALUE; s++) { for (int s = 0; s < Short.MAX_VALUE; s++) {
try { try {
@ -129,8 +114,9 @@ public class MapReflectionAPI extends JavaPlugin {
getLogger().info("Registering the listeners..."); getLogger().info("Registering the listeners...");
Bukkit.getPluginManager().registerEvents(new MapListener(), this); Bukkit.getPluginManager().registerEvents(new MapListener(), this);
Bukkit.getPluginManager().registerEvents(new PacketListener(), this);
getLogger().info("Enabling metrics..."); getLogger().info("Loading metrics...");
Metrics metrics = new Metrics(this, 16033); Metrics metrics = new Metrics(this, 16033);
metrics.addCustomChart(new SingleLineChart("managed_maps", () -> mapManager.getManagedMapsCount())); metrics.addCustomChart(new SingleLineChart("managed_maps", () -> mapManager.getManagedMapsCount()));
@ -182,9 +168,6 @@ public class MapReflectionAPI extends JavaPlugin {
@Override @Override
public void onDisable() { public void onDisable() {
getLogger().info("Disabling the packet handler...");
if (packetListener != null) Bukkit.getOnlinePlayers().forEach(p -> packetListener.removePlayer(p));
getLogger().info("MapReflectionAPI is disabled!"); getLogger().info("MapReflectionAPI is disabled!");
instance = null; instance = null;

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -19,7 +19,6 @@
package tech.sbdevelopment.mapreflectionapi.api; package tech.sbdevelopment.mapreflectionapi.api;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.ItemFrame; import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -117,13 +116,4 @@ public interface MapController extends IMapController {
* @param frame {@link ItemFrame} to clear * @param frame {@link ItemFrame} to clear
*/ */
void clearFrame(Player player, ItemFrame frame); void clearFrame(Player player, ItemFrame frame);
/**
* Get an {@link ItemFrame} by its entity ID
*
* @param world The world the {@link ItemFrame} is in
* @param entityId Entity-ID of the {@link ItemFrame}
* @return The found {@link ItemFrame}, or <code>null</code>
*/
ItemFrame getItemFrameById(World world, int entityId);
} }

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -18,47 +18,29 @@
package tech.sbdevelopment.mapreflectionapi.api; package tech.sbdevelopment.mapreflectionapi.api;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import tech.sbdevelopment.mapreflectionapi.managers.Configuration; import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import static com.cryptomorin.xseries.reflection.XReflection.*;
/** /**
* The {@link MapManager} manages all the maps. It also contains functions for wrapping. * The {@link MapManager} manages all the maps. It also contains functions for wrapping.
*/ */
public class MapManager { public class MapManager {
protected final Set<Integer> occupiedIds = new HashSet<>(); protected final Set<Integer> occupiedIds = new HashSet<>();
protected final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>(); 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 * Get the amount of maps managed by the plugin
@ -147,15 +129,9 @@ public class MapManager {
* @return The wrapper * @return The wrapper
*/ */
private MapWrapper wrapNewImage(ArrayImage image) { private MapWrapper wrapNewImage(ArrayImage image) {
try { MapWrapper wrapper = new MapWrapper(image);
MapWrapper wrapper = (MapWrapper) wrapperClass.getDeclaredConstructor(ArrayImage.class).newInstance(image); managedMaps.add(wrapper);
managedMaps.add(wrapper); return wrapper;
return wrapper;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
e.printStackTrace();
return null;
}
} }
/** /**
@ -201,6 +177,30 @@ public class MapManager {
return null; return null;
} }
/**
* Get an {@link ItemFrame} by its entity ID
*
* @param world The world the {@link ItemFrame} is in
* @param entityId Entity-ID of the {@link ItemFrame}
* @return The found {@link ItemFrame}, or <code>null</code>
*/
public ItemFrame getItemFrameById(World world, int entityId) {
Object worldHandle = ReflectionUtil.getHandle(world);
Object nmsEntity = ReflectionUtil.callMethod(worldHandle, supports(18) ? "a" : "getEntity", entityId);
if (nmsEntity == null) return null;
Object craftEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity");
if (craftEntity == null) return null;
Class<?> itemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame");
if (itemFrameClass == null) return null;
if (craftEntity.getClass().isAssignableFrom(itemFrameClass))
return (ItemFrame) itemFrameClass.cast(craftEntity);
return null;
}
/** /**
* Register an occupied map ID * Register an occupied map ID
* *

View file

@ -0,0 +1,188 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 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 lombok.Data;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import java.util.ArrayList;
import java.util.List;
import static com.cryptomorin.xseries.reflection.XReflection.*;
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket;
/**
* The {@link MapSender} sends the Map packets to players.
*/
public class MapSender {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender() {
}
/**
* 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);
}
private static final Class<?> packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap");
private static final Class<?> worldMapData = supports(17) ? getNMSClass("world.level.saveddata.maps", supports(21, 2) ? "WorldMap$c" : "WorldMap$b") : null; //1.21.2+ uses WorldMap$c, 1.17+ uses WorldMap$b
private static final Class<?> mapIdClazz = supports(21) ? getNMSClass("world.level.saveddata.maps", "MapId") : null;
/**
* Send a map to a player
*
* @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;
}
int id = -id0;
Object packet;
if (supports(20, 4)) { //1.20.5+
Object updateData = ReflectionUtil.callConstructor(worldMapData,
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY, //Y size (2nd Y pos)
content.array //Data
);
Object mapId = ReflectionUtil.callConstructor(mapIdClazz, id);
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
mapId, //ID
(byte) 0, //Scale, 0 = 1 block per pixel
false, //Show icons
new ReflectionUtil.CollectionParam<>(), //Icons
updateData
);
} else if (supports(17)) { //1.17+
Object updateData = ReflectionUtil.callConstructor(worldMapData,
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
);
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
id, //ID
(byte) 0, //Scale, 0 = 1 block per pixel
false, //Show icons
new ReflectionUtil.CollectionParam<>(), //Icons
updateData
);
} else if (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)
);
}
sendPacket(player, packet);
}
@Data
static final class QueuedMap {
private final int id;
private final ArrayImage image;
private final Player player;
}
}

View file

@ -0,0 +1,387 @@
/*
* 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 lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.OfflinePlayer;
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 tech.sbdevelopment.mapreflectionapi.utils.XMaterial;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static com.cryptomorin.xseries.reflection.XReflection.*;
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getHandle;
import static com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.sendPacket;
/**
* A {@link MapWrapper} wraps one image.
*/
@Getter
public class MapWrapper extends AbstractMapWrapper {
public static final String REFERENCE_METADATA = "MAP_WRAPPER_REF";
protected ArrayImage content;
/**
* Construct a new {@link MapWrapper}
*
* @param image The {@link ArrayImage} to wrap
*/
public MapWrapper(ArrayImage image) {
this.content = image;
}
private static final Class<?> craftStackClass = getCraftClass("inventory.CraftItemStack");
private static final Class<?> setSlotPacketClass = getNMSClass("network.protocol.game", "PacketPlayOutSetSlot");
private static final Class<?> entityClass = getNMSClass("world.entity", "Entity");
private static final Class<?> dataWatcherClass = getNMSClass("network.syncher", "DataWatcher");
private static final Class<?> entityMetadataPacketClass = getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata");
private static final Class<?> entityItemFrameClass = getNMSClass("world.entity.decoration", "EntityItemFrame");
private static final Class<?> dataWatcherItemClass = 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) {
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
MapContentUpdateEvent event = new MapContentUpdateEvent(MapWrapper.this, content, async);
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 (supports(21)) {
//1.21.5 = bQ, 1.21 - 1.21.4 = cc
inventoryMenuName = supports(21, 4) ? "bQ" : "cc";
} else if (supports(20)) {
//1.20.5 = cb, 1.20.2 - 1.20.4 = bR, 1.20(.1) = bQ
inventoryMenuName = supports(20, 4) ? "cb" : supports(20, 2) ? "bR" : "bQ";
} else if (supports(19)) {
//1.19.4 = bO, >= 1.19.3 = bT
inventoryMenuName = supports(19, 3) ? "bO" : "bT";
} else if (supports(18)) {
//1.18.1 = ap, 1.18(.2) = ao
inventoryMenuName = supports(18, 1) ? "bV" : "bU";
} else if (supports(17)) {
//1.17, same as 1.18(.2)
inventoryMenuName = "bU";
} else {
//1.12-1.16
inventoryMenuName = "defaultContainer";
}
Object inventoryMenu = ReflectionUtil.getField(getHandle(player), inventoryMenuName);
Object nmsStack = asCraftItemStack(player);
Object packet;
if (supports(17)) { //1.17+
int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, 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
);
}
sendPacket(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() != XMaterial.FILLED_MAP.parseMaterial() && !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() != XMaterial.FILLED_MAP.parseMaterial() && !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(XMaterial.FILLED_MAP.parseMaterial(), 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = MapReflectionAPI.getMapManager().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 = MapReflectionAPI.getMapManager().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());
}
private Object asCraftItemStack(Player player) {
return createCraftItemStack(new ItemStack(XMaterial.FILLED_MAP.parseMaterial(), 1, (short) getMapId(player)), (short) getMapId(player));
}
private Object createCraftItemStack(@NotNull ItemStack stack, int mapId) {
if (mapId < 0) return null;
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
//1.20.5 uses new NBT compound system
if (supports(20, 4)) {
Object mapIdComponent = ReflectionUtil.getDeclaredField(getNMSClass("core.component", "DataComponents"), supports(21, 4) ? "M" : supports(21, 2) ? "L" : "B"); //1.21.2+ uses L, otherwise B
Object mapId1 = ReflectionUtil.callConstructor(getNMSClass("world.level.saveddata.maps", "MapId"), mapId);
// Use generic reflection because of generics
// <T> T ItemStack#b(DataComponentType<? super T> dataComponentType, T t)
try {
Method m = nmsStack.getClass().getMethod("b", getNMSClass("core.component", "DataComponentType"), Object.class);
m.setAccessible(true);
m.invoke(nmsStack, mapIdComponent, mapId1);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
return null;
}
} else if (supports(13)) {
String nbtObjectName;
if (supports(20)) { //1.20
nbtObjectName = "w";
} else if (supports(19)) { //1.19
nbtObjectName = "v";
} else if (supports(18)) { //1.18
nbtObjectName = supports(18, 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, 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 (supports(21)) { //1.21
dataWatcherObjectName = supports(21, 2) ? "e" : "f"; //1.21.2+ = e, 1.21(.1) = f
} else if (supports(19, 3)) { //1.19.3 and 1.20(.1)
dataWatcherObjectName = "g";
} else if (supports(19)) { //1.19-1.19.2
dataWatcherObjectName = "ao";
} else if (supports(18)) { //1.18
dataWatcherObjectName = supports(18, 1) ? "ap" : "ao"; //1.18.1 = ap, 1.18(.2) = ao
} else if (supports(17)) { //1.17
dataWatcherObjectName = "ao";
} else if (supports(14)) { //1.14 - 1.16
dataWatcherObjectName = "ITEM";
} else if (supports(13)) { //1.13
dataWatcherObjectName = "e";
} else { //1.12
dataWatcherObjectName = "c";
}
Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, dataWatcherObjectName);
ReflectionUtil.ListParam<Object> list = new ReflectionUtil.ListParam<>();
Object packet;
if (supports(19, 3)) { //1.19.3
Class<?> dataWatcherRecordClass = getNMSClass("network.syncher", "DataWatcher$" + (supports(20, 4) ? "c" : "b"));
// Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter
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);
}
sendPacket(player, packet);
}
};
}

View file

@ -86,7 +86,7 @@ public interface MultiMapController extends IMapController {
* Show this {@link MultiMapController} in {@link ItemFrame}s * Show this {@link MultiMapController} in {@link ItemFrame}s
* *
* @param player {@link Player} that will be able to see the maps * @param player {@link Player} that will be able to see the maps
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[width][height]</code>) * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[rows][columns]</code>)
* @see MapController#showInFrame(Player, int) * @see MapController#showInFrame(Player, int)
*/ */
void showInFrames(Player player, Integer[][] entityIdMatrix); void showInFrames(Player player, Integer[][] entityIdMatrix);
@ -95,7 +95,7 @@ public interface MultiMapController extends IMapController {
* Show this {@link MultiMapController} in {@link ItemFrame}s * Show this {@link MultiMapController} in {@link ItemFrame}s
* *
* @param player {@link Player} that will be able to see the maps * @param player {@link Player} that will be able to see the maps
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[width][height]</code>) * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[rows][columns]</code>)
* @param callable {@link DebugCallable} which will be called to display debug information, or <code>null</code> * @param callable {@link DebugCallable} which will be called to display debug information, or <code>null</code>
* @see MapController#showInFrame(Player, int, String) * @see MapController#showInFrame(Player, int, String)
*/ */
@ -105,7 +105,7 @@ public interface MultiMapController extends IMapController {
* Show this {@link MultiMapController} in {@link ItemFrame}s * Show this {@link MultiMapController} in {@link ItemFrame}s
* *
* @param player {@link Player} that will be able to see the maps * @param player {@link Player} that will be able to see the maps
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[width][height]</code>) * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[rows][columns]</code>)
* @param force if <code>false</code>, the map will not be shown if there is not Map-Item in the ItemFrames * @param force if <code>false</code>, the map will not be shown if there is not Map-Item in the ItemFrames
* @see MapController#showInFrame(Player, ItemFrame, boolean) * @see MapController#showInFrame(Player, ItemFrame, boolean)
*/ */
@ -115,7 +115,7 @@ public interface MultiMapController extends IMapController {
* Show this {@link MultiMapController} in {@link ItemFrame}s * Show this {@link MultiMapController} in {@link ItemFrame}s
* *
* @param player {@link Player} that will be able to see the maps * @param player {@link Player} that will be able to see the maps
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[width][height]</code>) * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[rows][columns]</code>)
* @see MapController#showInFrame(Player, ItemFrame) * @see MapController#showInFrame(Player, ItemFrame)
*/ */
void showInFrames(Player player, ItemFrame[][] itemFrameMatrix); void showInFrames(Player player, ItemFrame[][] itemFrameMatrix);
@ -124,7 +124,7 @@ public interface MultiMapController extends IMapController {
* Clear the frames * Clear the frames
* *
* @param player {@link Player} that will be able to see the cleared frames * @param player {@link Player} that will be able to see the cleared frames
* @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[width][height]</code>) * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (<code>int[rows][columns]</code>)
*/ */
void clearFrames(Player player, Integer[][] entityIdMatrix); void clearFrames(Player player, Integer[][] entityIdMatrix);
@ -132,7 +132,7 @@ public interface MultiMapController extends IMapController {
* Clear the frames * Clear the frames
* *
* @param player {@link Player} that will be able to see the cleared frames * @param player {@link Player} that will be able to see the cleared frames
* @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[width][height]</code>) * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (<code>ItemFrame[rows][columns]</code>)
*/ */
void clearFrames(Player player, ItemFrame[][] itemFrameMatrix); void clearFrames(Player player, ItemFrame[][] itemFrameMatrix);
@ -144,11 +144,11 @@ public interface MultiMapController extends IMapController {
* Called to get debug information for a frame * Called to get debug information for a frame
* *
* @param controller the {@link MapController} * @param controller the {@link MapController}
* @param x X-Position of the current frame * @param row Row of the current frame
* @param y Y-Position of the current frame * @param column Column of the current frame
* @return {@link String} to show when a player looks at the map, or <code>null</code> * @return {@link String} to show when a player looks at the map, or <code>null</code>
* @see MapController#showInFrame(Player, int, String) * @see MapController#showInFrame(Player, int, String)
*/ */
String call(MapController controller, int x, int y); String call(MapController controller, int row, int column);
} }
} }

View file

@ -25,52 +25,75 @@ import org.jetbrains.annotations.NotNull;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static tech.sbdevelopment.mapreflectionapi.utils.MainUtil.validateArrayDimensions;
/** /**
* A {@link MultiMapWrapper} wraps one image split in pieces. * A {@link MultiMapWrapper} wraps one image split in pieces.
*/ */
public class MultiMapWrapper extends AbstractMapWrapper { public class MultiMapWrapper extends AbstractMapWrapper {
private final MapWrapper[][] wrapperMatrix; private final MapWrapper[][] wrapperMatrix;
/**
* Creates a new {@link MultiMapWrapper} from the given image.
* The image will be split into the given amount of rows and columns.
*
* @param image The image to wrap
* @param rows The amount of rows
* @param columns The amount of columns
*/
public MultiMapWrapper(BufferedImage image, int rows, int columns) { public MultiMapWrapper(BufferedImage image, int rows, int columns) {
this(splitImage(image, columns, rows)); this(splitImage(image, rows, columns));
} }
/**
* Creates a new {@link MultiMapWrapper} from the given image.
* The image will be split into the given amount of rows and columns.
*
* @param image The image to wrap
* @param rows The amount of rows
* @param columns The amount of columns
*/
public MultiMapWrapper(ArrayImage image, int rows, int columns) { public MultiMapWrapper(ArrayImage image, int rows, int columns) {
this(splitImage(image.toBuffered(), columns, rows)); this(splitImage(image.toBuffered(), rows, columns));
} }
public MultiMapWrapper(ArrayImage[][] imageMatrix) { /**
* Creates a new {@link MultiMapWrapper} from the given image.
*
* @param imageMatrix The image matrix to wrap
*/
protected MultiMapWrapper(ArrayImage[][] imageMatrix) {
wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length]; wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
for (int x = 0; x < imageMatrix.length; x++) { for (int row = 0; row < imageMatrix.length; row++) {
if (imageMatrix[x].length != imageMatrix[0].length) { if (imageMatrix[row].length != imageMatrix[0].length) {
throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!"); throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!");
} }
for (int y = 0; y < imageMatrix[x].length; y++) { for (int column = 0; column < imageMatrix[row].length; column++) {
wrapperMatrix[x][y] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[x][y]); wrapperMatrix[row][column] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[row][column]);
} }
} }
} }
public MultiMapWrapper(BufferedImage[][] imageMatrix) { /**
* Creates a new {@link MultiMapWrapper} from the given image.
*
* @param imageMatrix The image matrix to wrap
*/
protected MultiMapWrapper(BufferedImage[][] imageMatrix) {
wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length]; wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
for (int x = 0; x < imageMatrix.length; x++) { for (int row = 0; row < imageMatrix.length; row++) {
if (imageMatrix[x].length != imageMatrix[0].length) { if (imageMatrix[row].length != imageMatrix[0].length) {
throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!"); throw new IllegalArgumentException("An image in a MultiMapWrapper is not rectangular!");
} }
for (int y = 0; y < imageMatrix[x].length; y++) { for (int column = 0; column < imageMatrix[row].length; column++) {
wrapperMatrix[x][y] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[x][y]); wrapperMatrix[row][column] = MapReflectionAPI.getMapManager().wrapImage(imageMatrix[row][column]);
} }
} }
} }
@ -117,10 +140,10 @@ public class MultiMapWrapper extends AbstractMapWrapper {
@Override @Override
public void update(@NotNull ArrayImage content) { public void update(@NotNull ArrayImage content) {
ArrayImage[][] split = splitImage(content.toBuffered(), wrapperMatrix[0].length, wrapperMatrix.length); ArrayImage[][] split = splitImage(content.toBuffered(), wrapperMatrix.length, wrapperMatrix[0].length);
for (int x = 0; x < wrapperMatrix.length; x++) { for (int row = 0; row < wrapperMatrix.length; row++) {
for (int y = 0; y < wrapperMatrix[x].length; y++) { for (int column = 0; column < wrapperMatrix[row].length; column++) {
wrapperMatrix[x][y].getController().update(split[x][y]); wrapperMatrix[row][column].getController().update(split[row][column]);
} }
} }
} }
@ -150,33 +173,27 @@ public class MultiMapWrapper extends AbstractMapWrapper {
@Override @Override
public void showInFrames(Player player, Integer[][] entityIdMatrix) { public void showInFrames(Player player, Integer[][] entityIdMatrix) {
validateArrayDimensions(wrapperMatrix, entityIdMatrix); for (int row = 0; row < entityIdMatrix.length; row++) {
for (int column = 0; column < entityIdMatrix[row].length; column++) {
for (int x = 0; x < entityIdMatrix.length; x++) { wrapperMatrix[row][column].getController().showInFrame(player, entityIdMatrix[row][column]);
for (int y = 0; y < entityIdMatrix[x].length; y++) {
wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y]);
} }
} }
} }
@Override @Override
public void showInFrames(Player player, Integer[][] entityIdMatrix, DebugCallable callable) { public void showInFrames(Player player, Integer[][] entityIdMatrix, DebugCallable callable) {
validateArrayDimensions(wrapperMatrix, entityIdMatrix); for (int row = 0; row < entityIdMatrix.length; row++) {
for (int column = 0; column < entityIdMatrix[row].length; column++) {
for (int x = 0; x < entityIdMatrix.length; x++) { wrapperMatrix[row][column].getController().showInFrame(player, entityIdMatrix[row][column], callable.call(wrapperMatrix[row][column].getController(), row, column));
for (int y = 0; y < entityIdMatrix[x].length; y++) {
wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y], callable.call(wrapperMatrix[y][x].getController(), x, y));
} }
} }
} }
@Override @Override
public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) { public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) {
validateArrayDimensions(wrapperMatrix, itemFrameMatrix); for (int row = 0; row < itemFrameMatrix.length; row++) {
for (int column = 0; column < itemFrameMatrix[row].length; column++) {
for (int x = 0; x < itemFrameMatrix.length; x++) { wrapperMatrix[row][column].getController().showInFrame(player, itemFrameMatrix[row][column], force);
for (int y = 0; y < itemFrameMatrix[x].length; y++) {
wrapperMatrix[y][x].getController().showInFrame(player, itemFrameMatrix[x][wrapperMatrix.length - 1 - y], force);
} }
} }
} }
@ -188,47 +205,47 @@ public class MultiMapWrapper extends AbstractMapWrapper {
@Override @Override
public void clearFrames(Player player, Integer[][] entityIdMatrix) { public void clearFrames(Player player, Integer[][] entityIdMatrix) {
validateArrayDimensions(wrapperMatrix, entityIdMatrix); for (int row = 0; row < entityIdMatrix.length; row++) {
for (int column = 0; column < entityIdMatrix[row].length; column++) {
for (int x = 0; x < entityIdMatrix.length; x++) { wrapperMatrix[row][column].getController().clearFrame(player, entityIdMatrix[row][column]);
for (int y = 0; y < entityIdMatrix[x].length; y++) {
wrapperMatrix[y][x].getController().clearFrame(player, entityIdMatrix[x][y]);
} }
} }
} }
@Override @Override
public void clearFrames(Player player, ItemFrame[][] itemFrameMatrix) { public void clearFrames(Player player, ItemFrame[][] itemFrameMatrix) {
validateArrayDimensions(wrapperMatrix, itemFrameMatrix); for (int row = 0; row < itemFrameMatrix.length; row++) {
for (int column = 0; column < itemFrameMatrix[row].length; column++) {
for (int x = 0; x < itemFrameMatrix.length; x++) { wrapperMatrix[row][column].getController().clearFrame(player, itemFrameMatrix[row][column]);
for (int y = 0; y < itemFrameMatrix[x].length; y++) {
wrapperMatrix[y][x].getController().clearFrame(player, itemFrameMatrix[x][y]);
} }
} }
} }
}; };
/* /**
* Modified Method from http://kalanir.blogspot.de/2010/02/how-to-split-image-into-chunks-java.html * Splits a BufferedImage into a matrix of ArrayImages.
*
* @param image The image to split
* @param rows The number of rows
* @param columns The number of columns
* @return The matrix of ArrayImages
*/ */
private static ArrayImage[][] splitImage(final BufferedImage image, final int columns, final int rows) { private static ArrayImage[][] splitImage(final BufferedImage image, final int rows, final int columns) {
int chunkWidth = image.getWidth() / columns; int chunkWidth = image.getWidth() / columns;
int chunkHeight = image.getHeight() / rows; int chunkHeight = image.getHeight() / rows;
ArrayImage[][] images = new ArrayImage[rows][columns]; ArrayImage[][] images = new ArrayImage[rows][columns];
for (int x = 0; x < rows; x++) {
for (int y = 0; y < columns; y++) {
BufferedImage raw = new BufferedImage(chunkWidth, chunkHeight, image.getType());
Graphics2D gr = raw.createGraphics(); for (int i = 0; i < rows; i++) {
gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null); for (int j = 0; j < columns; j++) {
gr.dispose(); int x = j * chunkWidth;
int y = i * chunkHeight;
images[x][y] = new ArrayImage(raw); BufferedImage raw = image.getSubimage(x, y, chunkWidth, chunkHeight);
raw.flush(); images[i][j] = new ArrayImage(raw);
} }
} }
return images; return images;
} }

View file

@ -20,53 +20,40 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
import tech.sbdevelopment.mapreflectionapi.utils.XMaterial;
/** /**
* This event gets fired when a map in the creative inventory gets updated * This event gets fired when a map in the creative inventory gets updated
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Getter @Getter
public class CreateInventoryMapUpdateEvent extends Event implements Cancellable { public class CreativeInventoryMapUpdateEvent extends CancellableEvent {
private static final HandlerList handlerList = new HandlerList();
@Setter
private boolean cancelled;
private final Player player; private final Player player;
private final int slot; private final int slot;
private final ItemStack item; private final ItemStack item;
private MapWrapper mapWrapper; private MapWrapper mapWrapper;
/** /**
* Construct a new {@link CreateInventoryMapUpdateEvent} * Construct a new {@link CreativeInventoryMapUpdateEvent}
* *
* @param player The player whose inventory is updated * @param player The player whose inventory is updated
* @param slot The new slot * @param slot The new slot
* @param item The item in the new slot * @param item The item in the new slot
* @param isAsync Is this event called async? * @param isAsync Is this event called async?
*/ */
public CreateInventoryMapUpdateEvent(Player player, int slot, ItemStack item, boolean isAsync) { public CreativeInventoryMapUpdateEvent(Player player, int slot, ItemStack item, boolean isAsync) {
super(isAsync); super(isAsync);
this.player = player; this.player = player;
this.slot = slot; this.slot = slot;
this.item = item; this.item = item;
} }
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
/** /**
* Get the {@link MapWrapper} of the map of this event * Get the {@link MapWrapper} of the map of this event
* *
@ -76,7 +63,7 @@ public class CreateInventoryMapUpdateEvent extends Event implements Cancellable
public MapWrapper getMapWrapper() { public MapWrapper getMapWrapper() {
if (mapWrapper == null) { if (mapWrapper == null) {
if (item == null) return null; if (item == null) return null;
if (item.getType() != Material.MAP) return null; if (!XMaterial.FILLED_MAP.isSimilar(item)) return null;
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, item.getDurability()); mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, item.getDurability());
} }

View file

@ -20,23 +20,15 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable; import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/** /**
* This event gets fired when a map creation is cancelled * This event gets fired when a map creation is cancelled
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Getter @Getter
public class MapCancelEvent extends Event implements Cancellable { public class MapCancelEvent extends CancellableEvent {
private static final HandlerList handlerList = new HandlerList();
@Setter
private boolean cancelled;
private final Player player; private final Player player;
private final int id; private final int id;
@ -52,9 +44,4 @@ public class MapCancelEvent extends Event implements Cancellable {
this.player = player; this.player = player;
this.id = id; this.id = id;
} }
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
} }

View file

@ -21,11 +21,9 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage; import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.events.types.Event;
/** /**
* This event gets fired when the content of a {@link MapWrapper} is updated * This event gets fired when the content of a {@link MapWrapper} is updated
@ -33,8 +31,6 @@ import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
@RequiredArgsConstructor @RequiredArgsConstructor
@Getter @Getter
public class MapContentUpdateEvent extends Event { public class MapContentUpdateEvent extends Event {
private static final HandlerList handlerList = new HandlerList();
private final MapWrapper wrapper; private final MapWrapper wrapper;
private final ArrayImage content; private final ArrayImage content;
@Setter @Setter
@ -52,9 +48,4 @@ public class MapContentUpdateEvent extends Event {
this.wrapper = wrapper; this.wrapper = wrapper;
this.content = content; this.content = content;
} }
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
} }

View file

@ -20,28 +20,20 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.bukkit.entity.ItemFrame; import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI; import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
/** /**
* This event gets fired when a player interact with a map * This event gets fired when a player interact with a map
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Getter @Getter
public class MapInteractEvent extends Event implements Cancellable { public class MapInteractEvent extends CancellableEvent {
private static final HandlerList handlerList = new HandlerList();
@Setter
private boolean cancelled;
private final Player player; private final Player player;
private final int entityID; private final int entityID;
private final int action; private final int action;
@ -69,11 +61,6 @@ public class MapInteractEvent extends Event implements Cancellable {
this.hand = hand; this.hand = hand;
} }
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
/** /**
* Get the {@link ItemFrame} the map is in * Get the {@link ItemFrame} the map is in
* *
@ -81,10 +68,8 @@ public class MapInteractEvent extends Event implements Cancellable {
*/ */
@Nullable @Nullable
public ItemFrame getFrame() { public ItemFrame getFrame() {
if (getMapWrapper() == null) return null;
if (frame == null) { if (frame == null) {
frame = getMapWrapper().getController().getItemFrameById(player.getWorld(), entityID); frame = MapReflectionAPI.getMapManager().getItemFrameById(player.getWorld(), entityID);
} }
return frame; return frame;
} }
@ -96,10 +81,11 @@ public class MapInteractEvent extends Event implements Cancellable {
*/ */
@Nullable @Nullable
public MapWrapper getMapWrapper() { public MapWrapper getMapWrapper() {
if (getFrame() == null) return null;
if (mapWrapper == null) { if (mapWrapper == null) {
mapWrapper = MapReflectionAPI.getMapManager().getWrapperForId(player, entityID); if (!frame.hasMetadata(MapWrapper.REFERENCE_METADATA)) return null;
mapWrapper = (MapWrapper) frame.getMetadata(MapWrapper.REFERENCE_METADATA).get(0).value();
} }
return mapWrapper; return mapWrapper;
} }
} }

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -0,0 +1,54 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.api.events.types;
import lombok.NoArgsConstructor;
import org.bukkit.event.Cancellable;
@NoArgsConstructor
public class CancellableEvent extends Event implements Cancellable {
/**
* If this event gets cancelled.
*/
private boolean cancelled;
public CancellableEvent(boolean isAsync) {
super(isAsync);
}
/**
* Check if this event gets cancelled.
*
* @return true if cancelled, false if not
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Set if this event gets cancelled.
*
* @param cancelled true if you wish to cancel this event
*/
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View file

@ -16,27 +16,31 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package tech.sbdevelopment.mapreflectionapi.api; package tech.sbdevelopment.mapreflectionapi.api.events.types;
/** import lombok.Getter;
* A {@link MapWrapper} wraps one image. import lombok.NoArgsConstructor;
*/ import org.bukkit.event.HandlerList;
public abstract class MapWrapper extends AbstractMapWrapper {
protected ArrayImage content; @NoArgsConstructor
public class Event extends org.bukkit.event.Event {
public Event(boolean isAsync) {
super(isAsync);
}
/** /**
* Construct a new {@link MapWrapper} * A list of EventHandlers listening to this event.
*
* @param image The {@link ArrayImage} to wrap
*/ */
public MapWrapper(ArrayImage image) { @Getter
this.content = image; private static final HandlerList handlerList = new HandlerList();
}
public ArrayImage getContent() {
return content;
}
/**
* Get the EventHandlers listening to this event.
*
* @return The EventHandlers listening to this event.
*/
@Override @Override
public abstract MapController getController(); public HandlerList getHandlers() {
return handlerList;
}
} }

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -0,0 +1,212 @@
/*
* 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.CreativeInventoryMapUpdateEvent;
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 com.cryptomorin.xseries.reflection.minecraft.MinecraftConnection.getConnection;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
import static com.cryptomorin.xseries.reflection.XReflection.*;
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");
private static final Class<?> playerCommonConnection;
static {
if (supports(20, 2)) {
// The packet send method has been abstracted from ServerGamePacketListenerImpl to ServerCommonPacketListenerImpl in 1.20.2
playerCommonConnection = getNMSClass("server.network", "ServerCommonPacketListenerImpl");
} else {
playerCommonConnection = getNMSClass("server.network", "PlayerConnection");
}
}
@EventHandler
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 {
boolean cancel = false;
if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) {
Object packetPlayOutMap = packetPlayOutMapClass.cast(packet);
int id;
boolean inv = false;
if (supports(20, 4)) { //1.20.4 uses MapId class and record classes (final fields...)
Object mapId = getDeclaredField(packetPlayOutMap, "b");
id = (int) getDeclaredField(mapId, "c");
if (id < 0) {
Object newMapid = callConstructor(mapId.getClass(), -id);
Object c = getDeclaredField(packetPlayOutMap, "c");
Object d = getDeclaredField(packetPlayOutMap, "d");
Object e = getDeclaredField(packetPlayOutMap, "e");
Object f = getDeclaredField(packetPlayOutMap, "f");
packetPlayOutMap = callConstructor(packetPlayOutMapClass, newMapid, c, d, e, f);
packet = packetPlayOutMap;
inv = true;
}
} else {
id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
setDeclaredField(packetPlayOutMap, "a", -id);
inv = true;
}
}
if (!inv) {
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(player, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) cancel = true;
}
}
if (!cancel) super.write(ctx, packet, promise);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
boolean cancel = false;
if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) {
Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet);
int entityId = (int) getDeclaredField(packetPlayInEntity, supports(20, 4) ? "b" : "a");
Enum<?> actionEnum;
Enum<?> hand;
Object pos;
if (supports(17)) {
Object action = getDeclaredField(packetPlayInEntity, supports(20, 4) ? "c" : "b");
actionEnum = (Enum<?>) callDeclaredMethod(action, "a");
Class<?> d = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$d");
Class<?> e = getNMSClass("network.protocol.game", "PacketPlayInUseEntity$e");
if (action.getClass().isAssignableFrom(e)) {
hand = (Enum<?>) getDeclaredField(action, "a");
pos = getDeclaredField(action, "b");
} else {
pos = null;
if (action.getClass().isAssignableFrom(d)) {
hand = (Enum<?>) getDeclaredField(action, "a");
} else {
hand = null;
}
}
} else {
actionEnum = (Enum<?>) callDeclaredMethod(packetPlayInEntity, supports(13) ? "b" : "a"); //1.13 = b, 1.12 = a
hand = (Enum<?>) callDeclaredMethod(packetPlayInEntity, supports(13) ? "c" : "b"); //1.13 = c, 1.12 = b
pos = callDeclaredMethod(packetPlayInEntity, 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)) cancel = true;
} else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) {
Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet);
int slot;
if (supports(20, 4)) { //1.20.4+ uses short
slot = (short) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, "b");
} else { //1.20.3 and lower uses int
slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(19, 4) ? "a" : supports(13) ? "b" : "a"); //1.20.4 - 1.19.4 = a, 1.19.3 - 1.13 and 1.20.5 = b, 1.12 = a
}
Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, supports(20, 4) ? "e" : supports(20, 2) ? "d" : supports(18) ? "c" : "getItemStack"); //1.20.5 = e, 1.20.2-1.20.4 = d, >= 1.18 = c, 1.17 = getItemStack
ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack);
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
CreativeInventoryMapUpdateEvent event = new CreativeInventoryMapUpdateEvent(player, slot, craftStack, async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) cancel = true;
}
}
if (!cancel) 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 networkManager = getDeclaredField(playerCommonConnection, getConnection(player), supports(21) ? "e" : supports(20, 2) ? "c" : supports(19, 4) ? "h" : supports(19) ? "b" : supports(17) ? "a" : "networkManager"); //1.20.2 = ServerCommonPacketListenerImpl#c, 1.20(.1) & 1.19.4 = h, >= 1.19.3 = b, 1.18 - 1.17 = a, 1.16 = networkManager
return (Channel) getDeclaredField(networkManager, supports(20, 2) ? "n" : supports(18) ? "m" : supports(17) ? "k" : "channel"); //1.20.2 = n, 1.20(.1), 1.19 & 1.18 = m, 1.17 = k, 1.16 = channel
}
private Vector vec3DToVector(Object vec3d) {
if (!(vec3d.getClass().isAssignableFrom(vec3DClass))) return new Vector(0, 0, 0);
Object vec3dNMS = vec3DClass.cast(vec3d);
double x = (double) getDeclaredField(vec3dNMS, supports(21, 2) ? "d" : supports(19) ? "c" : supports(17) ? "b" : "x"); //1.21.2+ = d, 1.19 = c, 1.18 = b, 1.16 = x
double y = (double) getDeclaredField(vec3dNMS, supports(21, 2) ? "e" : supports(19) ? "d" : supports(17) ? "c" : "y"); //1.21.2+ = e, 1.19 = d, 1.18 = c, 1.16 = y
double z = (double) getDeclaredField(vec3dNMS, supports(21, 2) ? "f" : supports(19) ? "e" : supports(17) ? "d" : "z"); //1.21.2+ = f, 1.19 = e, 1.18 = d, 1.16 = z
return new Vector(x, y, z);
}
}

View file

@ -1,6 +1,6 @@
/* /*
* This file is part of MapReflectionAPI. * This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View file

@ -18,6 +18,9 @@
package tech.sbdevelopment.mapreflectionapi.utils; package tech.sbdevelopment.mapreflectionapi.utils;
import java.util.Map;
import java.util.function.Supplier;
public class MainUtil { public class MainUtil {
private MainUtil() { private MainUtil() {
} }
@ -35,10 +38,4 @@ public class MainUtil {
return true; return true;
} }
} }
public static <A, B> void validateArrayDimensions(A[][] arrayOne, B[][] arrayTwo) {
if (arrayOne.length != arrayTwo.length || arrayOne[0].length != arrayTwo[0].length) {
throw new IllegalArgumentException("The dimensions of two provided arrays (" + arrayOne.getClass().getName() + ", " + arrayTwo.getClass().getName() + ") do not match!");
}
}
} }

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