Compare commits

..

20 commits

Author SHA1 Message Date
ef91b97d1f
Legacy NMS to 1.21.2/3 2024-11-07 19:30:20 +01:00
5f9cbe7425
Re-update based on missing commit 2024-06-30 20:33:07 +02:00
bc055e0041
Merge remote-tracking branch 'origin/legacy/nms' into legacy/nms
# Conflicts:
#	.idea/encodings.xml
#	pom.xml
2024-06-30 20:26:03 +02:00
39dcfc8b31
Added 1.21 to legacy NMS branch (untested) 2024-06-30 20:25:39 +02:00
Stijn Bannink
874b7d227c Added 1.20.5 support in NMS branch 2024-04-29 16:43:23 +02:00
Stijn Bannink
4f187b92bb LEGACY: Added v1_20_R3 2023-12-22 20:38:54 +01:00
Stijn Bannink
592c270e68 Bumped legacy NMS branch to 1.20.2 2023-09-22 14:35:41 +02:00
Stijn Bannink
fa136c4955 Fixed build issue 2023-07-05 19:29:23 +02:00
Stijn Bannink
230d7fc5b2 Fixed deploy again (revision problem) 2023-07-05 12:04:21 +02:00
Stijn Bannink
a3ba68434f Bumped README version 2023-07-05 11:54:49 +02:00
Stijn Bannink
e9b19fa2f1 Fixed deploy 2023-07-05 11:49:59 +02:00
Stijn Bannink
db517ea09b
Merge pull request #19 from SBDPlugins/feature/rewrite-from-legacy
v1.5: Rewrite from legacy NMS implementation
2023-07-05 11:34:07 +02:00
Stijn Bannink
8afc1fb202 Fixed v1.20(.1), small startup print improvements 2023-07-05 11:33:48 +02:00
Stijn Bannink
3ac1042894 Added javadoc 2023-07-04 17:47:30 +02:00
Stijn Bannink
336d9626e1 Readded copyright files 2023-07-04 17:35:14 +02:00
Stijn Bannink
24583880a2 Bumped to v1.5 2023-07-04 17:33:05 +02:00
Stijn Bannink
b163e57563 Disabled GitHub actions 2023-07-04 17:32:39 +02:00
Stijn Bannink
6db8a11f5c Updated copyright 2023-07-04 17:31:22 +02:00
Stijn Bannink
6840d732ad Restored some .idea files 2023-07-04 17:26:57 +02:00
Stijn Bannink
fd225647f5 Merge between master and legacy/nms 2023-07-04 17:24:35 +02:00
110 changed files with 8971 additions and 3975 deletions

1
.gitignore vendored
View file

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

View file

@ -1,6 +1,6 @@
<component name="CopyrightManager">
<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="myName" value="SBDevelopment" />
</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="myName" value="SBDevelopment" />
</copyright>
</component>

7
.idea/copyright/profiles_settings.xml generated Normal file
View file

@ -0,0 +1,7 @@
<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"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

20
.idea/encodings.xml generated
View file

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<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/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_12_R1/src/main/java" charset="UTF-8" />
@ -17,8 +21,20 @@
<file url="file://$PROJECT_DIR$/NMS-v1_17_R1/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_18_R2/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_18_R2/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R3/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R3/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R1/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_20_R1/src/main/resources" charset="UTF-8" />
<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/resources" charset="UTF-8" />
</component>

16
.idea/misc.xml generated
View file

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

12
.idea/ros.xml generated Normal file
View file

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

