Merge between master and legacy/nms

This commit is contained in:
Stijn Bannink 2023-07-04 17:24:35 +02:00
parent 040a5014a4
commit fd225647f5
89 changed files with 5775 additions and 874 deletions

8
.idea/.gitignore generated vendored
View file

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -1,6 +0,0 @@
<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>
</component>

7
.idea/discord.xml generated
View file

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

8
.idea/encodings.xml generated
View file

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Encoding"> <component name="Encoding">
<file url="file://$PROJECT_DIR$/API/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/API/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Dist/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Dist/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/Dist/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/Dist/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_12_R1/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_12_R1/src/main/java" charset="UTF-8" />
@ -19,6 +21,12 @@
<file url="file://$PROJECT_DIR$/NMS-v1_18_R2/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_18_R2/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/NMS-v1_19_R1/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R2/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R2/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_R3/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/NMS-v1_19_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$/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component> </component>

17
.idea/misc.xml generated
View file

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

2
.idea/vcs.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="" vcs="Git" />
</component> </component>
</project> </project>

183
API/pom.xml Normal file
View file

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<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

@ -38,6 +38,7 @@ import java.util.logging.Level;
public class MapReflectionAPI extends JavaPlugin { public class MapReflectionAPI extends JavaPlugin {
private static MapReflectionAPI instance; private static MapReflectionAPI instance;
private static MapManager mapManager; private static MapManager mapManager;
private static PacketListener packetListener;
/** /**
* Get the plugin instance * Get the plugin instance
@ -89,8 +90,24 @@ public class MapReflectionAPI extends JavaPlugin {
getLogger().info("Loading the commands..."); getLogger().info("Loading the commands...");
getCommand("mapmanager").setExecutor(new MapManagerCMD()); getCommand("mapmanager").setExecutor(new MapManagerCMD());
getLogger().info("Loading the packet listener...");
try {
packetListener = PacketListener.construct(this);
} catch (IllegalStateException e) {
getLogger().log(Level.SEVERE, e.getMessage(), e);
Bukkit.getPluginManager().disablePlugin(this);
return;
}
packetListener.init(this);
getLogger().info("Loading the map manager..."); getLogger().info("Loading the map manager...");
mapManager = new MapManager(); try {
mapManager = new MapManager(this);
} catch (IllegalStateException e) {
getLogger().log(Level.SEVERE, e.getMessage(), e);
Bukkit.getPluginManager().disablePlugin(this);
return;
}
if (Configuration.getInstance().isAllowVanilla()) { if (Configuration.getInstance().isAllowVanilla()) {
getLogger().info("Vanilla Maps are allowed. Discovering occupied Map IDs..."); getLogger().info("Vanilla Maps are allowed. Discovering occupied Map IDs...");
@ -113,7 +130,6 @@ public class MapReflectionAPI extends JavaPlugin {
getLogger().info("Registering the listeners..."); getLogger().info("Registering the listeners...");
Bukkit.getPluginManager().registerEvents(new MapListener(), this); Bukkit.getPluginManager().registerEvents(new MapListener(), this);
Bukkit.getPluginManager().registerEvents(new PacketListener(), this);
getLogger().info("Loading metrics..."); getLogger().info("Loading metrics...");
Metrics metrics = new Metrics(this, 16033); Metrics metrics = new Metrics(this, 16033);
@ -167,6 +183,9 @@ public class MapReflectionAPI extends JavaPlugin {
@Override @Override
public void onDisable() { public void onDisable() {
getLogger().info("Disabling the packet handler...");
if (packetListener != null) Bukkit.getOnlinePlayers().forEach(p -> packetListener.removePlayer(p));
getLogger().info("MapReflectionAPI is disabled!"); getLogger().info("MapReflectionAPI is disabled!");
instance = null; instance = null;

View file

@ -33,4 +33,4 @@ public abstract class AbstractMapWrapper {
getController().cancelSend(); getController().cancelSend();
getController().clearViewers(); getController().clearViewers();
} }
} }

View file

@ -18,13 +18,17 @@
package tech.sbdevelopment.mapreflectionapi.api; package tech.sbdevelopment.mapreflectionapi.api;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException; import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import tech.sbdevelopment.mapreflectionapi.managers.Configuration; import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -36,6 +40,25 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MapManager { public class MapManager {
protected final Set<Integer> occupiedIds = new HashSet<>(); protected final Set<Integer> occupiedIds = new HashSet<>();
protected final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>(); protected final List<MapWrapper> managedMaps = new CopyOnWriteArrayList<>();
private final Class<?> wrapperClass;
public MapManager(JavaPlugin plugin) throws IllegalStateException {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
MapReflectionAPI.getInstance().getLogger().info("Initializing the map manager for Minecraft version " + version + "...");
try {
final Class<?> clazz = Class.forName("tech.sbdevelopment.mapreflectionapi.nms.MapWrapper_" + version);
if (MapWrapper.class.isAssignableFrom(clazz)) {
wrapperClass = clazz;
} else {
throw new IllegalStateException("Plugin corrupted! Detected invalid MapWrapper class.");
}
} catch (Exception ex) {
throw new IllegalStateException("This Spigot version (" + version + ") is not supported! Contact the developer to get support.");
}
}
/** /**
* Get the amount of maps managed by the plugin * Get the amount of maps managed by the plugin
@ -124,9 +147,15 @@ public class MapManager {
* @return The wrapper * @return The wrapper
*/ */
private MapWrapper wrapNewImage(ArrayImage image) { private MapWrapper wrapNewImage(ArrayImage image) {
MapWrapper wrapper = new MapWrapper(image); try {
managedMaps.add(wrapper); MapWrapper wrapper = (MapWrapper) wrapperClass.getDeclaredConstructor(ArrayImage.class).newInstance(image);
return wrapper; managedMaps.add(wrapper);
return wrapper;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
e.printStackTrace();
return null;
}
} }
/** /**

View file

@ -0,0 +1,16 @@
package tech.sbdevelopment.mapreflectionapi.api;
public abstract class MapWrapper extends AbstractMapWrapper {
protected ArrayImage content;
public MapWrapper(ArrayImage image) {
this.content = image;
}
public ArrayImage getContent() {
return content;
}
@Override
public abstract MapController getController();
}

View file

@ -24,8 +24,8 @@ import lombok.Setter;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper; import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
/** /**
* This event gets fired when the content of a {@link MapWrapper} is updated * This event gets fired when the content of a {@link MapWrapper} is updated

View file

@ -0,0 +1,85 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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("Initializing the packet handler for Minecraft version " + 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;
}
}
}

129
Dist/pom.xml Normal file
View file

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>MapReflectionAPI-Dist</artifactId>
<build>
<directory>../target</directory>
<finalName>${project.parent.name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.0.0-M2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<!-- Include all the dependencies required by the plugin -->
<artifactSet>
<includes>
<include>tech.sbdevelopment:MapReflectionAPI*</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_20_R1</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_19_R3</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_18_R2</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_16_R3</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_17_R1</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_15_R1</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_14_R1</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_13_R2</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-NMS-v1_12_R1</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

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

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<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,151 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_12_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_12_R1() {
}
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);
}
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);
}
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,254 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,128 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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);
}
}

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

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<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,151 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_13_R2 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_13_R2() {
}
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);
}
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);
}
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,253 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,128 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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);
}
}

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

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<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,152 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_14_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_14_R1() {
}
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);
}
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);
}
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,253 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,128 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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);
}
}

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

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<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,152 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_15_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_15_R1() {
}
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);
}
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);
}
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,254 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,128 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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);
}
}

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

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<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,152 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_16_R3 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_16_R3() {
}
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);
}
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);
}
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,253 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,128 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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);
}
}

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

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<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,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_17_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_17_R1() {
}
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);
}
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);
}
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,256 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,126 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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
}
}

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

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<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,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_18_R2 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_18_R2() {
}
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);
}
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);
}
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,256 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,126 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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
}
}

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

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<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,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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;
public class MapSender_v1_19_R3 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_19_R3() {
}
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);
}
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);
}
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,244 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
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,131 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
public class PacketListener_v1_19_R3 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap packetPlayOutMap) {
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) {
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action
Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type
EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand
Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) {
int slot = packetPlayInSetCreativeSlot.a();
ItemStack item = packetPlayInSetCreativeSlot.c();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
//The connection is private since 1.19.4 :|
NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().b, "h");
ChannelPipeline pipeline = networkManager.m.pipeline(); //connection channel
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
//The connection is private since 1.19.4 :|
NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().b, "h");
Channel channel = networkManager.m; //connection channel
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0);
return new Vector(vec3dObj.c, vec3dObj.d, vec3dObj.e); //x, y, z
}
}

77
NMS-v1_20_R1/pom.xml Normal file
View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of MapReflectionAPI.
~ Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MapReflectionAPI</artifactId>
<groupId>tech.sbdevelopment</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MapReflectionAPI-NMS-v1_20_R1</artifactId>
<properties>
<NMSVersion>1.20.1-R0.1-SNAPSHOT</NMSVersion>
<jdk.version>17</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${jdk.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>${NMSVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI-API</artifactId>
<version>${revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,123 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.level.saveddata.maps.WorldMap;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import java.util.ArrayList;
import java.util.List;
public class MapSender_v1_20_R1 {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender_v1_20_R1() {
}
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);
}
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);
}
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,244 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
import net.minecraft.network.protocol.game.PacketPlayOutSetSlot;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.EntityItemFrame;
import org.bukkit.*;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.ArrayImage;
import tech.sbdevelopment.mapreflectionapi.api.MapController;
import tech.sbdevelopment.mapreflectionapi.api.MapWrapper;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import java.util.*;
public class MapWrapper_v1_20_R1 extends MapWrapper {
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(ArrayImage content) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper_v1_20_R1.this.content = duplicate.getContent();
return;
}
MapWrapper_v1_20_R1.this.content = content;
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender_v1_20_R1.sendMap(id, MapWrapper_v1_20_R1.this.content, player);
} else {
MapSender_v1_20_R1.addToQueue(id, MapWrapper_v1_20_R1.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender_v1_20_R1.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
CraftPlayer craftPlayer = (CraftPlayer) player;
int windowId = craftPlayer.getHandle().bQ.j; //inventoryMenu containerId
int stateId = craftPlayer.getHandle().bQ.j(); //inventoryMenu getStateId()
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
PacketPlayOutSetSlot packet = new PacketPlayOutSetSlot(windowId, stateId, slot, nmsStack);
((CraftPlayer) player).getHandle().c.a(packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != Material.FILLED_MAP && !force) return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != Material.FILLED_MAP && !force) return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(Material.FILLED_MAP, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata("MAP_WRAPPER_REF", MapReflectionAPI.getInstance());
frame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper_v1_20_R1.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
CraftWorld craftWorld = (CraftWorld) world;
Entity entity = craftWorld.getHandle().a(entityId);
if (entity == null) return null;
if (entity instanceof ItemFrame) return (ItemFrame) entity;
return null;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack);
nmsStack.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().c.a(packet);
}
};
public MapWrapper_v1_20_R1(ArrayImage image) {
super(image);
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -0,0 +1,131 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package tech.sbdevelopment.mapreflectionapi.nms;
import io.netty.channel.*;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import net.minecraft.network.protocol.game.PacketPlayOutMap;
import net.minecraft.world.EnumHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3D;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.listeners.PacketListener;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
public class PacketListener_v1_20_R1 extends PacketListener {
@Override
protected void injectPlayer(Player p) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
//On send packet
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet instanceof PacketPlayOutMap packetPlayOutMap) {
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
//It's one of our maps, invert ID and let through!
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId); //mapId
} else {
boolean async = !plugin.getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(p, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(p, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
//On receive packet
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet instanceof PacketPlayInUseEntity packetPlayInUseEntity) {
int entityId = (int) getDeclaredField(packetPlayInUseEntity, "a"); //entityId
Object action = getDeclaredField(packetPlayInUseEntity, "b"); //action
Enum<?> actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type
EnumHand hand = hasField(action, "a") ? (EnumHand) getDeclaredField(action, "a") : null; //hand
Vec3D pos = hasField(action, "b") ? (Vec3D) getDeclaredField(action, "b") : null; //pos
if (Bukkit.getScheduler().callSyncMethod(plugin, () -> {
boolean async = !plugin.getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(p, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet instanceof PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot) {
int slot = packetPlayInSetCreativeSlot.a();
ItemStack item = packetPlayInSetCreativeSlot.c();
boolean async = !plugin.getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(p, slot, CraftItemStack.asBukkitCopy(item), async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
//The connection is private since 1.19.4 :|
NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().c, "h");
ChannelPipeline pipeline = networkManager.m.pipeline(); //connection channel
pipeline.addBefore("packet_handler", p.getName(), channelDuplexHandler);
}
@Override
public void removePlayer(Player p) {
//The connection is private since 1.19.4 :|
NetworkManager networkManager = (NetworkManager) getField(((CraftPlayer) p).getHandle().c, "h");
Channel channel = networkManager.m; //connection channel
channel.eventLoop().submit(() -> channel.pipeline().remove(p.getName()));
}
@Override
protected Vector vec3DToVector(Object vec3d) {
if (!(vec3d instanceof Vec3D vec3dObj)) return new Vector(0, 0, 0);
return new Vector(vec3dObj.c, vec3dObj.d, vec3dObj.e); //x, y, z
}
}

3
TODO Normal file
View file

@ -0,0 +1,3 @@
- replace _V1xxxxx by package
- bump version
- test

165
pom.xml
View file

@ -24,18 +24,33 @@
<groupId>tech.sbdevelopment</groupId> <groupId>tech.sbdevelopment</groupId>
<artifactId>MapReflectionAPI</artifactId> <artifactId>MapReflectionAPI</artifactId>
<version>1.4.4</version> <packaging>pom</packaging>
<packaging>jar</packaging> <version>${revision}</version>
<name>MapReflectionAPI</name> <name>MapReflectionAPI</name>
<description>This API helps developer with viewing images on maps.</description> <description>This API helps developer with viewing images on maps.</description>
<url>https://sbdplugins.nl</url> <url>https://sbdplugins.nl</url>
<properties> <properties>
<revision>1.4.4</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.lombok.delombok-target>${project.build.directory}/javadoc-delombok</maven.lombok.delombok-target> <jdk.version>11</jdk.version>
</properties> </properties>
<modules>
<module>API</module>
<module>Dist</module>
<module>NMS-v1_20_R1</module>
<module>NMS-v1_19_R3</module>
<module>NMS-v1_18_R2</module>
<module>NMS-v1_17_R1</module>
<module>NMS-v1_16_R3</module>
<module>NMS-v1_15_R1</module>
<module>NMS-v1_14_R1</module>
<module>NMS-v1_13_R2</module>
<module>NMS-v1_12_R1</module>
</modules>
<distributionManagement> <distributionManagement>
<repository> <repository>
<id>sbdevelopment-repo</id> <id>sbdevelopment-repo</id>
@ -44,102 +59,46 @@
</distributionManagement> </distributionManagement>
<build> <build>
<defaultGoal>clean package</defaultGoal>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-toolchains-plugin</artifactId>
<version>3.10.1</version> <version>3.1.0</version>
<configuration>
<release>11</release>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase>
<goals> <goals>
<goal>shade</goal> <goal>toolchain</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> </goals>
</execution> </execution>
</executions> </executions>
<configuration>
<toolchains>
<jdk>
<version>${jdk.version}</version>
</jdk>
</toolchains>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.4.0</version> <version>3.11.0</version>
<configuration> <configuration>
<release>11</release> <release>${jdk.version}</release>
<sourcepath>${maven.lombok.delombok-target}</sourcepath> </configuration>
<sourceFileExcludes> </plugin>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/io/*.java</sourceFileExclude>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/map/*.java</sourceFileExclude> <plugin>
<sourceFileExclude>**/com/bergerkiller/bukkit/common/map/color/*.java</sourceFileExclude> <groupId>org.apache.maven.plugins</groupId>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/*.java</sourceFileExclude> <artifactId>maven-deploy-plugin</artifactId>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/cmd/*.java</sourceFileExclude> <version>3.1.1</version>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/managers/*.java</sourceFileExclude> <configuration>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/utils/*.java</sourceFileExclude> <skip>true</skip>
<sourceFileExclude>**/tech/sbdevelopment/mapreflectionapi/listeners/*.java</sourceFileExclude>
</sourceFileExcludes>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>plugin.yml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>plugin.yml</exclude>
</excludes>
</resource>
</resources>
</build> </build>
<repositories> <repositories>
@ -147,49 +106,13 @@
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository> </repository>
<repository>
<id>MG-Dev Jenkins CI Maven Repository</id>
<url>https://ci.mg-dev.eu/plugin/repository/everything</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.19.4-R0.1-SNAPSHOT</version> <version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.0</version>
<scope>compile</scope>
</dependency>
<!-- Libraries below are provided by Spigot -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>23.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>4.1.77.Final</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -1,165 +0,0 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.api;
import lombok.Data;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import java.util.ArrayList;
import java.util.List;
/**
* The {@link MapSender} sends the Map packets to players.
*/
public class MapSender {
private static final List<QueuedMap> sendQueue = new ArrayList<>();
private static int senderID = -1;
private MapSender() {
}
/**
* Add a map to the send queue
*
* @param id The ID of the map
* @param content The {@link ArrayImage} to view on the map
* @param player The {@link Player} to view for
*/
public static void addToQueue(final int id, final ArrayImage content, final Player player) {
QueuedMap toSend = new QueuedMap(id, content, player);
if (sendQueue.contains(toSend)) return;
sendQueue.add(toSend);
runSender();
}
/**
* Cancels a senderID in the sender queue
*
* @param s The senderID to cancel
*/
public static void cancelID(int s) {
sendQueue.removeIf(queuedMap -> queuedMap.id == s);
}
/**
* Run the sender task
*/
private static void runSender() {
if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.isEmpty())
return;
senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapReflectionAPI.getInstance(), () -> {
if (sendQueue.isEmpty()) return;
for (int i = 0; i < Math.min(sendQueue.size(), 10 + 1); i++) {
QueuedMap current = sendQueue.get(0);
if (current == null) return;
sendMap(current.id, current.image, current.player);
if (!sendQueue.isEmpty()) sendQueue.remove(0);
}
}, 0, 2);
}
private static final Class<?> packetPlayOutMapClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutMap");
private static final Class<?> worldMapData = ReflectionUtil.supports(17) ? ReflectionUtil.getNMSClass("world.level.saveddata.maps", "WorldMap$b") : null;
/**
* Send a map to a player
*
* @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;
Object packet;
if (ReflectionUtil.supports(17)) { //1.17+
Object updateData = ReflectionUtil.callConstructor(worldMapData,
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY, //Y size (2nd Y pos)
content.array //Data
);
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
id, //ID
(byte) 0, //Scale, 0 = 1 block per pixel
false, //Show icons
new ReflectionUtil.CollectionParam<>(), //Icons
updateData
);
} else if (ReflectionUtil.supports(14)) { //1.16-1.14
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
id, //ID
(byte) 0, //Scale, 0 = 1 block per pixel
false, //Tracking position
false, //Locked
new ReflectionUtil.CollectionParam<>(), //Icons
content.array, //Data
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY //Y size (2nd Y pos)
);
} else { //1.13-
packet = ReflectionUtil.callConstructor(packetPlayOutMapClass,
id, //ID
(byte) 0, //Scale, 0 = 1 block per pixel
false, //???
new ReflectionUtil.CollectionParam<>(), //Icons
content.array, //Data
content.minX, //X pos
content.minY, //Y pos
content.maxX, //X size (2nd X pos)
content.maxY //Y size (2nd Y pos)
);
}
ReflectionUtil.sendPacket(player, packet);
}
@Data
static final class QueuedMap {
private final int id;
private final ArrayImage image;
private final Player player;
}
}

View file

@ -1,384 +0,0 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.api;
import org.bukkit.*;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.NotNull;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.MapContentUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.exceptions.MapLimitExceededException;
import tech.sbdevelopment.mapreflectionapi.managers.Configuration;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* A {@link MapWrapper} wraps one image.
*/
public class MapWrapper extends AbstractMapWrapper {
private static final String REFERENCE_METADATA = "MAP_WRAPPER_REF";
protected ArrayImage content;
private static final Material MAP_MATERIAL;
static {
MAP_MATERIAL = ReflectionUtil.supports(13) ? Material.FILLED_MAP : Material.MAP;
}
/**
* Construct a new {@link MapWrapper}
*
* @param image The {@link ArrayImage} to wrap
*/
public MapWrapper(ArrayImage image) {
this.content = image;
}
private static final Class<?> craftStackClass = ReflectionUtil.getCraftClass("inventory.CraftItemStack");
private static final Class<?> setSlotPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutSetSlot");
private static final Class<?> entityClass = ReflectionUtil.getNMSClass("world.entity", "Entity");
private static final Class<?> dataWatcherClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher");
private static final Class<?> entityMetadataPacketClass = ReflectionUtil.getNMSClass("network.protocol.game", "PacketPlayOutEntityMetadata");
private static final Class<?> entityItemFrameClass = ReflectionUtil.getNMSClass("world.entity.decoration", "EntityItemFrame");
private static final Class<?> dataWatcherItemClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$Item");
protected MapController controller = new MapController() {
private final Map<UUID, Integer> viewers = new HashMap<>();
@Override
public void addViewer(Player player) throws MapLimitExceededException {
if (!isViewing(player)) {
viewers.put(player.getUniqueId(), MapReflectionAPI.getMapManager().getNextFreeIdFor(player));
}
}
@Override
public void removeViewer(OfflinePlayer player) {
viewers.remove(player.getUniqueId());
}
@Override
public void clearViewers() {
for (UUID uuid : viewers.keySet()) {
viewers.remove(uuid);
}
}
@Override
public boolean isViewing(OfflinePlayer player) {
if (player == null) return false;
return viewers.containsKey(player.getUniqueId());
}
@Override
public int getMapId(OfflinePlayer player) {
if (isViewing(player)) {
return viewers.get(player.getUniqueId());
}
return -1;
}
@Override
public void update(@NotNull ArrayImage content) {
MapContentUpdateEvent event = new MapContentUpdateEvent(MapWrapper.this, content);
Bukkit.getPluginManager().callEvent(event);
if (Configuration.getInstance().isImageCache()) {
MapWrapper duplicate = MapReflectionAPI.getMapManager().getDuplicate(content);
if (duplicate != null) {
MapWrapper.this.content = duplicate.getContent();
return;
}
}
MapWrapper.this.content = content;
if (event.isSendContent()) {
for (UUID id : viewers.keySet()) {
sendContent(Bukkit.getPlayer(id));
}
}
}
@Override
public void sendContent(Player player) {
sendContent(player, false);
}
@Override
public void sendContent(Player player, boolean withoutQueue) {
if (!isViewing(player)) return;
int id = getMapId(player);
if (withoutQueue) {
MapSender.sendMap(id, MapWrapper.this.content, player);
} else {
MapSender.addToQueue(id, MapWrapper.this.content, player);
}
}
@Override
public void cancelSend() {
for (int s : viewers.values()) {
MapSender.cancelID(s);
}
}
@Override
public void showInInventory(Player player, int slot, boolean force) {
if (!isViewing(player)) return;
if (player.getGameMode() == GameMode.CREATIVE && !force) return;
if (slot < 9) {
slot += 36;
} else if (slot > 35 && slot != 45) {
slot = 8 - (slot - 36);
}
String inventoryMenuName;
if (ReflectionUtil.supports(20)) { //1.20
inventoryMenuName = "bQ";
} else if (ReflectionUtil.supports(19)) { //1.19
inventoryMenuName = ReflectionUtil.VER_MINOR == 3 ? "bO" : "bT"; //1.19.4 = bO, >= 1.19.3 = bT
} else if (ReflectionUtil.supports(18)) { //1.18
inventoryMenuName = ReflectionUtil.VER_MINOR == 1 ? "bV" : "bU"; //1.18.1 = ap, 1.18(.2) = ao
} else if (ReflectionUtil.supports(17)) { //1.17, same as 1.18(.2)
inventoryMenuName = "bU";
} else { //1.12-1.16
inventoryMenuName = "defaultContainer";
}
Object inventoryMenu = ReflectionUtil.getField(ReflectionUtil.getHandle(player), inventoryMenuName);
ItemStack stack;
if (ReflectionUtil.supports(13)) {
stack = new ItemStack(MAP_MATERIAL, 1);
} else {
stack = new ItemStack(MAP_MATERIAL, 1, (short) getMapId(player));
}
Object nmsStack = createCraftItemStack(stack, (short) getMapId(player));
Object packet;
if (ReflectionUtil.supports(17)) { //1.17+
int stateId = (int) ReflectionUtil.callMethod(inventoryMenu, ReflectionUtil.supports(18) ? "j" : "getStateId");
packet = ReflectionUtil.callConstructor(setSlotPacketClass,
0, //0 = Player inventory
stateId,
slot,
nmsStack
);
} else { //1.16-
packet = ReflectionUtil.callConstructor(setSlotPacketClass,
0, //0 = Player inventory
slot,
nmsStack
);
}
ReflectionUtil.sendPacketSync(player, packet);
}
@Override
public void showInInventory(Player player, int slot) {
showInInventory(player, slot, false);
}
@Override
public void showInHand(Player player, boolean force) {
if (player.getInventory().getItemInMainHand().getType() != MAP_MATERIAL && !force)
return;
showInInventory(player, player.getInventory().getHeldItemSlot(), force);
}
@Override
public void showInHand(Player player) {
showInHand(player, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame) {
showInFrame(player, frame, false);
}
@Override
public void showInFrame(Player player, ItemFrame frame, boolean force) {
if (frame.getItem().getType() != MAP_MATERIAL && !force)
return;
showInFrame(player, frame.getEntityId());
}
@Override
public void showInFrame(Player player, int entityId) {
showInFrame(player, entityId, null);
}
@Override
public void showInFrame(Player player, int entityId, String debugInfo) {
if (!isViewing(player)) return;
ItemStack stack = new ItemStack(MAP_MATERIAL, 1);
if (debugInfo != null) {
ItemMeta itemMeta = stack.getItemMeta();
itemMeta.setDisplayName(debugInfo);
stack.setItemMeta(itemMeta);
}
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) {
frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance());
frame.setMetadata(REFERENCE_METADATA, new FixedMetadataValue(MapReflectionAPI.getInstance(), MapWrapper.this));
}
sendItemFramePacket(player, entityId, stack, getMapId(player));
});
}
@Override
public void clearFrame(Player player, int entityId) {
sendItemFramePacket(player, entityId, null, -1);
Bukkit.getScheduler().runTask(MapReflectionAPI.getInstance(), () -> {
ItemFrame frame = getItemFrameById(player.getWorld(), entityId);
if (frame != null) frame.removeMetadata(REFERENCE_METADATA, MapReflectionAPI.getInstance());
});
}
@Override
public void clearFrame(Player player, ItemFrame frame) {
clearFrame(player, frame.getEntityId());
}
@Override
public ItemFrame getItemFrameById(World world, int entityId) {
Object worldHandle = ReflectionUtil.getHandle(world);
Object nmsEntity = ReflectionUtil.callMethod(worldHandle, ReflectionUtil.supports(18) ? "a" : "getEntity", entityId);
if (nmsEntity == null) return null;
Object craftEntity = ReflectionUtil.callMethod(nmsEntity, "getBukkitEntity");
if (craftEntity == null) return null;
Class<?> itemFrameClass = ReflectionUtil.getNMSClass("world.entity.decoration", "EntityItemFrame");
if (itemFrameClass == null) return null;
if (craftEntity.getClass().isAssignableFrom(itemFrameClass))
return (ItemFrame) itemFrameClass.cast(craftEntity);
return null;
}
private Object createCraftItemStack(@NotNull ItemStack stack, int mapId) {
if (mapId < 0) return null;
Object nmsStack = ReflectionUtil.callMethod(craftStackClass, "asNMSCopy", stack);
if (ReflectionUtil.supports(13)) {
String nbtObjectName;
if (ReflectionUtil.supports(19)) { //1.19
nbtObjectName = "v";
} else if (ReflectionUtil.supports(18)) { //1.18
nbtObjectName = ReflectionUtil.VER_MINOR == 1 ? "t" : "u"; //1.18.1 = t, 1.18(.2) = u
} else { //1.13 - 1.17
nbtObjectName = "getOrCreateTag";
}
Object nbtObject = ReflectionUtil.callMethod(nmsStack, nbtObjectName);
ReflectionUtil.callMethod(nbtObject, ReflectionUtil.supports(18) ? "a" : "setInt", "map", mapId);
}
return nmsStack;
}
private void sendItemFramePacket(Player player, int entityId, ItemStack stack, int mapId) {
Object nmsStack = createCraftItemStack(stack, mapId);
String dataWatcherObjectName;
if (ReflectionUtil.supports(19)) { //1.19
dataWatcherObjectName = ReflectionUtil.VER_MINOR == 3 ? "g" : "ao"; //1.19.4 = g, >= 1.19.3 = ao
} else if (ReflectionUtil.supports(18)) { //1.18
dataWatcherObjectName = ReflectionUtil.VER_MINOR == 1 ? "ap" : "ao"; //1.18.1 = ap, 1.18(.2) = ao
} else if (ReflectionUtil.supports(17)) { //1.17
dataWatcherObjectName = "ao";
} else if (ReflectionUtil.supports(14)) { //1.14 - 1.16
dataWatcherObjectName = "ITEM";
} else if (ReflectionUtil.supports(13)) { //1.13
dataWatcherObjectName = "e";
} else { //1.12
dataWatcherObjectName = "c";
}
Object dataWatcherObject = ReflectionUtil.getDeclaredField(entityItemFrameClass, dataWatcherObjectName);
ReflectionUtil.ListParam list = new ReflectionUtil.ListParam<>();
Object packet;
if (ReflectionUtil.supports(19, 2)) { //1.19.3
Class<?> dataWatcherRecordClass = ReflectionUtil.getNMSClass("network.syncher", "DataWatcher$b");
// Sadly not possible to use ReflectionUtil (in its current state), because of the Object parameter
Object dataWatcherItem;
try {
Method m = dataWatcherRecordClass.getMethod("a", dataWatcherObject.getClass(), Object.class);
m.setAccessible(true);
dataWatcherItem = m.invoke(null, dataWatcherObject, nmsStack);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
return;
}
list.add(dataWatcherItem);
packet = ReflectionUtil.callConstructor(entityMetadataPacketClass,
entityId,
list
);
} else { //1.19.2 or lower
Object dataWatcher = ReflectionUtil.callConstructorNull(dataWatcherClass, entityClass);
packet = ReflectionUtil.callConstructor(entityMetadataPacketClass,
entityId,
dataWatcher, //dummy watcher!
true
);
Object dataWatcherItem = ReflectionUtil.callFirstConstructor(dataWatcherItemClass, dataWatcherObject, nmsStack);
list.add(dataWatcherItem);
ReflectionUtil.setDeclaredField(packet, "b", list);
}
ReflectionUtil.sendPacketSync(player, packet);
}
};
public ArrayImage getContent() {
return content;
}
@Override
public MapController getController() {
return controller;
}
}

