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

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
}
}