178
API/pom.xml Normal file
View file

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-API</artifactId>
<properties>
<jdk.version>11</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>com.bergerkiller.bukkit.common</pattern>
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bkcommonlib</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>tech.sbdevelopment.mapreflectionapi.libs.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.20.0</version>
<configuration>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<outputDirectory>${maven.lombok.delombok-target}</outputDirectory>
<addOutputDirectory>false</addOutputDirectory>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<release>${jdk.version}</release>
<sourcepath>${maven.lombok.delombok-target}</sourcepath>
<sourceFileExcludes>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/io/*.java</sourceFileExclude>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/map/*.java</sourceFileExclude>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/map/color/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/cmd/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/managers/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/utils/*.java</sourceFileExclude>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/listeners/*.java</sourceFileExclude>
</sourceFileExcludes>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>plugin.yml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>plugin.yml</exclude>
</excludes>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>MG-Dev Jenkins CI Maven Repository</id>
<url>https://ci.mg-dev.eu/plugin/repository/everything</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
<!-- Libraries below are provided by Spigot -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>23.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>4.1.77.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,6 +19,7 @@
package tech.sbdevelopment.mapreflectionapi.api;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
@ -116,4 +117,13 @@ public interface MapController extends IMapController {
* @param frame {@link ItemFrame} to clear
*/
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.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,29 +18,47 @@
package tech.sbdevelopment.mapreflectionapi.api;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.
*/
public class MapManager {
protected final Set<Integer> occupiedIds = new HashSet<>();
protected final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>();
private final Class<?> wrapperClass;
public MapManager(JavaPlugin plugin) throws IllegalStateException {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
plugin.getLogger().info("Enabling MapManager for " + version + "...");
try {
final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.MapWrapper_" + version);
if (MapWrapper.class.isAssignableFrom(clazz)) {
wrapperClass = clazz;
} else {
throw new IllegalStateException("Plugin corrupted! Detected invalid MapWrapper class.");
}
} catch (Exception ex) {
throw new IllegalStateException("This Spigot version (" + version + ") is not supported! Contact the developer to get support.");
}
}
/**
* Get the amount of maps managed by the plugin
@ -129,9 +147,15 @@ public class MapManager {
* @return The wrapper
*/
private MapWrapper wrapNewImage(ArrayImage image) {
MapWrapper wrapper = new MapWrapper(image);
managedMaps.add(wrapper);
return wrapper;
try {
MapWrapper wrapper = (MapWrapper) wrapperClass.getDeclaredConstructor(ArrayImage.class).newInstance(image);
managedMaps.add(wrapper);
return wrapper;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
/**
@ -177,30 +201,6 @@ public class MapManager {
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
*

View file

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

View file

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

View file

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

View file

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

View file

@ -20,15 +20,23 @@ package tech.sbdevelopment.mapreflectionapi.api.events;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.api.events.types.CancellableEvent;
import org.bukkit.event.Cancellable;
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
*/
@RequiredArgsConstructor
@Getter
public class MapCancelEvent extends CancellableEvent {
public class MapCancelEvent extends Event implements Cancellable {
private static final HandlerList handlerList = new HandlerList();
@Setter
private boolean cancelled;
private final Player player;
private final int id;
@ -44,4 +52,9 @@ public class MapCancelEvent extends CancellableEvent {
this.player = player;
this.id = id;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlerList;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,80 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.listeners;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.Vector;
public abstract class PacketListener implements Listener {
protected JavaPlugin plugin;
public static PacketListener construct(JavaPlugin plugin) throws IllegalStateException {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
plugin.getLogger().info("Enabling PacketListener for " + version + "...");
try {
final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.PacketListener_" + version);
if (PacketListener.class.isAssignableFrom(clazz)) {
return (PacketListener) clazz.getDeclaredConstructor().newInstance();
} else {
throw new IllegalStateException("Plugin corrupted! Detected invalid PacketListener class.");
}
} catch (Exception ex) {
throw new IllegalStateException("This Minecraft version (" + version + ") is not supported! Contact the developer to get support.");
}
}
public void init(JavaPlugin plugin) {
this.plugin = plugin;
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@EventHandler
public void onJoin(PlayerJoinEvent e) {
injectPlayer(e.getPlayer());
}
@EventHandler
public void onQuit(PlayerQuitEvent e) {
removePlayer(e.getPlayer());
}
protected abstract void injectPlayer(Player p);
public abstract void removePlayer(Player p);
protected abstract Vector vec3DToVector(Object vec3d);
protected boolean hasField(Object packet, String field) {
try {
packet.getClass().getDeclaredField(field);
return true;
} catch (NoSuchFieldException ex) {
return false;
}
}
}

View file

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

View file

@ -18,9 +18,6 @@
package tech.sbdevelopment.mapreflectionapi.utils;
import java.util.Map;
import java.util.function.Supplier;
public class MainUtil {
private MainUtil() {
}
@ -38,4 +35,10 @@ public class MainUtil {
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!");
}
}
}

View file

@ -0,0 +1,537 @@
/*
* 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,6 +1,6 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -143,12 +143,14 @@ public class UpdateManager {
File pluginFile = getPluginFile(); // /plugins/XXX.jar
if (pluginFile == null) {
this.downloadResponse.accept(DownloadResponse.ERROR, null);
Bukkit.getLogger().info("Pluginfile is null");
return;
}
File updateFolder = Bukkit.getUpdateFolderFile();
if (!updateFolder.exists()) {
if (!updateFolder.mkdirs()) {
this.downloadResponse.accept(DownloadResponse.ERROR, null);
Bukkit.getLogger().info("Updatefolder doesn't exists, and can't be made");
return;
}
}

View file

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

124
Dist/pom.xml Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>MapReflectionAPI-Dist</artifactId>
<build>
<directory>../target</directory>
<finalName>${project.parent.name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.0.0-M2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<!-- Include all the dependencies required by the plugin -->
<artifactSet>
<includes>
<include>tech.sbdevelopment:MapReflectionAPI*</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_20_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>

72
NMS-v1_12_R1/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_12_R1</artifactId>
<properties>
<NMSVersion>1.12.2-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>11</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,166 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_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

@ -0,0 +1,249 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_12_R1 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_12_R1.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_12_R1.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_12_R1.sendMap(id, MapWrapper_v1_12_R1.this.content, player);
} else {
MapSender_v1_12_R1.addToQueue(id, MapWrapper_v1_12_R1.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_12_R1.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().defaultContainer.windowId;
ItemStack stack = new ItemStack(Material.MAP, 1);
net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_12_R1.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().getEntity(entityId);
if (entity == null) return null;
org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.server.v1_12_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
if (nmsStack.getTag() == null) nmsStack.setTag(new NBTTagCompound()); //No orCreate on 1.12.2!
nmsStack.getTag().setInt("map", mapId); //getTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.server.v1_12_R1.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_12_R1.ItemStack>) getDeclaredField(EntityItemFrame.class, "c");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
};
public MapWrapper_v1_12_R1(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class PacketListener_v1_12_R1 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap) {
PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet;
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity) {
PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet;
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.a(); //action
EnumHand hand = packetPlayInUseEntity.b(); //hand
Vec3D pos = packetPlayInUseEntity.c(); //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet;
int slot = packetPlayInSetCreativeSlot.a();
ItemStack item = packetPlayInSetCreativeSlot.getItemStack();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline();
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel;
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0);
Vec3D vec3dObj = (Vec3D) vec3d;
return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z);
}
}

72
NMS-v1_13_R2/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_13_R2</artifactId>
<properties>
<NMSVersion>1.13.2-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>11</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,166 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_13_R2.PacketPlayOutMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The {@link MapSender_v1_13_R2} sends the Map packets to players.
*/
public class MapSender_v1_13_R2 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_13_R2() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //???
new ArrayList<>(), //Icons
content.array, //Data
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY //Y size (2nd Y pos)
);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
} catch (Exception e) {
e.printStackTrace();
}
});
}
static final class QueuedMap {
private final int id;
private final ArrayImage image;
private final Player player;
QueuedMap(int id, ArrayImage image, Player player) {
this.id = id;
this.image = image;
this.player = player;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (QueuedMap) obj;
return this.id == that.id &&
Objects.equals(this.image, that.image) &&
Objects.equals(this.player, that.player);
}
@Override
public int hashCode() {
return Objects.hash(id, image, player);
}
@Override
public String toString() {
return "QueuedMap[" +
"id=" + id + ", " +
"image=" + image + ", " +
"player=" + player + ']';
}
}
}