View file

@ -1,160 +0,0 @@
/*
* This file is part of MapReflectionAPI.
* Copyright (c) 2022-2023 inventivetalent / SBDevelopment - All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.sbdevelopment.mapreflectionapi.listeners;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import tech.sbdevelopment.mapreflectionapi.MapReflectionAPI;
import tech.sbdevelopment.mapreflectionapi.api.events.CreateInventoryMapUpdateEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapCancelEvent;
import tech.sbdevelopment.mapreflectionapi.api.events.MapInteractEvent;
import tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil;
import java.util.concurrent.TimeUnit;
import static tech.sbdevelopment.mapreflectionapi.utils.ReflectionUtil.*;
public class PacketListener implements Listener {
private static final Class<?> packetPlayOutMapClass = getNMSClass("network.protocol.game", "PacketPlayOutMap");
private static final Class<?> packetPlayInUseEntityClass = getNMSClass("network.protocol.game", "PacketPlayInUseEntity");
private static final Class<?> packetPlayInSetCreativeSlotClass = getNMSClass("network.protocol.game", "PacketPlayInSetCreativeSlot");
private static final Class<?> vec3DClass = getNMSClass("world.phys", "Vec3D");
private static final Class<?> craftStackClass = getCraftClass("inventory.CraftItemStack");
@EventHandler
public void onJoin(PlayerJoinEvent e) {
injectPlayer(e.getPlayer());
}
@EventHandler
public void onQuit(PlayerQuitEvent e) {
removePlayer(e.getPlayer());
}
private void injectPlayer(Player player) {
ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
if (packet.getClass().isAssignableFrom(packetPlayOutMapClass)) {
Object packetPlayOutMap = packetPlayOutMapClass.cast(packet);
int id = (int) getDeclaredField(packetPlayOutMap, "a");
if (id < 0) {
int newId = -id;
setDeclaredField(packetPlayOutMap, "a", newId);
} else {
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
MapCancelEvent event = new MapCancelEvent(player, id, async);
if (MapReflectionAPI.getMapManager().isIdUsedBy(player, id)) event.setCancelled(true);
if (event.getHandlers().getRegisteredListeners().length > 0)
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.write(ctx, packet, promise);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
if (packet.getClass().isAssignableFrom(packetPlayInUseEntityClass)) {
Object packetPlayInEntity = packetPlayInUseEntityClass.cast(packet);
int entityId = (int) getDeclaredField(packetPlayInEntity, "a");
Enum<?> actionEnum;
Enum<?> hand;
Object pos;
if (ReflectionUtil.supports(17)) {
Object action = getDeclaredField(packetPlayInEntity, "b");
actionEnum = (Enum<?>) callDeclaredMethod(action, "a"); //action type
hand = hasField(action, "a") ? (Enum<?>) getDeclaredField(action, "a") : null;
pos = hasField(action, "b") ? getDeclaredField(action, "b") : null;
} else {
actionEnum = (Enum<?>) callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "b" : "a"); //1.13 = b, 1.12 = a
hand = (Enum<?>) callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "c" : "b"); //1.13 = c, 1.12 = b
pos = callDeclaredMethod(packetPlayInEntity, ReflectionUtil.supports(13) ? "d" : "c"); //1.13 = d, 1.12 = c
}
if (Bukkit.getScheduler().callSyncMethod(MapReflectionAPI.getInstance(), () -> {
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
MapInteractEvent event = new MapInteractEvent(player, entityId, actionEnum.ordinal(), pos != null ? vec3DToVector(pos) : null, hand != null ? hand.ordinal() : 0, async);
if (event.getFrame() != null && event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
return false;
}).get(1, TimeUnit.SECONDS)) return;
} else if (packet.getClass().isAssignableFrom(packetPlayInSetCreativeSlotClass)) {
Object packetPlayInSetCreativeSlot = packetPlayInSetCreativeSlotClass.cast(packet);
int slot = (int) ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, ReflectionUtil.supports(19, 3) ? "a" : ReflectionUtil.supports(13) ? "b" : "a"); //1.19.4 = a, 1.19.3 - 1.13 = b, 1.12 = a
Object nmsStack = ReflectionUtil.callDeclaredMethod(packetPlayInSetCreativeSlot, ReflectionUtil.supports(18) ? "c" : "getItemStack"); //1.18 = c, 1.17 = getItemStack
ItemStack craftStack = (ItemStack) ReflectionUtil.callMethod(craftStackClass, "asBukkitCopy", nmsStack);
boolean async = !MapReflectionAPI.getInstance().getServer().isPrimaryThread();
CreateInventoryMapUpdateEvent event = new CreateInventoryMapUpdateEvent(player, slot, craftStack, async);
if (event.getMapWrapper() != null) {
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
}
}
super.channelRead(ctx, packet);
}
};
Channel channel = getChannel(player);
channel.pipeline().addBefore("packet_handler", player.getName(), channelDuplexHandler);
}
private void removePlayer(Player player) {
Channel channel = getChannel(player);
channel.eventLoop().submit(() -> channel.pipeline().remove(player.getName()));
}
private Channel getChannel(Player player) {
Object playerHandle = getHandle(player);
Object playerConnection = getDeclaredField(playerHandle, ReflectionUtil.supports(20) ? "c" : ReflectionUtil.supports(17) ? "b" : "playerConnection"); //1.20 = c, 1.17-1.19 = b, 1.16 = playerConnection
Object networkManager = getDeclaredField(playerConnection, ReflectionUtil.supports(19, 3) ? "h" : ReflectionUtil.supports(19) ? "b" : ReflectionUtil.supports(17) ? "a" : "networkManager"); //1.19.4 = h, >= 1.19.3 = b, 1.18 = a, 1.16 = networkManager
return (Channel) getDeclaredField(networkManager, ReflectionUtil.supports(18) ? "m" : ReflectionUtil.supports(17) ? "k" : "channel"); //1.19 & 1.18 = m, 1.17 = k, 1.16 = channel
}
private Vector vec3DToVector(Object vec3d) {
if (!(vec3d.getClass().isAssignableFrom(vec3DClass))) return new Vector(0, 0, 0);
Object vec3dNMS = vec3DClass.cast(vec3d);
double x = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "c" : ReflectionUtil.supports(17) ? "b" : "x"); //1.19 = c, 1.18 = b, 1.16 = x
double y = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "d" : ReflectionUtil.supports(17) ? "c" : "y"); //1.19 = d, 1.18 = c, 1.16 = y
double z = (double) ReflectionUtil.getDeclaredField(vec3dNMS, ReflectionUtil.supports(19) ? "e" : ReflectionUtil.supports(17) ? "d" : "z"); //1.19 = e, 1.18 = d, 1.16 = z
return new Vector(x, y, z);
}
}