View file

@ -0,0 +1,248 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_13_R2.*;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_13_R2 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_13_R2.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_13_R2.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_13_R2.sendMap(id, MapWrapper_v1_13_R2.this.content, player);
} else {
MapSender_v1_13_R2.addToQueue(id, MapWrapper_v1_13_R2.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_13_R2.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().defaultContainer.windowId;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.server.v1_13_R2.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_13_R2.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().getEntity(entityId);
if (entity == null) return null;
org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.server.v1_13_R2.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.server.v1_13_R2.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_13_R2.ItemStack>) getDeclaredField(EntityItemFrame.class, "e");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
};
public MapWrapper_v1_13_R2(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.server.v1_13_R2.*;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class PacketListener_v1_13_R2 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap) {
PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet;
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity) {
PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet;
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action
EnumHand hand = packetPlayInUseEntity.c(); //hand
Vec3D pos = packetPlayInUseEntity.d(); //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet;
int slot = packetPlayInSetCreativeSlot.b();
ItemStack item = packetPlayInSetCreativeSlot.getItemStack();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline();
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel;
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0);
Vec3D vec3dObj = (Vec3D) vec3d;
return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z);
}
}

72
NMS-v1_14_R1/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_14_R1</artifactId>
<properties>
<NMSVersion>1.14.4-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>11</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,167 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_14_R1.PacketPlayOutMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The {@link MapSender_v1_14_R1} sends the Map packets to players.
*/
public class MapSender_v1_14_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_14_R1() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //Tracking position
false, //Locked
new ArrayList<>(), //Icons
content.array, //Data
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY //Y size (2nd Y pos)
);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
} catch (Exception e) {
e.printStackTrace();
}
});
}
static final class QueuedMap {
private final int id;
private final ArrayImage image;
private final Player player;
QueuedMap(int id, ArrayImage image, Player player) {
this.id = id;
this.image = image;
this.player = player;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (QueuedMap) obj;
return this.id == that.id &&
Objects.equals(this.image, that.image) &&
Objects.equals(this.player, that.player);
}
@Override
public int hashCode() {
return Objects.hash(id, image, player);
}
@Override
public String toString() {
return "QueuedMap[" +
"id=" + id + ", " +
"image=" + image + ", " +
"player=" + player + ']';
}
}
}

View file

@ -0,0 +1,248 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_14_R1.*;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_14_R1 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_14_R1.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_14_R1.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_14_R1.sendMap(id, MapWrapper_v1_14_R1.this.content, player);
} else {
MapSender_v1_14_R1.addToQueue(id, MapWrapper_v1_14_R1.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_14_R1.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().defaultContainer.windowId;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.server.v1_14_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_14_R1.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().getEntity(entityId);
if (entity == null) return null;
org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.server.v1_14_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.server.v1_14_R1.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_14_R1.ItemStack>) getDeclaredField(EntityItemFrame.class, "ITEM");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
};
public MapWrapper_v1_14_R1(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.server.v1_14_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class PacketListener_v1_14_R1 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap) {
PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet;
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity) {
PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet;
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action
EnumHand hand = packetPlayInUseEntity.c(); //hand
Vec3D pos = packetPlayInUseEntity.d(); //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet;
int slot = packetPlayInSetCreativeSlot.b();
ItemStack item = packetPlayInSetCreativeSlot.getItemStack();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline();
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel;
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0);
Vec3D vec3dObj = (Vec3D) vec3d;
return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z);
}
}

72
NMS-v1_15_R1/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_15_R1</artifactId>
<properties>
<NMSVersion>1.15.2-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>11</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,167 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_15_R1.PacketPlayOutMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The {@link MapSender_v1_15_R1} sends the Map packets to players.
*/
public class MapSender_v1_15_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_15_R1() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //Tracking position
false, //Locked
new ArrayList<>(), //Icons
content.array, //Data
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY //Y size (2nd Y pos)
);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
} catch (Exception e) {
e.printStackTrace();
}
});
}
static final class QueuedMap {
private final int id;
private final ArrayImage image;
private final Player player;
QueuedMap(int id, ArrayImage image, Player player) {
this.id = id;
this.image = image;
this.player = player;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (QueuedMap) obj;
return this.id == that.id &&
Objects.equals(this.image, that.image) &&
Objects.equals(this.player, that.player);
}
@Override
public int hashCode() {
return Objects.hash(id, image, player);
}
@Override
public String toString() {
return "QueuedMap[" +
"id=" + id + ", " +
"image=" + image + ", " +
"player=" + player + ']';
}
}
}

View file

@ -0,0 +1,249 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_15_R1.*;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_15_R1 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_15_R1.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_15_R1.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_15_R1.sendMap(id, MapWrapper_v1_15_R1.this.content, player);
} else {
MapSender_v1_15_R1.addToQueue(id, MapWrapper_v1_15_R1.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_15_R1.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().defaultContainer.windowId;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.server.v1_15_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_15_R1.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().getEntity(entityId);
if (entity == null) return null;
org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.server.v1_15_R1.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.server.v1_15_R1.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_15_R1.ItemStack>) getDeclaredField(EntityItemFrame.class, "ITEM");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
};
public MapWrapper_v1_15_R1(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.server.v1_15_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class PacketListener_v1_15_R1 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap) {
PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet;
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity) {
PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet;
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action
EnumHand hand = packetPlayInUseEntity.c(); //hand
Vec3D pos = packetPlayInUseEntity.d(); //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet;
int slot = packetPlayInSetCreativeSlot.b();
ItemStack item = packetPlayInSetCreativeSlot.getItemStack();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline();
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel;
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0);
Vec3D vec3dObj = (Vec3D) vec3d;
return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z);
}
}

72
NMS-v1_16_R3/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_16_R3</artifactId>
<properties>
<NMSVersion>1.16.4-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>11</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,167 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_16_R3.PacketPlayOutMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The {@link MapSender_v1_16_R3} sends the Map packets to players.
*/
public class MapSender_v1_16_R3 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_16_R3() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //Tracking position
false, //Locked
new ArrayList<>(), //Icons
content.array, //Data
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY //Y size (2nd Y pos)
);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
} catch (Exception e) {
e.printStackTrace();
}
});
}
static final class QueuedMap {
private final int id;
private final ArrayImage image;
private final Player player;
QueuedMap(int id, ArrayImage image, Player player) {
this.id = id;
this.image = image;
this.player = player;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (QueuedMap) obj;
return this.id == that.id &&
Objects.equals(this.image, that.image) &&
Objects.equals(this.player, that.player);
}
@Override
public int hashCode() {
return Objects.hash(id, image, player);
}
@Override
public String toString() {
return "QueuedMap[" +
"id=" + id + ", " +
"image=" + image + ", " +
"player=" + player + ']';
}
}
}

View file

@ -0,0 +1,248 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.server.v1_16_R3.*;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_16_R3 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_16_R3.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_16_R3.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_16_R3.sendMap(id, MapWrapper_v1_16_R3.this.content, player);
} else {
MapSender_v1_16_R3.addToQueue(id, MapWrapper_v1_16_R3.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_16_R3.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().defaultContainer.windowId;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.server.v1_16_R3.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, slot, nmsStack);
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_16_R3.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().getEntity(entityId);
if (entity == null) return null;
org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
if (bukkitEntity instanceof ItemFrame) return (ItemFrame) bukkitEntity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.server.v1_16_R3.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.server.v1_16_R3.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.server.v1_16_R3.ItemStack>) getDeclaredField(EntityItemFrame.class, "ITEM");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
};
public MapWrapper_v1_16_R3(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.server.v1_16_R3.*;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class PacketListener_v1_16_R3 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap) {
PacketPlayOutMap packetPlayOutMap = (PacketPlayOutMap) packet;
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity) {
PacketPlayInUseEntity packetPlayInUseEntity = (PacketPlayInUseEntity) packet;
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
PacketPlayInUseEntity.EnumEntityUseAction action = packetPlayInUseEntity.b(); //action
EnumHand hand = packetPlayInUseEntity.c(); //hand
Vec3D pos = packetPlayInUseEntity.d(); //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, action.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) packet;
int slot = packetPlayInSetCreativeSlot.b();
ItemStack item = packetPlayInSetCreativeSlot.getItemStack();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel.pipeline();
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().playerConnection.networkManager.channel;
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D)) return new Vector(0, 0, 0);
Vec3D vec3dObj = (Vec3D) vec3d;
return new Vector(vec3dObj.x, vec3dObj.y, vec3dObj.z);
}
}

72
NMS-v1_17_R1/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_17_R1</artifactId>
<properties>
<NMSVersion>1.17.1-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>17</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,138 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
/**
* The {@link MapSender_v1_17_R1} sends the Map packets to players.
*/
public class MapSender_v1_17_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_17_R1() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
WorldMap.b updateData = new WorldMap.b(
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY, //Y size (2nd Y pos)
content.array //Data
);
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //Show icons
new ArrayList<>(), //Icons
updateData
);
((CraftPlayer) player).getHandle().b.sendPacket(packet); //connection send()
} catch (Exception e) {
e.printStackTrace();
}
});
}
record QueuedMap(int id, ArrayImage image, Player player) {
}
}

View file

@ -0,0 +1,251 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.network.syncher.DataWatcherObject;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.EntityItemFrame;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_17_R1 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_17_R1.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_17_R1.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_17_R1.sendMap(id, MapWrapper_v1_17_R1.this.content, player);
} else {
MapSender_v1_17_R1.addToQueue(id, MapWrapper_v1_17_R1.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_17_R1.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().bU.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().bU.getStateId(); //inventoryMenu getStateId()
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack);
((CraftPlayer) player).getHandle().b.sendPacket(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_17_R1.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().getEntity(entityId);
if (entity == null) return null;
if (entity instanceof ItemFrame) return (ItemFrame) entity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.getOrCreateTag().setInt("map", mapId); //getOrCreateTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.world.item.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.world.item.ItemStack>) getDeclaredField(EntityItemFrame.class, "ao");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().b.sendPacket(packet);
}
};
public MapWrapper_v1_17_R1(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,121 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
public class PacketListener_v1_17_R1 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap packetPlayOutMap) {
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) {
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action
Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type
EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand
Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) {
int slot = packetPlayInSetCreativeSlot.b();
ItemStack item = packetPlayInSetCreativeSlot.getItemStack();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().b.a.k.pipeline(); //connection connection channel
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().b.a.k; //connection connection channel
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0);
return new Vector(vec3dObj.b, vec3dObj.c, vec3dObj.d); //x, y, z
}
}

72
NMS-v1_18_R2/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_18_R2</artifactId>
<properties>
<NMSVersion>1.18.2-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>17</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,138 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
/**
* The {@link MapSender_v1_18_R2} sends the Map packets to players.
*/
public class MapSender_v1_18_R2 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_18_R2() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
WorldMap.b updateData = new WorldMap.b(
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY, //Y size (2nd Y pos)
content.array //Data
);
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //Show icons
new ArrayList<>(), //Icons
updateData
);
((CraftPlayer) player).getHandle().b.a(packet); //connection send()
} catch (Exception e) {
e.printStackTrace();
}
});
}
record QueuedMap(int id, ArrayImage image, Player player) {
}
}

View file

@ -0,0 +1,251 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.network.syncher.DataWatcherObject;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.EntityItemFrame;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.getDeclaredField;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.setDeclaredField;
public class MapWrapper_v1_18_R2 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_18_R2.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_18_R2.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_18_R2.sendMap(id, MapWrapper_v1_18_R2.this.content, player);
} else {
MapSender_v1_18_R2.addToQueue(id, MapWrapper_v1_18_R2.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_18_R2.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().bU.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().bU.j(); //inventoryMenu getStateId()
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack);
((CraftPlayer) player).getHandle().b.a(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_18_R2.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().a(entityId);
if (entity == null) return null;
if (entity instanceof ItemFrame) return (ItemFrame) entity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.u().a("map", mapId); //getOrCreateTag putInt
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, new DataWatcher(null), true);
try {
List<DataWatcher.Item<?>> list = new ArrayList<>();
DataWatcherObject<net.minecraft.world.item.ItemStack> dataWatcherObject = (DataWatcherObject<net.minecraft.world.item.ItemStack>) getDeclaredField(EntityItemFrame.class, "ao");
DataWatcher.Item<?> dataWatcherItem = new DataWatcher.Item<>(dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
setDeclaredField(packet, "b", list);
} catch (Exception e) {
e.printStackTrace();
return;
}
((CraftPlayer) player).getHandle().b.a(packet);
}
};
public MapWrapper_v1_18_R2(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,121 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
public class PacketListener_v1_18_R2 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap packetPlayOutMap) {
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) {
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action
Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type
EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand
Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) {
int slot = packetPlayInSetCreativeSlot.b();
ItemStack item = packetPlayInSetCreativeSlot.c();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
ChannelPipeline pipeline = ((CraftPlayer) p).getHandle().b.a.m.pipeline(); //connection connection channel
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
Channel channel = ((CraftPlayer) p).getHandle().b.a.m; //connection connection channel
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0);
return new Vector(vec3dObj.b, vec3dObj.c, vec3dObj.d); //x, y, z
}
}

72
NMS-v1_19_R3/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_19_R3</artifactId>
<properties>
<NMSVersion>1.19.4-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>17</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,138 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
/**
* The {@link MapSender_v1_19_R3} sends the Map packets to players.
*/
public class MapSender_v1_19_R3 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_19_R3() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
/**
* Send a map to a player
*
* @param id0 The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void sendMap(final int id0, final ArrayImage content, final Player player) {
if (player == null || !player.isOnline()) {
List<QueuedMap> toRemove = new ArrayList<>();
for (QueuedMap qMap : sendQueue) {
if (qMap == null) continue;
if (qMap.player == null || !qMap.player.isOnline()) {
toRemove.add(qMap);
}
}
Bukkit.getScheduler().cancelTask(senderID);
sendQueue.removeAll(toRemove);
return;
}
final int id = -id0;
Bukkit.getScheduler().runTaskAsynchronously(MapReflectionAPI.getInstance(), () -> {
try {
WorldMap.b updateData = new WorldMap.b(
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY, //Y size (2nd Y pos)
content.array //Data
);
PacketPlayOutMap packet = new PacketPlayOutMap(
id, //ID
(byte) 0, //Scale
false, //Show icons
new ArrayList<>(), //Icons
updateData
);
((CraftPlayer) player).getHandle().b.a(packet); //connection send()
} catch (Exception e) {
e.printStackTrace();
}
});
}
record QueuedMap(int id, ArrayImage image, Player player) {
}
}

View file

@ -0,0 +1,239 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.EntityItemFrame;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
public class MapWrapper_v1_19_R3 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_19_R3.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_19_R3.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_19_R3.sendMap(id, MapWrapper_v1_19_R3.this.content, player);
} else {
MapSender_v1_19_R3.addToQueue(id, MapWrapper_v1_19_R3.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_19_R3.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().bO.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().bO.j(); //inventoryMenu getStateId()
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack);
((CraftPlayer) player).getHandle().b.a(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_19_R3.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().a(entityId);
if (entity == null) return null;
if (entity instanceof ItemFrame) return (ItemFrame) entity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.v().a("map", mapId); //getOrCreateTag putInt
List<DataWatcher.b<?>> list = new ArrayList<>();
DataWatcher.b<?> dataWatcherItem = DataWatcher.b.a(EntityItemFrame.g, nmsStack);
list.add(dataWatcherItem);
PacketPlayOutEntityMetadata packet = new PacketPlayOutEntityMetadata(entityId, list);
((CraftPlayer) player).getHandle().b.a(packet);
}
};
public MapWrapper_v1_19_R3(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,125 @@
/*
* 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
}
}

72
NMS-v1_20_R2/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_20_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

@ -0,0 +1,138 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_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

@ -0,0 +1,239 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.EntityItemFrame;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_20_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

@ -0,0 +1,126 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_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
}
}

72
NMS-v1_20_R3/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_20_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

@ -0,0 +1,138 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_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

@ -0,0 +1,239 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.EntityItemFrame;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_20_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

@ -0,0 +1,126 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_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
}
}

72
NMS-v1_20_R4/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_20_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

@ -0,0 +1,141 @@
/*
* 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

@ -0,0 +1,243 @@
/*
* 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

@ -0,0 +1,126 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_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
}
}

72
NMS-v1_21_R1/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_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

@ -0,0 +1,141 @@
/*
* 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

@ -0,0 +1,243 @@
/*
* 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

@ -0,0 +1,126 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_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
}
}

72
NMS-v1_21_R2/pom.xml Normal file
View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_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

@ -0,0 +1,141 @@
/*
* 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

@ -0,0 +1,242 @@
/*
* 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;
}
}

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