From d890451602bb923b3d55fe4d319e2f0919b19194 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 29 Nov 2022 16:57:25 +0100 Subject: [PATCH 1/8] WIP Merging of FightSystem and LobbySystem --- .../de/steamwar/core/FlatteningWrapper14.java | 13 + .../de/steamwar/core/ProtocolWrapper18.java | 63 +++ SpigotCore_19/build.gradle | 1 + .../de/steamwar/core/ProtocolWrapper19.java | 63 +++ .../de/steamwar/core/BountifulWrapper8.java | 54 +++ .../de/steamwar/core/FlatteningWrapper8.java | 15 + .../de/steamwar/core/ProtocolWrapper8.java | 87 ++++ .../de/steamwar/core/BountifulWrapper9.java | 48 ++ SpigotCore_Main/build.gradle | 1 + .../de/steamwar/core/BountifulWrapper.java | 8 + .../de/steamwar/core/FlatteningWrapper.java | 3 + .../src/de/steamwar/core/ProtocolWrapper.java | 43 ++ .../src/de/steamwar/entity/REntity.java | 420 ++++++++++++++++++ .../src/de/steamwar/entity/REntityServer.java | 217 +++++++++ 14 files changed, 1036 insertions(+) create mode 100644 SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java create mode 100644 SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java create mode 100644 SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java create mode 100644 SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java create mode 100644 SpigotCore_Main/src/de/steamwar/entity/REntity.java create mode 100644 SpigotCore_Main/src/de/steamwar/entity/REntityServer.java diff --git a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java index 1844eba..daea6e1 100644 --- a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java +++ b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java @@ -294,4 +294,17 @@ public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper head.setItemMeta(headmeta); return head; } + + private static final Class entityPose = Reflection.getClass("{nms.world.entity}.EntityPose"); + private static final Object standing = entityPose.getEnumConstants()[0]; + private static final Object sneaking = entityPose.getEnumConstants()[5]; + @Override + public Object getPose(boolean isSneaking) { + return isSneaking ? sneaking : standing; + } + + @Override + public void setNamedSpawnPacketDataWatcher(Object packet) { + // field not present + } } diff --git a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java new file mode 100644 index 0000000..a09d9e9 --- /dev/null +++ b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java @@ -0,0 +1,63 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.core; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import com.mojang.datafixers.util.Pair; +import net.minecraft.world.entity.EntityTypes; +import org.bukkit.entity.EntityType; + +import java.util.Collections; +import java.util.List; + +public class ProtocolWrapper18 implements ProtocolWrapper { + + private static final Reflection.FieldAccessor equipmentStack = Reflection.getField(equipmentPacket, List.class, 0); + @Override + public void setEquipmentPacketStack(Object packet, Object slot, Object stack) { + equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack))); + } + + private static final Reflection.FieldAccessor spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, EntityTypes.class, 0); + @Override + public void setSpawnPacketType(Object packet, EntityType type) { + switch(type) { + case PRIMED_TNT: + spawnType.set(packet, EntityTypes.as); + break; + case ARROW: + spawnType.set(packet, EntityTypes.d); + break; + case FIREBALL: + spawnType.set(packet, EntityTypes.S); + break; + } + } + + private static final Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); + private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); + private static final Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); + private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, GameProfile.class, int.class, enumGamemode, iChatBaseComponent); + @Override + public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) { + return playerInfoDataConstructor.invoke(profile, 0, mode, null); + } +} diff --git a/SpigotCore_19/build.gradle b/SpigotCore_19/build.gradle index 6f8f9d6..3a3e5d2 100644 --- a/SpigotCore_19/build.gradle +++ b/SpigotCore_19/build.gradle @@ -52,5 +52,6 @@ dependencies { compileOnly 'org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT' compileOnly 'com.mojang:brigadier:1.0.18' compileOnly 'com.mojang:datafixerupper:4.0.26' + compileOnly 'com.mojang:authlib:1.5.25' compileOnly swdep("Spigot-1.19") } diff --git a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java new file mode 100644 index 0000000..d672c97 --- /dev/null +++ b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java @@ -0,0 +1,63 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.core; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import com.mojang.datafixers.util.Pair; +import net.minecraft.world.entity.EntityTypes; +import org.bukkit.entity.EntityType; + +import java.util.Collections; +import java.util.List; + +public class ProtocolWrapper19 implements ProtocolWrapper { + + private static final Reflection.FieldAccessor equipmentStack = Reflection.getField(equipmentPacket, List.class, 0); + @Override + public void setEquipmentPacketStack(Object packet, Object slot, Object stack) { + equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack))); + } + + private static final Reflection.FieldAccessor spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, EntityTypes.class, 0); + @Override + public void setSpawnPacketType(Object packet, EntityType type) { + switch(type) { + case PRIMED_TNT: + spawnType.set(packet, EntityTypes.av); + break; + case ARROW: + spawnType.set(packet, EntityTypes.e); + break; + case FIREBALL: + spawnType.set(packet, EntityTypes.V); + break; + } + } + + private static final Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); + private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); + private static final Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); + private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, GameProfile.class, int.class, enumGamemode, iChatBaseComponent, Reflection.getClass("net.minecraft.world.entity.player.ProfilePublicKey$a")); + @Override + public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) { + return playerInfoDataConstructor.invoke(profile, 0, mode, null, null); + } +} diff --git a/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java b/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java index 8c08082..0659c10 100644 --- a/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java @@ -19,14 +19,20 @@ package de.steamwar.core; +import com.comphenix.tinyprotocol.Reflection; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.BaseComponent; import net.minecraft.server.v1_8_R3.ChatComponentText; +import net.minecraft.server.v1_8_R3.MathHelper; import net.minecraft.server.v1_8_R3.PacketPlayOutChat; import org.bukkit.Sound; import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.Player; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper { @Override @@ -38,4 +44,52 @@ public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper { public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) { ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(BaseComponent.toLegacyText(msg)), (byte)type.ordinal())); } + + @Override + public Object getDataWatcherObject(int index, Class type) { + return index; + } + + private static final Class watchableObject = Reflection.getClass("{nms}.DataWatcher$WatchableObject"); + private static final Reflection.ConstructorInvoker watchableObjectConstructor = Reflection.getConstructor(watchableObject, int.class, int.class, Object.class); + private static final Map, Integer> watchableDatatypes = new HashMap<>(); + static { + watchableDatatypes.put(byte.class, 0); + watchableDatatypes.put(short.class, 1); + watchableDatatypes.put(int.class, 2); + watchableDatatypes.put(float.class, 3); + watchableDatatypes.put(String.class, 4); + } + + @Override + public Object getDataWatcherItem(Object dwo, Object value) { + return watchableObjectConstructor.invoke(watchableDatatypes.get(value.getClass()), dwo, value); + } + + private static final Class teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); + private static final Reflection.FieldAccessor teleportX = Reflection.getField(teleportPacket, int.class, 1); + private static final Reflection.FieldAccessor teleportY = Reflection.getField(teleportPacket, int.class, 2); + private static final Reflection.FieldAccessor teleportZ = Reflection.getField(teleportPacket, int.class, 3); + @Override + public void setTeleportPacketPosition(Object packet, double x, double y, double z) { + teleportX.set(packet, MathHelper.floor(x * 32)); + teleportY.set(packet, MathHelper.floor(y * 32)); + teleportZ.set(packet, MathHelper.floor(z * 32)); + } + + @Override + public void setSpawnPacketUUID(Object packet, UUID uuid) { + // field not present + } + + private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); + private static final Reflection.FieldAccessor namedSpawnX = Reflection.getField(namedSpawnPacket, int.class, 0); + private static final Reflection.FieldAccessor namedSpawnY = Reflection.getField(namedSpawnPacket, int.class, 1); + private static final Reflection.FieldAccessor namedSpawnZ = Reflection.getField(namedSpawnPacket, int.class, 2); + @Override + public void setNamedSpawnPosition(Object packet, double x, double y, double z) { + namedSpawnX.set(packet, MathHelper.floor(x * 32)); + namedSpawnY.set(packet, MathHelper.floor(y * 32)); + namedSpawnZ.set(packet, MathHelper.floor(z * 32)); + } } diff --git a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java index ca0a636..14b605d 100644 --- a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java @@ -64,4 +64,19 @@ public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper head.setItemMeta(headmeta); return head; } + + @Override + public Object getPose(boolean sneaking) { + return Byte.valueOf((byte)(sneaking ? 2 : 0)); + } + + private static final Class dataWatcher = Reflection.getClass("{nms}.DataWatcher"); + private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); + private static final Reflection.FieldAccessor namedSpawnDataWatcher = Reflection.getField(namedSpawnPacket, dataWatcher, 0); + private static final Class entity = Reflection.getClass("{nms}.Entity"); + private static final Reflection.ConstructorInvoker dataWatcherConstructor = Reflection.getConstructor(dataWatcher, entity); + @Override + public void setNamedSpawnPacketDataWatcher(Object packet) { + namedSpawnDataWatcher.set(packet, dataWatcherConstructor.invoke((Object) null)); + } } diff --git a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java new file mode 100644 index 0000000..08123bf --- /dev/null +++ b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java @@ -0,0 +1,87 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.core; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import org.bukkit.entity.EntityType; + +public class ProtocolWrapper8 implements ProtocolWrapper { + + private static final Reflection.FieldAccessor equipmentSlot; + static { + if(Core.getVersion() == 8) { + equipmentSlot = Reflection.getField(equipmentPacket, int.class, 1); + } else { + Class enumItemSlot = Reflection.getClass("{nms.world.entity}.EnumItemSlot"); + equipmentSlot = Reflection.getField(equipmentPacket, enumItemSlot, 0); + } + } + + private static final Reflection.FieldAccessor equipmentStack = Reflection.getField(equipmentPacket, itemStack, 0); + @Override + public void setEquipmentPacketStack(Object packet, Object slot, Object stack) { + equipmentSlot.set(packet, slot); + equipmentStack.set(packet, stack); + } + + private static final Reflection.FieldAccessor spawnType; + private static final Object tnt; + private static final Object arrow; + private static final Object fireball; + static { + if(Core.getVersion() < 14) { + spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, Core.getVersion() > 8 ? 6 : 9); + tnt = 50; + arrow = 60; + fireball = 63; + } else { + Class entityTypes = Reflection.getClass("{nms.world.entity}.EntityTypes"); + spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0); + tnt = Reflection.getField(entityTypes, "TNT", entityTypes).get(null); + arrow = Reflection.getField(entityTypes, "ARROW", entityTypes).get(null); + fireball = Reflection.getField(entityTypes, "FIREBALL", entityTypes).get(null); + } + } + @Override + public void setSpawnPacketType(Object packet, EntityType type) { + switch(type) { + case PRIMED_TNT: + spawnType.set(packet, tnt); + break; + case ARROW: + spawnType.set(packet, arrow); + break; + case FIREBALL: + spawnType.set(packet, fireball); + break; + } + } + + private static final Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); + private static final Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); + private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); + private static final Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); + private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, playerInfoPacket, GameProfile.class, int.class, enumGamemode, iChatBaseComponent); + @Override + public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) { + return playerInfoDataConstructor.invoke(packet, profile, 0, mode, null); + } +} diff --git a/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java b/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java index d12a37d..c16300c 100644 --- a/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java +++ b/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java @@ -19,11 +19,14 @@ package de.steamwar.core; +import com.comphenix.tinyprotocol.Reflection; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Sound; import org.bukkit.entity.Player; +import java.util.UUID; + public class BountifulWrapper9 implements BountifulWrapper.IBountifulWrapper { @Override @@ -35,4 +38,49 @@ public class BountifulWrapper9 implements BountifulWrapper.IBountifulWrapper { public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) { player.spigot().sendMessage(type, msg); } + + private static final Class dataWatcherObject = Reflection.getClass("{nms.network.syncher}.DataWatcherObject"); + private static final Class dataWatcherRegistry = Reflection.getClass("{nms.network.syncher}.DataWatcherRegistry"); + private static final Class dataWatcherSerializer = Reflection.getClass("{nms.network.syncher}.DataWatcherSerializer"); + private static final Reflection.ConstructorInvoker dataWatcherObjectConstructor = Reflection.getConstructor(dataWatcherObject, int.class, dataWatcherSerializer); + @Override + public Object getDataWatcherObject(int index, Class type) { + return dataWatcherObjectConstructor.invoke(index, Reflection.getField(dataWatcherRegistry, dataWatcherSerializer, 0, type).get(null)); + } + + private static final Class item = Reflection.getClass("{nms.network.syncher}.DataWatcher$Item"); + private static final Reflection.ConstructorInvoker itemConstructor = Reflection.getConstructor(item, dataWatcherObject, Object.class); + @Override + public Object getDataWatcherItem(Object dwo, Object value) { + return itemConstructor.invoke(dwo, value); + } + + private static final Class teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); + private static final Reflection.FieldAccessor teleportX = Reflection.getField(teleportPacket, double.class, 0); + private static final Reflection.FieldAccessor teleportY = Reflection.getField(teleportPacket, double.class, 1); + private static final Reflection.FieldAccessor teleportZ = Reflection.getField(teleportPacket, double.class, 2); + @Override + public void setTeleportPacketPosition(Object packet, double x, double y, double z) { + teleportX.set(packet, x); + teleportY.set(packet, y); + teleportZ.set(packet, z); + } + + private static final Class spawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntity"); + private static final Reflection.FieldAccessor spawnUUID = Reflection.getField(spawnPacket, UUID.class, 0); + @Override + public void setSpawnPacketUUID(Object packet, UUID uuid) { + spawnUUID.set(packet, uuid); + } + + private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); + private static final Reflection.FieldAccessor namedSpawnX = Reflection.getField(namedSpawnPacket, double.class, 0); + private static final Reflection.FieldAccessor namedSpawnY = Reflection.getField(namedSpawnPacket, double.class, 1); + private static final Reflection.FieldAccessor namedSpawnZ = Reflection.getField(namedSpawnPacket, double.class, 2); + @Override + public void setNamedSpawnPosition(Object packet, double x, double y, double z) { + namedSpawnX.set(packet, x); + namedSpawnY.set(packet, y); + namedSpawnZ.set(packet, z); + } } diff --git a/SpigotCore_Main/build.gradle b/SpigotCore_Main/build.gradle index a8b5675..c225092 100644 --- a/SpigotCore_Main/build.gradle +++ b/SpigotCore_Main/build.gradle @@ -48,6 +48,7 @@ dependencies { compileOnly 'com.mojang:authlib:1.5.25' compileOnly 'mysql:mysql-connector-java:5.1.49' compileOnly 'com.viaversion:viaversion-api:4.3.1' + compileOnly 'it.unimi.dsi:fastutil:8.5.6' compileOnly swdep("WorldEdit-1.12") implementation 'net.wesjd:anvilgui:1.5.3-SNAPSHOT' diff --git a/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java b/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java index 8b22c31..6d5bc69 100644 --- a/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java @@ -23,6 +23,8 @@ import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.entity.Player; +import java.util.UUID; + public class BountifulWrapper { private BountifulWrapper() {} @@ -32,5 +34,11 @@ public class BountifulWrapper { void playPling(Player player); void sendMessage(Player player, ChatMessageType type, BaseComponent... msg); + + Object getDataWatcherObject(int index, Class type); + Object getDataWatcherItem(Object dataWatcherObject, Object value); + void setTeleportPacketPosition(Object packet, double x, double y, double z); + void setSpawnPacketUUID(Object packet, UUID uuid); + void setNamedSpawnPosition(Object packet, double x, double y, double z); } } diff --git a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java index 9da4b20..0042207 100644 --- a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java @@ -38,5 +38,8 @@ public class FlatteningWrapper { Material getMaterial(String material); Material getDye(int colorCode); ItemStack setSkullOwner(String player); + + Object getPose(boolean sneaking); + void setNamedSpawnPacketDataWatcher(Object packet); } } diff --git a/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java b/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java new file mode 100644 index 0000000..b569428 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java @@ -0,0 +1,43 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.core; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import org.bukkit.entity.EntityType; + +public interface ProtocolWrapper { + + Class itemStack = Reflection.getClass("{nms.world.item}.ItemStack"); + Class spawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntity"); + Class equipmentPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityEquipment"); + + // 0: hand, 1: offhand, 2: feet, 3: legs, 4: chest, 5: head + Object[] itemSlots = Core.getVersion() > 8 ? Reflection.getClass("{nms.world.entity}.EnumItemSlot").getEnumConstants() : new Integer[]{0, 0, 1, 2, 3, 4}; + + ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance()); + + void setEquipmentPacketStack(Object packet, Object slot, Object stack); + + void setSpawnPacketType(Object packet, EntityType type); + + Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode); + +} diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore_Main/src/de/steamwar/entity/REntity.java new file mode 100644 index 0000000..d3c3bb6 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -0,0 +1,420 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.entity; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.Core; +import de.steamwar.core.FlatteningWrapper; +import de.steamwar.core.ProtocolWrapper; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.function.Consumer; + +public class REntity { + + private static final Object entityStatusWatcher = BountifulWrapper.impl.getDataWatcherObject(0, Byte.class); + private static final Object sneakingDataWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 6 : 0, FlatteningWrapper.impl.getPose(true).getClass()); + private static final Object bowDrawnWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 7 : 6, Byte.class); + private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(16, Byte.class); //TODO multiversioning + private static final Object nameWatcher = BountifulWrapper.impl.getDataWatcherObject(2, Optional.class); // Optional, first optional //TODO multiversioning (dataWatcherRegistry.getFields()[fieldIndex].get(null)) + private static final Object nameVisibleWatcher = BountifulWrapper.impl.getDataWatcherObject(3, Boolean.class); //TODO multiversioning + private static final Object sizeWatcher = BountifulWrapper.impl.getDataWatcherObject(14, Byte.class); //TODO multiversioning + + /* + private static final String SCOREBOARD_TEAMNAME = "Replay"; + private static final Team team; + + static { + if(FightScoreboard.getBukkit().getTeam(SCOREBOARD_TEAMNAME) == null) + team = FightScoreboard.getBukkit().registerNewTeam(SCOREBOARD_TEAMNAME); + else + team = FightScoreboard.getBukkit().getTeam(SCOREBOARD_TEAMNAME); + team.setNameTagVisibility(NameTagVisibility.NEVER); + } + */ + + private static int entityIdCounter = -1; + private static final Random random = new Random(); + + private final REntityServer server; + private final EntityType entityType; + private final int entityId; + private final UUID uuid; + private final String name; + + private double x; + private double y; + private double z; + private byte yaw; + private byte pitch; + private byte headYaw; + + private boolean invisible; + private boolean sneaks; + private int fireTick; + private String displayName; + private final Map itemSlots; + + //TODO packet caching + public REntity(REntityServer server, UUID uuid, String name, Location location) { + this(server, EntityType.PLAYER, uuid, name, location); + //team.addEntry(name); + } + + public REntity(REntityServer server, EntityType entityType, Location location) { + this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), null, location); + } + + private REntity(REntityServer server, EntityType entityType, UUID uuid, String name, Location location) { + this.server = server; + this.entityType = entityType; + this.entityId = entityIdCounter--; + this.uuid = uuid; + this.name = name; + + this.x = location.getX(); + this.y = location.getY(); + this.z = location.getZ(); + this.headYaw = this.yaw = rotToByte(location.getYaw()); + this.pitch = rotToByte(location.getPitch()); + + this.itemSlots = entityType == EntityType.PLAYER ? new HashMap<>() : null; + + server.addEntity(this); + } + + public void move(double locX, double locY, double locZ, float pitch, float yaw, byte headYaw) { + server.preEntityMove(this, locX, locZ); + double fromX = this.x; + double fromZ = this.z; + this.x = locX; + this.y = locY; + this.z = locZ; + this.yaw = rotToByte(yaw); + this.pitch = rotToByte(pitch); + this.headYaw = headYaw; + server.updateEntity(this, getTeleportPacket()); + server.updateEntity(this, getHeadRotationPacket()); + server.postEntityMove(this, fromX, fromZ); + } + + private static final Class animationPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutAnimation"); + private static final Reflection.FieldAccessor animationEntity = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? 6 : 0); + private static final Reflection.FieldAccessor animationAnimation = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? 7 : 1); + public void showAnimation(byte animation) { + Object packet = Reflection.newInstance(animationPacket); + animationEntity.set(packet, entityId); + animationAnimation.set(packet, (int) animation); + server.updateEntity(this, packet); + } + + private static final Class velocityPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityVelocity"); + private static final Reflection.FieldAccessor velocityEntity = Reflection.getField(velocityPacket, int.class, 0); + private static final Reflection.FieldAccessor velocityX = Reflection.getField(velocityPacket, int.class, 1); + private static final Reflection.FieldAccessor velocityY = Reflection.getField(velocityPacket, int.class, 2); + private static final Reflection.FieldAccessor velocityZ = Reflection.getField(velocityPacket, int.class, 3); + public void setVelocity(double dX, double dY, double dZ) { + Object packet = Reflection.newInstance(velocityPacket); + velocityEntity.set(packet, entityId); + velocityX.set(packet, calcVelocity(dX)); + velocityY.set(packet, calcVelocity(dY)); + velocityZ.set(packet, calcVelocity(dZ)); + server.updateEntity(this, packet); + } + + private static final Class statusPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityStatus"); + private static final Reflection.FieldAccessor statusEntity = Reflection.getField(statusPacket, int.class, 0); + private static final Reflection.FieldAccessor statusStatus = Reflection.getField(statusPacket, byte.class, 0); + public void showDamage() { + Object packet = Reflection.newInstance(statusPacket); + statusEntity.set(packet, entityId); + statusStatus.set(packet, (byte) 2); + server.updateEntity(this, packet); + } + + public void sneak(boolean sneaking) { + sneaks = sneaking; + server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(sneaking))); //TODO entityStatusData in 1.8! + } + + public void setOnFire(boolean perma) { + fireTick = perma ? -1 : 21; + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte) 1)); + } + + public void setInvisible(boolean invisible) { + this.invisible = invisible; + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte) 0x20)); + } + + public void setBowDrawn(boolean drawn, boolean offHand) { + if(Core.getVersion() > 8){ + server.updateEntity(this, getDataWatcherPacket(bowDrawnWatcher, (byte) ((drawn ? 1 : 0) + (offHand ? 2 : 0)))); + }else{ + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte)0x10)); + } + } + + private static final Class chatComponentText = Reflection.getClass("{nms}.ChatComponentText"); //TODO multiversioning + private static final Reflection.ConstructorInvoker chatComponentTextConstructor = Reflection.getConstructor(chatComponentText, String.class); //TODO multiversioning + public void setDisplayName(String displayName) { + this.displayName = displayName; + server.updateEntity(this, getDataWatcherPacket(nameWatcher, displayName != null ? Optional.of(chatComponentTextConstructor.invoke(displayName)) : Optional.empty())); + server.updateEntity(this, getDataWatcherPacket(nameVisibleWatcher, displayName != null)); + } + + @Deprecated + public void setItem(String item, boolean enchanted, String slot) { + ItemStack stack = new ItemStack(Material.valueOf(item.replace("minecraft:", "").toUpperCase()), 1); + if(enchanted) + stack.addUnsafeEnchantment(Enchantment.DURABILITY, 1); + + switch(slot){ + case "HEAD": + setItem(ProtocolWrapper.itemSlots[5], stack); + break; + case "CHEST": + setItem(ProtocolWrapper.itemSlots[4], stack); + break; + case "LEGS": + setItem(ProtocolWrapper.itemSlots[3], stack); + break; + case "FEET": + setItem(ProtocolWrapper.itemSlots[2], stack); + break; + case "OFFHAND": + setItem(ProtocolWrapper.itemSlots[1], stack); + break; + case "MAINHAND": + default: + setItem(ProtocolWrapper.itemSlots[0], stack); + } + } + + public void setItem(Object slot, ItemStack stack) { + itemSlots.put(slot, stack); + server.updateEntity(this, getEquipmentPacket(slot, stack)); + } + + public void close() { + server.removeEntity(this); + } + + void spawn(Consumer packetSink) { + if(entityType == EntityType.PLAYER){ + packetSink.accept(getPlayerInfoPacket(addPlayer)); + packetSink.accept(getNamedSpawnPacket()); + for (Map.Entry entry : itemSlots.entrySet()) { + packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); + } + packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F)); //TODO multiversioning + } else if(entityType == EntityType.ARMOR_STAND) { + packetSink.accept(getSpawnLivingEntityPacket()); + packetSink.accept(getDataWatcherPacket(sizeWatcher, (byte) 0x10)); // small size //TODO multiversioning + for (Map.Entry entry : itemSlots.entrySet()) { + packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); + } + } else { + packetSink.accept(getSpawnEntityPacket()); + } + packetSink.accept(getTeleportPacket()); //TODO teleport necessary + packetSink.accept(getHeadRotationPacket()); //TODO head rotation for all types? + + //TODO merge MetadataPacket (DataWatcherPackets) + + if(sneaks) { + packetSink.accept(getDataWatcherPacket(entityStatusWatcher, FlatteningWrapper.impl.getPose(true))); + } + + if(fireTick != 0) { + packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, getDataWatcherPacket(entityStatusWatcher, (byte)1))); + } + + if(invisible) { + packetSink.accept(getDataWatcherPacket(entityStatusWatcher, (byte) 0x20)); + } + + if(displayName != null) { + packetSink.accept(getDataWatcherPacket(nameWatcher, Optional.of(chatComponentTextConstructor.invoke(displayName)))); + packetSink.accept(getDataWatcherPacket(nameVisibleWatcher, true)); + } + } + + void tick() { + if(fireTick > 0) { + fireTick--; + if(fireTick == 0) { + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte)0)); + } + } + } + + private static final Class destroyPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityDestroy"); + private static final Reflection.FieldAccessor destroyEntities; + static { + if(Core.getVersion() > 15) + destroyEntities = Reflection.getField(destroyPacket, IntList.class, 0); + else + destroyEntities = Reflection.getField(destroyPacket, int[].class, 0); + } + void despawn(Consumer packetSink){ + if(entityType == EntityType.PLAYER){ + packetSink.accept(getPlayerInfoPacket(removePlayer)); + //team.removeEntry(name); + } + + Object packet = Reflection.newInstance(destroyPacket); + destroyEntities.set(packet, Core.getVersion() > 15 ? new IntArrayList(new int[]{entityId}) : new int[]{entityId}); + packetSink.accept(packet); + } + + double x() { + return x; + } + + double z() { + return z; + } + + private static final Class metadataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityMetadata"); + private static final Reflection.FieldAccessor metadataEntity = Reflection.getField(metadataPacket, int.class, 0); + private static final Reflection.FieldAccessor metadataMetadata = Reflection.getField(metadataPacket, List.class, 0); + + private Object getDataWatcherPacket(Object... dataWatcherKeyValues) { + Object packet = Reflection.newInstance(metadataPacket); + metadataEntity.set(packet, entityId); + + ArrayList nativeWatchers = new ArrayList<>(1); + for(int i = 0; i < dataWatcherKeyValues.length; i+=2) { + nativeWatchers.add(BountifulWrapper.impl.getDataWatcherItem(dataWatcherKeyValues[i], dataWatcherKeyValues[i+1])); + } + metadataMetadata.set(packet, nativeWatchers); + + return packet; + } + + private static final Class teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); + private static final Reflection.FieldAccessor teleportEntity = Reflection.getField(teleportPacket, int.class, 0); + private static final Reflection.FieldAccessor teleportYaw = Reflection.getField(teleportPacket, byte.class, 0); + private static final Reflection.FieldAccessor teleportPitch = Reflection.getField(teleportPacket, byte.class, 1); + private Object getTeleportPacket(){ + Object packet = Reflection.newInstance(teleportPacket); + teleportEntity.set(packet, entityId); + BountifulWrapper.impl.setTeleportPacketPosition(packet, x, y, z); + teleportYaw.set(packet, yaw); + teleportPitch.set(packet, pitch); + return packet; + } + + private static final Class headRotationPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityHeadRotation"); + private static final Reflection.FieldAccessor headRotationEntity = Reflection.getField(headRotationPacket, int.class, 0); + private static final Reflection.FieldAccessor headRotationYaw = Reflection.getField(headRotationPacket, byte.class, 0); + private Object getHeadRotationPacket(){ + Object packet = Reflection.newInstance(headRotationPacket); + headRotationEntity.set(packet, entityId); + headRotationYaw.set(packet, headYaw); + return packet; + } + + private static final Reflection.FieldAccessor equipmentEntity = Reflection.getField(ProtocolWrapper.equipmentPacket, int.class, 0); + + private static final Class craftItemStack = Reflection.getClass("{obc}.inventory.CraftItemStack"); + private static final Reflection.MethodInvoker asNMSCopy = Reflection.getTypedMethod(REntity.craftItemStack, "asNMSCopy", ProtocolWrapper.itemStack, ItemStack.class); + private Object getEquipmentPacket(Object slot, ItemStack stack){ + Object packet = Reflection.newInstance(ProtocolWrapper.equipmentPacket); + equipmentEntity.set(packet, entityId); + ProtocolWrapper.impl.setEquipmentPacketStack(packet, slot, asNMSCopy.invoke(null, stack)); //TODO grouped send (1.8 incompatible) + return packet; + } + + private static final Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); + private static final Class playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); + private static final Object addPlayer = playerInfoActionClass.getEnumConstants()[0]; + private static final Reflection.FieldAccessor playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0); + private static final Object removePlayer = playerInfoActionClass.getEnumConstants()[4]; + private static final Reflection.FieldAccessor playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0); + private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); + private static final Object creative = enumGamemode.getEnumConstants()[Core.getVersion() > 15 ? 1 : 2]; + private Object getPlayerInfoPacket(Object action){ + Object packet = Reflection.newInstance(playerInfoPacket); + playerInfoAction.set(packet, action); + playerInfoData.set(packet, Collections.singletonList(ProtocolWrapper.impl.playerInfoDataConstructor(packet, new GameProfile(uuid, name), creative))); + return packet; + } + + private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); + private static final Reflection.FieldAccessor namedSpawnEntity = Reflection.getField(namedSpawnPacket, int.class, 0); + private static final Reflection.FieldAccessor namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0); + private Object getNamedSpawnPacket() { + Object packet = Reflection.newInstance(namedSpawnPacket); + namedSpawnEntity.set(packet, entityId); + namedSpawnUUID.set(packet, uuid); + BountifulWrapper.impl.setNamedSpawnPosition(packet, x, y, z); + FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet); + return packet; + } + + private static final Class spawnLivingPacket = Reflection.getClass("{nms}.PacketPlayOutSpawnEntityLiving"); //TODO multiversioning + private static final Reflection.ConstructorInvoker spawnLivingPacketConstructor = Reflection.getConstructor(spawnLivingPacket); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingEntityId = Reflection.getField(spawnLivingPacket, int.class, 0); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingUUID = Reflection.getField(spawnLivingPacket, UUID.class, 0); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingEntityType = Reflection.getField(spawnLivingPacket, int.class, 1); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingEntityX = Reflection.getField(spawnLivingPacket, double.class, 0); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingEntityY = Reflection.getField(spawnLivingPacket, double.class, 1); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingEntityZ = Reflection.getField(spawnLivingPacket, double.class, 2); //TODO multiversioning + private Object getSpawnLivingEntityPacket() { + Object packet = spawnLivingPacketConstructor.invoke(); + spawnLivingEntityId.set(packet, entityId); + spawnLivingUUID.set(packet, uuid); + spawnLivingEntityType.set(packet, 1); //TODO set correct type + spawnLivingEntityX.set(packet, x); + spawnLivingEntityY.set(packet, y); + spawnLivingEntityZ.set(packet, z); + return packet; + } + + + private static final Reflection.FieldAccessor spawnEntity = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, 0); + private Object getSpawnEntityPacket() { + Object packet = Reflection.newInstance(ProtocolWrapper.spawnPacket); + spawnEntity.set(packet, entityId); + BountifulWrapper.impl.setSpawnPacketUUID(packet, uuid); + ProtocolWrapper.impl.setSpawnPacketType(packet, entityType); + //TODO set position + return packet; + } + + private byte rotToByte(float rot) { + return (byte)((int)(rot * 256.0F / 360.0F)); + } + + private int calcVelocity(double value) { + return (int)(Math.max(-3.9, Math.min(value, 3.9)) * 8000); + } +} diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java b/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java new file mode 100644 index 0000000..3a63b41 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java @@ -0,0 +1,217 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2022 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.entity; + +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.core.Core; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + + +public class REntityServer implements Listener { + + private static final HashSet emptyEntities = new HashSet<>(0); + private static final HashSet emptyPlayers = new HashSet<>(0); + + private final HashMap> entities = new HashMap<>(); + private final HashMap> players = new HashMap<>(); + private final HashMap lastLocation = new HashMap<>(); + + private final BukkitTask tickTask; + + public REntityServer() { + Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance()); + tickTask = Core.getInstance().getServer().getScheduler().runTaskTimer(Core.getInstance(), this::tick, 1, 1); + } + + public void addPlayer(Player player) { + Location location = player.getLocation(); + lastLocation.put(player, location); + forChunkInView(player, location, (x, z) -> addPlayerToChunk(player, x, z)); + } + + public void removePlayer(Player player) { + Location location = lastLocation.remove(player); + forChunkInView(player, location, (x, z) -> removePlayerFromChunk(player, x, z)); + } + + public void close() { + for(Player player : lastLocation.keySet().toArray(new Player[0])) { + removePlayer(player); + } + tickTask.cancel(); + HandlerList.unregisterAll(this); + } + + void addEntity(REntity entity) { + entities.computeIfAbsent(entityToId(entity), i -> new HashSet<>()).add(entity); + entity.spawn(packet -> updateEntity(entity, packet)); + } + + void preEntityMove(REntity entity, double toX, double toZ) { + long fromId = entityToId(entity); + long toId = posToId(toX, toZ); + if(fromId == toId) + return; + + onMissing(players.get(fromId), players.get(toId), entity::despawn); + } + + void postEntityMove(REntity entity, double fromX, double fromZ) { + long fromId = posToId(fromX, fromZ); + long toId = entityToId(entity); + if(fromId == toId) + return; + + onMissing(players.get(toId), players.get(fromId), entity::spawn); + } + + void updateEntity(REntity entity, Object packet) { + for(Player player : players.getOrDefault(entityToId(entity), emptyPlayers)) { + TinyProtocol.instance.sendPacket(player, packet); + } + } + + void removeEntity(REntity entity) { + entity.despawn(packet -> updateEntity(entity, packet)); + long id = entityToId(entity); + HashSet entitiesInChunk = entities.get(id); + entitiesInChunk.remove(entity); + if(entitiesInChunk.isEmpty()) + entities.remove(id); + } + + //TODO on settings, on respawn? on boatmove? + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onMove(PlayerMoveEvent e) { + Player player = e.getPlayer(); + Location from = lastLocation.get(player); + Location to = e.getTo(); + if(from == null || to == null) + return; + + int fromX = posToChunk(from.getX()); + int fromZ = posToChunk(from.getZ()); + int toX = posToChunk(to.getX()); + int toZ = posToChunk(to.getZ()); + if(fromX == toX && fromZ == toZ) + return; + + int viewDistance = viewRadius(player); + forChunkInView(player, from, (x, z) -> { + if(Math.abs(x - toX) > viewDistance || Math.abs(z - toX) > viewDistance) { + removePlayerFromChunk(player, x, z); + } + }); + forChunkInView(player, to, (x, z) -> { + if(Math.abs(x - fromX) > viewDistance || Math.abs(z - fromZ) > viewDistance) { + addPlayerToChunk(player, x, z); + } + }); + } + + @EventHandler + public void onQuit(PlayerQuitEvent e) { + Player player = e.getPlayer(); + Location location = lastLocation.remove(player); + if(location == null) + return; + + forChunkInView(player, location, (x, z) -> players.get(chunkToId(x, z)).remove(player)); + } + + private void onMissing(HashSet of, HashSet in, Consumer> packetProvider) { + for(Player player : of) { + if(!in.contains(player)) { + packetProvider.accept(packet -> TinyProtocol.instance.sendPacket(player, packet)); //TODO multi packet generation + } + } + } + + private void forChunkInView(Player player, Location location, BiConsumer func) { + int chunkX = posToChunk(location.getX()); + int chunkZ = posToChunk(location.getZ()); + int viewDistance = viewRadius(player); + + for(int x = chunkX - viewDistance; x <= chunkX + viewDistance; x++) { + for(int z = chunkZ - viewDistance; z <= chunkZ + viewDistance; z++) { + func.accept(x, z); + } + } + } + + private void addPlayerToChunk(Player player, int x, int z) { + long id = chunkToId(x, z); + players.computeIfAbsent(id, i -> new HashSet<>()).add(player); + for(REntity entity : entities.getOrDefault(id, emptyEntities)) { + entity.spawn(packet -> TinyProtocol.instance.sendPacket(player, packet)); + } + } + + private void removePlayerFromChunk(Player player, int x, int z) { + long id = chunkToId(x, z); + players.get(id).remove(player); + for(REntity entity : entities.getOrDefault(id, emptyEntities)) { + entity.despawn(packet -> TinyProtocol.instance.sendPacket(player, packet)); + } + } + + private void tick() { + for(HashSet entitiesInChunk : entities.values()) { + for(REntity entity : entitiesInChunk) { + entity.tick(); + } + } + } + + private int posToChunk(double coord) { + return (int)(coord / 16) - (coord < 0 ? 1 : 0); + } + + private int viewRadius(Player player) { + return player.getClientViewDistance() / 2; + } + + private long entityToId(REntity entity) { + return posToId(entity.x(), entity.z()); + } + + private long posToId(double x, double z) { + return chunkToId(posToChunk(x), posToChunk(z)); + } + + private long chunkToId(int x, int z) { + //TODO negative coord clash? + return (long) x << 32 + z; + } +} -- 2.39.2 From 34d841324c529df89aa790ff579eff26377715a6 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Wed, 14 Dec 2022 21:56:02 +0100 Subject: [PATCH 2/8] WIP REntity --- .../src/de/steamwar/entity/REntity.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore_Main/src/de/steamwar/entity/REntity.java index d3c3bb6..98b8fef 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -21,10 +21,7 @@ package de.steamwar.entity; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; -import de.steamwar.core.BountifulWrapper; -import de.steamwar.core.Core; -import de.steamwar.core.FlatteningWrapper; -import de.steamwar.core.ProtocolWrapper; +import de.steamwar.core.*; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.bukkit.Location; @@ -181,11 +178,9 @@ public class REntity { } } - private static final Class chatComponentText = Reflection.getClass("{nms}.ChatComponentText"); //TODO multiversioning - private static final Reflection.ConstructorInvoker chatComponentTextConstructor = Reflection.getConstructor(chatComponentText, String.class); //TODO multiversioning public void setDisplayName(String displayName) { this.displayName = displayName; - server.updateEntity(this, getDataWatcherPacket(nameWatcher, displayName != null ? Optional.of(chatComponentTextConstructor.invoke(displayName)) : Optional.empty())); + server.updateEntity(this, getDataWatcherPacket(nameWatcher, displayName != null ? Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)) : Optional.empty())); server.updateEntity(this, getDataWatcherPacket(nameVisibleWatcher, displayName != null)); } @@ -261,7 +256,7 @@ public class REntity { } if(displayName != null) { - packetSink.accept(getDataWatcherPacket(nameWatcher, Optional.of(chatComponentTextConstructor.invoke(displayName)))); + packetSink.accept(getDataWatcherPacket(nameWatcher, Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)))); packetSink.accept(getDataWatcherPacket(nameVisibleWatcher, true)); } } @@ -349,7 +344,7 @@ public class REntity { private Object getEquipmentPacket(Object slot, ItemStack stack){ Object packet = Reflection.newInstance(ProtocolWrapper.equipmentPacket); equipmentEntity.set(packet, entityId); - ProtocolWrapper.impl.setEquipmentPacketStack(packet, slot, asNMSCopy.invoke(null, stack)); //TODO grouped send (1.8 incompatible) + ProtocolWrapper.impl.setEquipmentPacketStack(packet, slot, asNMSCopy.invoke(null, stack)); return packet; } @@ -380,14 +375,14 @@ public class REntity { return packet; } - private static final Class spawnLivingPacket = Reflection.getClass("{nms}.PacketPlayOutSpawnEntityLiving"); //TODO multiversioning + private static final Class spawnLivingPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntityLiving"); //TODO not existing in 1.19 private static final Reflection.ConstructorInvoker spawnLivingPacketConstructor = Reflection.getConstructor(spawnLivingPacket); //TODO multiversioning private static final Reflection.FieldAccessor spawnLivingEntityId = Reflection.getField(spawnLivingPacket, int.class, 0); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingUUID = Reflection.getField(spawnLivingPacket, UUID.class, 0); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingUUID = Reflection.getField(spawnLivingPacket, UUID.class, 0); //TODO not existing in 1.8 private static final Reflection.FieldAccessor spawnLivingEntityType = Reflection.getField(spawnLivingPacket, int.class, 1); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingEntityX = Reflection.getField(spawnLivingPacket, double.class, 0); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingEntityY = Reflection.getField(spawnLivingPacket, double.class, 1); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingEntityZ = Reflection.getField(spawnLivingPacket, double.class, 2); //TODO multiversioning + private static final Reflection.FieldAccessor spawnLivingEntityX = Reflection.getField(spawnLivingPacket, double.class, 0); //TODO int in 1.8 + private static final Reflection.FieldAccessor spawnLivingEntityY = Reflection.getField(spawnLivingPacket, double.class, 1); //TODO int in 1.8 + private static final Reflection.FieldAccessor spawnLivingEntityZ = Reflection.getField(spawnLivingPacket, double.class, 2); //TODO int in 1.8 private Object getSpawnLivingEntityPacket() { Object packet = spawnLivingPacketConstructor.invoke(); spawnLivingEntityId.set(packet, entityId); -- 2.39.2 From cea2ffdac6298f6c0c3210c0704c6010d1e436bb Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 9 Jan 2023 17:35:00 +0100 Subject: [PATCH 3/8] WIP CoreREntity --- .../de/steamwar/core/ProtocolWrapper18.java | 6 ++ .../de/steamwar/core/ProtocolWrapper19.java | 5 ++ .../de/steamwar/core/BountifulWrapper8.java | 34 +++----- .../de/steamwar/core/ProtocolWrapper8.java | 9 ++ .../de/steamwar/core/BountifulWrapper9.java | 35 +++----- .../de/steamwar/core/BountifulWrapper.java | 14 ++- .../src/de/steamwar/core/ProtocolWrapper.java | 1 + .../src/de/steamwar/entity/REntity.java | 86 +++++++++++-------- 8 files changed, 107 insertions(+), 83 deletions(-) diff --git a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java index a09d9e9..d0ecebc 100644 --- a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java +++ b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java @@ -37,6 +37,7 @@ public class ProtocolWrapper18 implements ProtocolWrapper { } private static final Reflection.FieldAccessor spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, EntityTypes.class, 0); + private static final Reflection.FieldAccessor spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1); @Override public void setSpawnPacketType(Object packet, EntityType type) { switch(type) { @@ -49,6 +50,11 @@ public class ProtocolWrapper18 implements ProtocolWrapper { case FIREBALL: spawnType.set(packet, EntityTypes.S); break; + case ARMOR_STAND: + spawnLivingType.set(packet, 1); + break; + default: + throw new IllegalArgumentException(type.name() + " is not implemented"); } } diff --git a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java index d672c97..c2d25eb 100644 --- a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java +++ b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java @@ -49,6 +49,11 @@ public class ProtocolWrapper19 implements ProtocolWrapper { case FIREBALL: spawnType.set(packet, EntityTypes.V); break; + case ARMOR_STAND: + spawnType.set(packet, EntityTypes.d); + break; + default: + throw new IllegalArgumentException(type.name() + " is not implemented"); } } diff --git a/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java b/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java index 0659c10..3a0ee7a 100644 --- a/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/BountifulWrapper8.java @@ -31,7 +31,6 @@ import org.bukkit.entity.Player; import java.util.HashMap; import java.util.Map; -import java.util.UUID; public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper { @@ -66,30 +65,21 @@ public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper { return watchableObjectConstructor.invoke(watchableDatatypes.get(value.getClass()), dwo, value); } - private static final Class teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); - private static final Reflection.FieldAccessor teleportX = Reflection.getField(teleportPacket, int.class, 1); - private static final Reflection.FieldAccessor teleportY = Reflection.getField(teleportPacket, int.class, 2); - private static final Reflection.FieldAccessor teleportZ = Reflection.getField(teleportPacket, int.class, 3); @Override - public void setTeleportPacketPosition(Object packet, double x, double y, double z) { - teleportX.set(packet, MathHelper.floor(x * 32)); - teleportY.set(packet, MathHelper.floor(y * 32)); - teleportZ.set(packet, MathHelper.floor(z * 32)); + public BountifulWrapper.PositionSetter getPositionSetter(Class packetClass, int fieldOffset8) { + Reflection.FieldAccessor posX = Reflection.getField(packetClass, int.class, fieldOffset8); + Reflection.FieldAccessor posY = Reflection.getField(packetClass, int.class, fieldOffset8+1); + Reflection.FieldAccessor posZ = Reflection.getField(packetClass, int.class, fieldOffset8+2); + + return (packet, x, y, z) -> { + posX.set(packet, MathHelper.floor(x * 32)); + posY.set(packet, MathHelper.floor(y * 32)); + posZ.set(packet, MathHelper.floor(z * 32)); + }; } @Override - public void setSpawnPacketUUID(Object packet, UUID uuid) { - // field not present - } - - private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); - private static final Reflection.FieldAccessor namedSpawnX = Reflection.getField(namedSpawnPacket, int.class, 0); - private static final Reflection.FieldAccessor namedSpawnY = Reflection.getField(namedSpawnPacket, int.class, 1); - private static final Reflection.FieldAccessor namedSpawnZ = Reflection.getField(namedSpawnPacket, int.class, 2); - @Override - public void setNamedSpawnPosition(Object packet, double x, double y, double z) { - namedSpawnX.set(packet, MathHelper.floor(x * 32)); - namedSpawnY.set(packet, MathHelper.floor(y * 32)); - namedSpawnZ.set(packet, MathHelper.floor(z * 32)); + public BountifulWrapper.UUIDSetter getUUIDSetter(Class packetClass) { + return (packet, uuid) -> {}; } } diff --git a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java index 08123bf..a12c6fb 100644 --- a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java @@ -43,21 +43,25 @@ public class ProtocolWrapper8 implements ProtocolWrapper { } private static final Reflection.FieldAccessor spawnType; + private static final Reflection.FieldAccessor spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1); private static final Object tnt; private static final Object arrow; private static final Object fireball; + private static final int armorStand; static { if(Core.getVersion() < 14) { spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, Core.getVersion() > 8 ? 6 : 9); tnt = 50; arrow = 60; fireball = 63; + armorStand = 30; } else { Class entityTypes = Reflection.getClass("{nms.world.entity}.EntityTypes"); spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0); tnt = Reflection.getField(entityTypes, "TNT", entityTypes).get(null); arrow = Reflection.getField(entityTypes, "ARROW", entityTypes).get(null); fireball = Reflection.getField(entityTypes, "FIREBALL", entityTypes).get(null); + armorStand = 1; } } @Override @@ -72,6 +76,11 @@ public class ProtocolWrapper8 implements ProtocolWrapper { case FIREBALL: spawnType.set(packet, fireball); break; + case ARMOR_STAND: + spawnLivingType.set(packet, armorStand); + break; + default: + throw new IllegalArgumentException(type.name() + " is not implemented"); } } diff --git a/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java b/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java index c16300c..dc7c774 100644 --- a/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java +++ b/SpigotCore_9/src/de/steamwar/core/BountifulWrapper9.java @@ -55,32 +55,23 @@ public class BountifulWrapper9 implements BountifulWrapper.IBountifulWrapper { return itemConstructor.invoke(dwo, value); } - private static final Class teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); - private static final Reflection.FieldAccessor teleportX = Reflection.getField(teleportPacket, double.class, 0); - private static final Reflection.FieldAccessor teleportY = Reflection.getField(teleportPacket, double.class, 1); - private static final Reflection.FieldAccessor teleportZ = Reflection.getField(teleportPacket, double.class, 2); @Override - public void setTeleportPacketPosition(Object packet, double x, double y, double z) { - teleportX.set(packet, x); - teleportY.set(packet, y); - teleportZ.set(packet, z); + public BountifulWrapper.PositionSetter getPositionSetter(Class packetClass, int fieldOffset8) { + Reflection.FieldAccessor posX = Reflection.getField(packetClass, double.class, 0); + Reflection.FieldAccessor posY = Reflection.getField(packetClass, double.class, 1); + Reflection.FieldAccessor posZ = Reflection.getField(packetClass, double.class, 2); + + return (packet, x, y, z) -> { + posX.set(packet, x); + posY.set(packet, y); + posZ.set(packet, z); + }; } - private static final Class spawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntity"); - private static final Reflection.FieldAccessor spawnUUID = Reflection.getField(spawnPacket, UUID.class, 0); @Override - public void setSpawnPacketUUID(Object packet, UUID uuid) { - spawnUUID.set(packet, uuid); - } + public BountifulWrapper.UUIDSetter getUUIDSetter(Class packetClass) { + Reflection.FieldAccessor uuidField = Reflection.getField(packetClass, UUID.class, 0); - private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); - private static final Reflection.FieldAccessor namedSpawnX = Reflection.getField(namedSpawnPacket, double.class, 0); - private static final Reflection.FieldAccessor namedSpawnY = Reflection.getField(namedSpawnPacket, double.class, 1); - private static final Reflection.FieldAccessor namedSpawnZ = Reflection.getField(namedSpawnPacket, double.class, 2); - @Override - public void setNamedSpawnPosition(Object packet, double x, double y, double z) { - namedSpawnX.set(packet, x); - namedSpawnY.set(packet, y); - namedSpawnZ.set(packet, z); + return uuidField::set; } } diff --git a/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java b/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java index 6d5bc69..f71d234 100644 --- a/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java @@ -37,8 +37,16 @@ public class BountifulWrapper { Object getDataWatcherObject(int index, Class type); Object getDataWatcherItem(Object dataWatcherObject, Object value); - void setTeleportPacketPosition(Object packet, double x, double y, double z); - void setSpawnPacketUUID(Object packet, UUID uuid); - void setNamedSpawnPosition(Object packet, double x, double y, double z); + + PositionSetter getPositionSetter(Class packetClass, int fieldOffset8); + UUIDSetter getUUIDSetter(Class packetClass); + } + + public interface PositionSetter { + void set(Object packet, double x, double y, double z); + } + + public interface UUIDSetter { + void set(Object packet, UUID uuid); } } diff --git a/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java b/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java index b569428..9e811e5 100644 --- a/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java @@ -27,6 +27,7 @@ public interface ProtocolWrapper { Class itemStack = Reflection.getClass("{nms.world.item}.ItemStack"); Class spawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntity"); + Class spawnLivingPacket = Core.getVersion() > 18 ? ProtocolWrapper.spawnPacket : Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntityLiving"); Class equipmentPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityEquipment"); // 0: hand, 1: offhand, 2: feet, 3: legs, 4: chest, 5: head diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 98b8fef..dac3e7f 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -74,6 +74,7 @@ public class REntity { private boolean invisible; private boolean sneaks; + private boolean bowDrawn; private int fireTick; private String displayName; private final Map itemSlots; @@ -157,24 +158,29 @@ public class REntity { public void sneak(boolean sneaking) { sneaks = sneaking; - server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(sneaking))); //TODO entityStatusData in 1.8! + if(Core.getVersion() > 12) { + server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(sneaking))); + } else { + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); + } } public void setOnFire(boolean perma) { fireTick = perma ? -1 : 21; - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte) 1)); + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } public void setInvisible(boolean invisible) { this.invisible = invisible; - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte) 0x20)); + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } public void setBowDrawn(boolean drawn, boolean offHand) { + bowDrawn = drawn; if(Core.getVersion() > 8){ server.updateEntity(this, getDataWatcherPacket(bowDrawnWatcher, (byte) ((drawn ? 1 : 0) + (offHand ? 2 : 0)))); }else{ - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte)0x10)); + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } } @@ -217,7 +223,7 @@ public class REntity { server.updateEntity(this, getEquipmentPacket(slot, stack)); } - public void close() { + public void die() { server.removeEntity(this); } @@ -229,30 +235,27 @@ public class REntity { packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); } packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F)); //TODO multiversioning - } else if(entityType == EntityType.ARMOR_STAND) { + } else if(entityType.isAlive()) { packetSink.accept(getSpawnLivingEntityPacket()); - packetSink.accept(getDataWatcherPacket(sizeWatcher, (byte) 0x10)); // small size //TODO multiversioning + packetSink.accept(getDataWatcherPacket(sizeWatcher, (byte) 0x10)); // small size //TODO multiversioning TODO only on ArmorStands for (Map.Entry entry : itemSlots.entrySet()) { packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); } } else { packetSink.accept(getSpawnEntityPacket()); } - packetSink.accept(getTeleportPacket()); //TODO teleport necessary + //packetSink.accept(getTeleportPacket()); packetSink.accept(getHeadRotationPacket()); //TODO head rotation for all types? //TODO merge MetadataPacket (DataWatcherPackets) - if(sneaks) { - packetSink.accept(getDataWatcherPacket(entityStatusWatcher, FlatteningWrapper.impl.getPose(true))); + if(Core.getVersion() > 12 && sneaks) { + packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(true))); } - if(fireTick != 0) { - packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, getDataWatcherPacket(entityStatusWatcher, (byte)1))); - } - - if(invisible) { - packetSink.accept(getDataWatcherPacket(entityStatusWatcher, (byte) 0x20)); + byte status = getEntityStatus(); + if(status != 0) { + packetSink.accept(getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } if(displayName != null) { @@ -265,7 +268,7 @@ public class REntity { if(fireTick > 0) { fireTick--; if(fireTick == 0) { - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, (byte)0)); + server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } } } @@ -297,6 +300,21 @@ public class REntity { return z; } + private byte getEntityStatus() { + byte status = 0; + + if(fireTick != 0) + status |= 1; + if(Core.getVersion() <= 12 && sneaks) + status |= 2; + if(Core.getVersion() == 8 && bowDrawn) + status |= 0x10; + if(invisible) + status |= 0x20; + + return status; + } + private static final Class metadataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityMetadata"); private static final Reflection.FieldAccessor metadataEntity = Reflection.getField(metadataPacket, int.class, 0); private static final Reflection.FieldAccessor metadataMetadata = Reflection.getField(metadataPacket, List.class, 0); @@ -316,12 +334,13 @@ public class REntity { private static final Class teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); private static final Reflection.FieldAccessor teleportEntity = Reflection.getField(teleportPacket, int.class, 0); + private static final BountifulWrapper.PositionSetter teleportPosition = BountifulWrapper.impl.getPositionSetter(teleportPacket, 1); private static final Reflection.FieldAccessor teleportYaw = Reflection.getField(teleportPacket, byte.class, 0); private static final Reflection.FieldAccessor teleportPitch = Reflection.getField(teleportPacket, byte.class, 1); private Object getTeleportPacket(){ Object packet = Reflection.newInstance(teleportPacket); teleportEntity.set(packet, entityId); - BountifulWrapper.impl.setTeleportPacketPosition(packet, x, y, z); + teleportPosition.set(packet, x, y, z); teleportYaw.set(packet, yaw); teleportPitch.set(packet, pitch); return packet; @@ -366,42 +385,37 @@ public class REntity { private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); private static final Reflection.FieldAccessor namedSpawnEntity = Reflection.getField(namedSpawnPacket, int.class, 0); private static final Reflection.FieldAccessor namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0); + private static final BountifulWrapper.PositionSetter namedSpawnPosition = BountifulWrapper.impl.getPositionSetter(namedSpawnPacket, 1); private Object getNamedSpawnPacket() { Object packet = Reflection.newInstance(namedSpawnPacket); namedSpawnEntity.set(packet, entityId); namedSpawnUUID.set(packet, uuid); - BountifulWrapper.impl.setNamedSpawnPosition(packet, x, y, z); + namedSpawnPosition.set(packet, x, y, z); FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet); return packet; } - private static final Class spawnLivingPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntityLiving"); //TODO not existing in 1.19 - private static final Reflection.ConstructorInvoker spawnLivingPacketConstructor = Reflection.getConstructor(spawnLivingPacket); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingEntityId = Reflection.getField(spawnLivingPacket, int.class, 0); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingUUID = Reflection.getField(spawnLivingPacket, UUID.class, 0); //TODO not existing in 1.8 - private static final Reflection.FieldAccessor spawnLivingEntityType = Reflection.getField(spawnLivingPacket, int.class, 1); //TODO multiversioning - private static final Reflection.FieldAccessor spawnLivingEntityX = Reflection.getField(spawnLivingPacket, double.class, 0); //TODO int in 1.8 - private static final Reflection.FieldAccessor spawnLivingEntityY = Reflection.getField(spawnLivingPacket, double.class, 1); //TODO int in 1.8 - private static final Reflection.FieldAccessor spawnLivingEntityZ = Reflection.getField(spawnLivingPacket, double.class, 2); //TODO int in 1.8 + private static final Reflection.FieldAccessor spawnLivingEntityId = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 0); + private static final BountifulWrapper.UUIDSetter livingSpawnUUID = BountifulWrapper.impl.getUUIDSetter(ProtocolWrapper.spawnLivingPacket); + private static final BountifulWrapper.PositionSetter livingSpawnPosition = BountifulWrapper.impl.getPositionSetter(ProtocolWrapper.spawnLivingPacket, 2); //TODO only diff to spawnEntity, simplify code private Object getSpawnLivingEntityPacket() { - Object packet = spawnLivingPacketConstructor.invoke(); + Object packet = Reflection.newInstance(ProtocolWrapper.spawnLivingPacket); spawnLivingEntityId.set(packet, entityId); - spawnLivingUUID.set(packet, uuid); - spawnLivingEntityType.set(packet, 1); //TODO set correct type - spawnLivingEntityX.set(packet, x); - spawnLivingEntityY.set(packet, y); - spawnLivingEntityZ.set(packet, z); + livingSpawnUUID.set(packet, uuid); + ProtocolWrapper.impl.setSpawnPacketType(packet, entityType); + livingSpawnPosition.set(packet, x, y, z); return packet; } - private static final Reflection.FieldAccessor spawnEntity = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, 0); + private static final BountifulWrapper.UUIDSetter spawnUUID = BountifulWrapper.impl.getUUIDSetter(ProtocolWrapper.spawnPacket); + private static final BountifulWrapper.PositionSetter spawnPosition = BountifulWrapper.impl.getPositionSetter(ProtocolWrapper.spawnPacket, 1); private Object getSpawnEntityPacket() { Object packet = Reflection.newInstance(ProtocolWrapper.spawnPacket); spawnEntity.set(packet, entityId); - BountifulWrapper.impl.setSpawnPacketUUID(packet, uuid); + spawnUUID.set(packet, uuid); ProtocolWrapper.impl.setSpawnPacketType(packet, entityType); - //TODO set position + spawnPosition.set(packet, x, y, z); return packet; } -- 2.39.2 From fb9c1dc6d07c7df6cc5791f4d690d1ab0ed81919 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 10 Jan 2023 11:24:44 +0100 Subject: [PATCH 4/8] First complete CoreEntity (untested) implementation --- .../de/steamwar/core/FlatteningWrapper14.java | 6 + .../de/steamwar/core/FlatteningWrapper8.java | 5 + .../de/steamwar/core/FlatteningWrapper.java | 1 + .../src/de/steamwar/entity/REntity.java | 196 +++++------------- .../src/de/steamwar/entity/RHologram.java | 62 ++++++ .../src/de/steamwar/entity/RPlayer.java | 128 ++++++++++++ 6 files changed, 256 insertions(+), 142 deletions(-) create mode 100644 SpigotCore_Main/src/de/steamwar/entity/RHologram.java create mode 100644 SpigotCore_Main/src/de/steamwar/entity/RPlayer.java diff --git a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java index daea6e1..9fc2ccd 100644 --- a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java +++ b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java @@ -27,6 +27,7 @@ import org.bukkit.inventory.meta.SkullMeta; import java.util.HashMap; import java.util.Map; +import java.util.Optional; public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper { @@ -307,4 +308,9 @@ public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper public void setNamedSpawnPacketDataWatcher(Object packet) { // field not present } + + @Override + public Object formatDisplayName(String displayName) { + return displayName != null ? Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)) : Optional.empty(); + } } diff --git a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java index 14b605d..0a3caba 100644 --- a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java @@ -79,4 +79,9 @@ public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper public void setNamedSpawnPacketDataWatcher(Object packet) { namedSpawnDataWatcher.set(packet, dataWatcherConstructor.invoke((Object) null)); } + + @Override + public Object formatDisplayName(String displayName) { + return displayName != null ? displayName : ""; + } } diff --git a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java index 0042207..b307106 100644 --- a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java @@ -41,5 +41,6 @@ public class FlatteningWrapper { Object getPose(boolean sneaking); void setNamedSpawnPacketDataWatcher(Object packet); + Object formatDisplayName(String displayName); } } diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore_Main/src/de/steamwar/entity/REntity.java index dac3e7f..2053cb1 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -20,54 +20,36 @@ package de.steamwar.entity; import com.comphenix.tinyprotocol.Reflection; -import com.mojang.authlib.GameProfile; import de.steamwar.core.*; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; public class REntity { private static final Object entityStatusWatcher = BountifulWrapper.impl.getDataWatcherObject(0, Byte.class); private static final Object sneakingDataWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 6 : 0, FlatteningWrapper.impl.getPose(true).getClass()); private static final Object bowDrawnWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 7 : 6, Byte.class); - private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(16, Byte.class); //TODO multiversioning - private static final Object nameWatcher = BountifulWrapper.impl.getDataWatcherObject(2, Optional.class); // Optional, first optional //TODO multiversioning (dataWatcherRegistry.getFields()[fieldIndex].get(null)) - private static final Object nameVisibleWatcher = BountifulWrapper.impl.getDataWatcherObject(3, Boolean.class); //TODO multiversioning - private static final Object sizeWatcher = BountifulWrapper.impl.getDataWatcherObject(14, Byte.class); //TODO multiversioning - - /* - private static final String SCOREBOARD_TEAMNAME = "Replay"; - private static final Team team; - - static { - if(FightScoreboard.getBukkit().getTeam(SCOREBOARD_TEAMNAME) == null) - team = FightScoreboard.getBukkit().registerNewTeam(SCOREBOARD_TEAMNAME); - else - team = FightScoreboard.getBukkit().getTeam(SCOREBOARD_TEAMNAME); - team.setNameTagVisibility(NameTagVisibility.NEVER); - } - */ + private static final Object nameWatcher = BountifulWrapper.impl.getDataWatcherObject(2, Core.getVersion() > 12 ? Optional.class : String.class); // Optional + private static final Object nameVisibleWatcher = BountifulWrapper.impl.getDataWatcherObject(3, Boolean.class); private static int entityIdCounter = -1; private static final Random random = new Random(); private final REntityServer server; private final EntityType entityType; - private final int entityId; - private final UUID uuid; - private final String name; + protected final int entityId; + protected final UUID uuid; - private double x; - private double y; - private double z; + protected double x; + protected double y; + protected double z; private byte yaw; private byte pitch; private byte headYaw; @@ -77,24 +59,17 @@ public class REntity { private boolean bowDrawn; private int fireTick; private String displayName; - private final Map itemSlots; - - //TODO packet caching - public REntity(REntityServer server, UUID uuid, String name, Location location) { - this(server, EntityType.PLAYER, uuid, name, location); - //team.addEntry(name); - } + protected final Map itemSlots; public REntity(REntityServer server, EntityType entityType, Location location) { - this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), null, location); + this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), location); } - private REntity(REntityServer server, EntityType entityType, UUID uuid, String name, Location location) { + protected REntity(REntityServer server, EntityType entityType, UUID uuid, Location location) { this.server = server; this.entityType = entityType; this.entityId = entityIdCounter--; this.uuid = uuid; - this.name = name; this.x = location.getX(); this.y = location.getY(); @@ -116,9 +91,11 @@ public class REntity { this.z = locZ; this.yaw = rotToByte(yaw); this.pitch = rotToByte(pitch); - this.headYaw = headYaw; server.updateEntity(this, getTeleportPacket()); - server.updateEntity(this, getHeadRotationPacket()); + if(this.headYaw != headYaw) { + this.headYaw = headYaw; + server.updateEntity(this, getHeadRotationPacket()); + } server.postEntityMove(this, fromX, fromZ); } @@ -186,36 +163,10 @@ public class REntity { public void setDisplayName(String displayName) { this.displayName = displayName; - server.updateEntity(this, getDataWatcherPacket(nameWatcher, displayName != null ? Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)) : Optional.empty())); - server.updateEntity(this, getDataWatcherPacket(nameVisibleWatcher, displayName != null)); - } - - @Deprecated - public void setItem(String item, boolean enchanted, String slot) { - ItemStack stack = new ItemStack(Material.valueOf(item.replace("minecraft:", "").toUpperCase()), 1); - if(enchanted) - stack.addUnsafeEnchantment(Enchantment.DURABILITY, 1); - - switch(slot){ - case "HEAD": - setItem(ProtocolWrapper.itemSlots[5], stack); - break; - case "CHEST": - setItem(ProtocolWrapper.itemSlots[4], stack); - break; - case "LEGS": - setItem(ProtocolWrapper.itemSlots[3], stack); - break; - case "FEET": - setItem(ProtocolWrapper.itemSlots[2], stack); - break; - case "OFFHAND": - setItem(ProtocolWrapper.itemSlots[1], stack); - break; - case "MAINHAND": - default: - setItem(ProtocolWrapper.itemSlots[0], stack); - } + server.updateEntity(this, getDataWatcherPacket( + nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName), + nameVisibleWatcher, displayName != null + )); } public void setItem(Object slot, ItemStack stack) { @@ -227,27 +178,22 @@ public class REntity { server.removeEntity(this); } + private static final Function livingSpawnPacketGenerator = entitySpawnPacketGenerator(ProtocolWrapper.spawnLivingPacket, 2); + private static final Function spawnPacketGenerator = entitySpawnPacketGenerator(ProtocolWrapper.spawnPacket, 1); void spawn(Consumer packetSink) { - if(entityType == EntityType.PLAYER){ - packetSink.accept(getPlayerInfoPacket(addPlayer)); - packetSink.accept(getNamedSpawnPacket()); - for (Map.Entry entry : itemSlots.entrySet()) { - packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); - } - packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F)); //TODO multiversioning - } else if(entityType.isAlive()) { - packetSink.accept(getSpawnLivingEntityPacket()); - packetSink.accept(getDataWatcherPacket(sizeWatcher, (byte) 0x10)); // small size //TODO multiversioning TODO only on ArmorStands - for (Map.Entry entry : itemSlots.entrySet()) { - packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); - } + if(entityType.isAlive()) { + packetSink.accept(livingSpawnPacketGenerator.apply(this)); } else { - packetSink.accept(getSpawnEntityPacket()); + packetSink.accept(spawnPacketGenerator.apply(this)); } - //packetSink.accept(getTeleportPacket()); - packetSink.accept(getHeadRotationPacket()); //TODO head rotation for all types? - //TODO merge MetadataPacket (DataWatcherPackets) + postSpawn(packetSink); + } + + protected void postSpawn(Consumer packetSink) { + if(headYaw != 0) { + packetSink.accept(getHeadRotationPacket()); + } if(Core.getVersion() > 12 && sneaks) { packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(true))); @@ -259,8 +205,7 @@ public class REntity { } if(displayName != null) { - packetSink.accept(getDataWatcherPacket(nameWatcher, Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)))); - packetSink.accept(getDataWatcherPacket(nameVisibleWatcher, true)); + packetSink.accept(getDataWatcherPacket(nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName), nameVisibleWatcher, true)); } } @@ -282,11 +227,6 @@ public class REntity { destroyEntities = Reflection.getField(destroyPacket, int[].class, 0); } void despawn(Consumer packetSink){ - if(entityType == EntityType.PLAYER){ - packetSink.accept(getPlayerInfoPacket(removePlayer)); - //team.removeEntry(name); - } - Object packet = Reflection.newInstance(destroyPacket); destroyEntities.set(packet, Core.getVersion() > 15 ? new IntArrayList(new int[]{entityId}) : new int[]{entityId}); packetSink.accept(packet); @@ -305,7 +245,7 @@ public class REntity { if(fireTick != 0) status |= 1; - if(Core.getVersion() <= 12 && sneaks) + if(sneaks) status |= 2; if(Core.getVersion() == 8 && bowDrawn) status |= 0x10; @@ -319,7 +259,7 @@ public class REntity { private static final Reflection.FieldAccessor metadataEntity = Reflection.getField(metadataPacket, int.class, 0); private static final Reflection.FieldAccessor metadataMetadata = Reflection.getField(metadataPacket, List.class, 0); - private Object getDataWatcherPacket(Object... dataWatcherKeyValues) { + protected Object getDataWatcherPacket(Object... dataWatcherKeyValues) { Object packet = Reflection.newInstance(metadataPacket); metadataEntity.set(packet, entityId); @@ -360,63 +300,35 @@ public class REntity { private static final Class craftItemStack = Reflection.getClass("{obc}.inventory.CraftItemStack"); private static final Reflection.MethodInvoker asNMSCopy = Reflection.getTypedMethod(REntity.craftItemStack, "asNMSCopy", ProtocolWrapper.itemStack, ItemStack.class); - private Object getEquipmentPacket(Object slot, ItemStack stack){ + protected Object getEquipmentPacket(Object slot, ItemStack stack){ Object packet = Reflection.newInstance(ProtocolWrapper.equipmentPacket); equipmentEntity.set(packet, entityId); ProtocolWrapper.impl.setEquipmentPacketStack(packet, slot, asNMSCopy.invoke(null, stack)); return packet; } - private static final Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); - private static final Class playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); - private static final Object addPlayer = playerInfoActionClass.getEnumConstants()[0]; - private static final Reflection.FieldAccessor playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0); - private static final Object removePlayer = playerInfoActionClass.getEnumConstants()[4]; - private static final Reflection.FieldAccessor playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0); - private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); - private static final Object creative = enumGamemode.getEnumConstants()[Core.getVersion() > 15 ? 1 : 2]; - private Object getPlayerInfoPacket(Object action){ - Object packet = Reflection.newInstance(playerInfoPacket); - playerInfoAction.set(packet, action); - playerInfoData.set(packet, Collections.singletonList(ProtocolWrapper.impl.playerInfoDataConstructor(packet, new GameProfile(uuid, name), creative))); - return packet; + private static Function entitySpawnPacketGenerator(Class spawnPacket, int posOffset8) { + BountifulWrapper.UUIDSetter uuid = BountifulWrapper.impl.getUUIDSetter(spawnPacket); + Function packetGenerator = spawnPacketGenerator(spawnPacket, posOffset8); + + return entity -> { + Object packet = packetGenerator.apply(entity); + uuid.set(packet, entity.uuid); + ProtocolWrapper.impl.setSpawnPacketType(packet, entity.entityType); + return packet; + }; } - private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); - private static final Reflection.FieldAccessor namedSpawnEntity = Reflection.getField(namedSpawnPacket, int.class, 0); - private static final Reflection.FieldAccessor namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0); - private static final BountifulWrapper.PositionSetter namedSpawnPosition = BountifulWrapper.impl.getPositionSetter(namedSpawnPacket, 1); - private Object getNamedSpawnPacket() { - Object packet = Reflection.newInstance(namedSpawnPacket); - namedSpawnEntity.set(packet, entityId); - namedSpawnUUID.set(packet, uuid); - namedSpawnPosition.set(packet, x, y, z); - FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet); - return packet; - } + protected static Function spawnPacketGenerator(Class spawnPacket, int posOffset8) { + Reflection.FieldAccessor entityId = Reflection.getField(spawnPacket, int.class, 0); + BountifulWrapper.PositionSetter position = BountifulWrapper.impl.getPositionSetter(spawnPacket, posOffset8); - private static final Reflection.FieldAccessor spawnLivingEntityId = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 0); - private static final BountifulWrapper.UUIDSetter livingSpawnUUID = BountifulWrapper.impl.getUUIDSetter(ProtocolWrapper.spawnLivingPacket); - private static final BountifulWrapper.PositionSetter livingSpawnPosition = BountifulWrapper.impl.getPositionSetter(ProtocolWrapper.spawnLivingPacket, 2); //TODO only diff to spawnEntity, simplify code - private Object getSpawnLivingEntityPacket() { - Object packet = Reflection.newInstance(ProtocolWrapper.spawnLivingPacket); - spawnLivingEntityId.set(packet, entityId); - livingSpawnUUID.set(packet, uuid); - ProtocolWrapper.impl.setSpawnPacketType(packet, entityType); - livingSpawnPosition.set(packet, x, y, z); - return packet; - } - - private static final Reflection.FieldAccessor spawnEntity = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, 0); - private static final BountifulWrapper.UUIDSetter spawnUUID = BountifulWrapper.impl.getUUIDSetter(ProtocolWrapper.spawnPacket); - private static final BountifulWrapper.PositionSetter spawnPosition = BountifulWrapper.impl.getPositionSetter(ProtocolWrapper.spawnPacket, 1); - private Object getSpawnEntityPacket() { - Object packet = Reflection.newInstance(ProtocolWrapper.spawnPacket); - spawnEntity.set(packet, entityId); - spawnUUID.set(packet, uuid); - ProtocolWrapper.impl.setSpawnPacketType(packet, entityType); - spawnPosition.set(packet, x, y, z); - return packet; + return entity -> { + Object packet = Reflection.newInstance(spawnPacket); + entityId.set(packet, entity.entityId); + position.set(packet, entity.x, entity.y, entity.z); + return packet; + }; } private byte rotToByte(float rot) { diff --git a/SpigotCore_Main/src/de/steamwar/entity/RHologram.java b/SpigotCore_Main/src/de/steamwar/entity/RHologram.java new file mode 100644 index 0000000..de5f3c5 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/entity/RHologram.java @@ -0,0 +1,62 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.entity; + +import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.Core; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; + +import java.util.function.Consumer; + +public class RHologram extends REntity { + + private static int sizeIndex() { + switch(Core.getVersion()) { + case 8: + case 9: + return 10; + case 10: + case 12: + return 11; + case 14: + return 13; + case 15: + return 14; + case 18: + case 19: + default: + return 15; + } + } + + private static final Object sizeWatcher = BountifulWrapper.impl.getDataWatcherObject(sizeIndex(), Byte.class); + + public RHologram(REntityServer server, Location location) { + super(server, EntityType.ARMOR_STAND, location); + setInvisible(true); + } + + @Override + void spawn(Consumer packetSink) { + super.spawn(packetSink); + packetSink.accept(getDataWatcherPacket(sizeWatcher, (byte) 0x10)); + } +} diff --git a/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java new file mode 100644 index 0000000..079250d --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -0,0 +1,128 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.entity; + +import com.comphenix.tinyprotocol.Reflection; +import com.mojang.authlib.GameProfile; +import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.Core; +import de.steamwar.core.FlatteningWrapper; +import de.steamwar.core.ProtocolWrapper; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Function; + +public class RPlayer extends REntity { + + /* + private static final String SCOREBOARD_TEAMNAME = "Replay"; + private static final Team team; + + static { + if(FightScoreboard.getBukkit().getTeam(SCOREBOARD_TEAMNAME) == null) + team = FightScoreboard.getBukkit().registerNewTeam(SCOREBOARD_TEAMNAME); + else + team = FightScoreboard.getBukkit().getTeam(SCOREBOARD_TEAMNAME); + team.setNameTagVisibility(NameTagVisibility.NEVER); + } + */ + + private static int skinPartsIndex() { + switch(Core.getVersion()) { + case 8: + return 10; + case 9: + return 12; + case 10: + case 12: + return 13; + case 14: + return 15; + case 15: + return 16; + case 18: + case 19: + default: + return 17; + } + } + + private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(skinPartsIndex(), Byte.class); + + private final String name; + + public RPlayer(REntityServer server, UUID uuid, String name, Location location) { + super(server, EntityType.PLAYER, uuid, location); + this.name = name; + //team.addEntry(name); + } + + @Override + void spawn(Consumer packetSink) { + packetSink.accept(getPlayerInfoPacket(addPlayer)); + packetSink.accept(getNamedSpawnPacket()); + packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F)); + + for (Map.Entry entry : itemSlots.entrySet()) { + packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); + } + + postSpawn(packetSink); + } + + @Override + void despawn(Consumer packetSink) { + //team.removeEntry(name); + super.despawn(packetSink); + packetSink.accept(getPlayerInfoPacket(removePlayer)); + } + + private static final Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); + private static final Class playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); + private static final Object addPlayer = playerInfoActionClass.getEnumConstants()[0]; + private static final Reflection.FieldAccessor playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0); + private static final Object removePlayer = playerInfoActionClass.getEnumConstants()[4]; + private static final Reflection.FieldAccessor playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0); + private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); + private static final Object creative = enumGamemode.getEnumConstants()[Core.getVersion() > 15 ? 1 : 2]; + private Object getPlayerInfoPacket(Object action){ + Object packet = Reflection.newInstance(playerInfoPacket); + playerInfoAction.set(packet, action); + playerInfoData.set(packet, Collections.singletonList(ProtocolWrapper.impl.playerInfoDataConstructor(packet, new GameProfile(uuid, name), creative))); + return packet; + } + + private static final Class namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn"); + private static final Function namedSpawnPacketGenerator = spawnPacketGenerator(namedSpawnPacket, 1); + private static final Reflection.FieldAccessor namedSpawnUUID = Reflection.getField(namedSpawnPacket, UUID.class, 0); + private Object getNamedSpawnPacket() { + Object packet = namedSpawnPacketGenerator.apply(this); + namedSpawnUUID.set(packet, uuid); + FlatteningWrapper.impl.setNamedSpawnPacketDataWatcher(packet); + return packet; + } +} -- 2.39.2 From 000f2189f84965a34476fedc254554c8fa43e15b Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 10 Jan 2023 11:35:56 +0100 Subject: [PATCH 5/8] Deduplicate Reflections --- .../src/de/steamwar/core/ProtocolWrapper18.java | 3 --- .../src/de/steamwar/core/ProtocolWrapper19.java | 3 --- .../src/de/steamwar/core/ProtocolWrapper8.java | 4 ---- .../src/de/steamwar/core/ProtocolWrapper.java | 4 ++++ SpigotCore_Main/src/de/steamwar/entity/RPlayer.java | 10 ++++------ 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java index d0ecebc..d4ea68a 100644 --- a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java +++ b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java @@ -58,9 +58,6 @@ public class ProtocolWrapper18 implements ProtocolWrapper { } } - private static final Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); - private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); - private static final Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, GameProfile.class, int.class, enumGamemode, iChatBaseComponent); @Override public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) { diff --git a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java index c2d25eb..5fcf5b4 100644 --- a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java +++ b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java @@ -57,9 +57,6 @@ public class ProtocolWrapper19 implements ProtocolWrapper { } } - private static final Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); - private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); - private static final Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, GameProfile.class, int.class, enumGamemode, iChatBaseComponent, Reflection.getClass("net.minecraft.world.entity.player.ProfilePublicKey$a")); @Override public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) { diff --git a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java index a12c6fb..f27fbfa 100644 --- a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java @@ -84,10 +84,6 @@ public class ProtocolWrapper8 implements ProtocolWrapper { } } - private static final Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); - private static final Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); - private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); - private static final Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, playerInfoPacket, GameProfile.class, int.class, enumGamemode, iChatBaseComponent); @Override public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) { diff --git a/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java b/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java index 9e811e5..a1f219b 100644 --- a/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/ProtocolWrapper.java @@ -29,6 +29,10 @@ public interface ProtocolWrapper { Class spawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntity"); Class spawnLivingPacket = Core.getVersion() > 18 ? ProtocolWrapper.spawnPacket : Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutSpawnEntityLiving"); Class equipmentPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityEquipment"); + Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); + Class playerInfoDataClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData"); + Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); + Class iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent"); // 0: hand, 1: offhand, 2: feet, 3: legs, 4: chest, 5: head Object[] itemSlots = Core.getVersion() > 8 ? Reflection.getClass("{nms.world.entity}.EnumItemSlot").getEnumConstants() : new Integer[]{0, 0, 1, 2, 3, 4}; diff --git a/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java index 079250d..5ae5cb5 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java +++ b/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -101,16 +101,14 @@ public class RPlayer extends REntity { packetSink.accept(getPlayerInfoPacket(removePlayer)); } - private static final Class playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo"); private static final Class playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); private static final Object addPlayer = playerInfoActionClass.getEnumConstants()[0]; - private static final Reflection.FieldAccessor playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0); + private static final Reflection.FieldAccessor playerInfoAction = Reflection.getField(ProtocolWrapper.playerInfoPacket, playerInfoActionClass, 0); private static final Object removePlayer = playerInfoActionClass.getEnumConstants()[4]; - private static final Reflection.FieldAccessor playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0); - private static final Class enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "{nms.world.level}.EnumGamemode" : "{nms}.WorldSettings$EnumGamemode"); - private static final Object creative = enumGamemode.getEnumConstants()[Core.getVersion() > 15 ? 1 : 2]; + private static final Reflection.FieldAccessor playerInfoData = Reflection.getField(ProtocolWrapper.playerInfoPacket, List.class, 0); + private static final Object creative = ProtocolWrapper.enumGamemode.getEnumConstants()[Core.getVersion() > 15 ? 1 : 2]; private Object getPlayerInfoPacket(Object action){ - Object packet = Reflection.newInstance(playerInfoPacket); + Object packet = Reflection.newInstance(ProtocolWrapper.playerInfoPacket); playerInfoAction.set(packet, action); playerInfoData.set(packet, Collections.singletonList(ProtocolWrapper.impl.playerInfoDataConstructor(packet, new GameProfile(uuid, name), creative))); return packet; -- 2.39.2 From 8886cc556287ab116963b417e0f6f2f5732392ea Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 10 Jan 2023 18:15:30 +0100 Subject: [PATCH 6/8] Swimming, Small ArmorStand, Initial MapType --- .../de/steamwar/core/FlatteningWrapper14.java | 13 +++++++++-- .../de/steamwar/core/ProtocolWrapper18.java | 3 +++ .../de/steamwar/core/ProtocolWrapper19.java | 3 +++ .../de/steamwar/core/FlatteningWrapper8.java | 4 ++-- .../de/steamwar/core/ProtocolWrapper8.java | 6 +++++ .../de/steamwar/core/FlatteningWrapper.java | 8 ++++++- .../{RHologram.java => RArmorStand.java} | 22 +++++++++++++++---- .../src/de/steamwar/entity/REntity.java | 16 +++++++------- 8 files changed, 58 insertions(+), 17 deletions(-) rename SpigotCore_Main/src/de/steamwar/entity/{RHologram.java => RArmorStand.java} (78%) diff --git a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java index 9fc2ccd..84a4dc6 100644 --- a/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java +++ b/SpigotCore_14/src/de/steamwar/core/FlatteningWrapper14.java @@ -298,10 +298,19 @@ public class FlatteningWrapper14 implements FlatteningWrapper.IFlatteningWrapper private static final Class entityPose = Reflection.getClass("{nms.world.entity}.EntityPose"); private static final Object standing = entityPose.getEnumConstants()[0]; + private static final Object swimming = entityPose.getEnumConstants()[3]; private static final Object sneaking = entityPose.getEnumConstants()[5]; @Override - public Object getPose(boolean isSneaking) { - return isSneaking ? sneaking : standing; + public Object getPose(FlatteningWrapper.EntityPose pose) { + switch (pose) { + case SNEAKING: + return sneaking; + case SWIMMING: + return swimming; + case NORMAL: + default: + return standing; + } } @Override diff --git a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java index d4ea68a..2c11fb5 100644 --- a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java +++ b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java @@ -50,6 +50,9 @@ public class ProtocolWrapper18 implements ProtocolWrapper { case FIREBALL: spawnType.set(packet, EntityTypes.S); break; + case ITEM_FRAME: + spawnType.set(packet, EntityTypes.R); + break; case ARMOR_STAND: spawnLivingType.set(packet, 1); break; diff --git a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java index 5fcf5b4..2c35efd 100644 --- a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java +++ b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java @@ -49,6 +49,9 @@ public class ProtocolWrapper19 implements ProtocolWrapper { case FIREBALL: spawnType.set(packet, EntityTypes.V); break; + case ITEM_FRAME: + spawnType.set(packet, EntityTypes.U); + break; case ARMOR_STAND: spawnType.set(packet, EntityTypes.d); break; diff --git a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java index 0a3caba..44f5c1a 100644 --- a/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/FlatteningWrapper8.java @@ -66,8 +66,8 @@ public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper } @Override - public Object getPose(boolean sneaking) { - return Byte.valueOf((byte)(sneaking ? 2 : 0)); + public Object getPose(FlatteningWrapper.EntityPose pose) { + return Byte.valueOf((byte)(pose == FlatteningWrapper.EntityPose.SNEAKING ? 2 : 0)); } private static final Class dataWatcher = Reflection.getClass("{nms}.DataWatcher"); diff --git a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java index f27fbfa..9540a98 100644 --- a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java @@ -47,6 +47,7 @@ public class ProtocolWrapper8 implements ProtocolWrapper { private static final Object tnt; private static final Object arrow; private static final Object fireball; + private static final Object itemFrame; private static final int armorStand; static { if(Core.getVersion() < 14) { @@ -55,12 +56,14 @@ public class ProtocolWrapper8 implements ProtocolWrapper { arrow = 60; fireball = 63; armorStand = 30; + itemFrame = 18; } else { Class entityTypes = Reflection.getClass("{nms.world.entity}.EntityTypes"); spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0); tnt = Reflection.getField(entityTypes, "TNT", entityTypes).get(null); arrow = Reflection.getField(entityTypes, "ARROW", entityTypes).get(null); fireball = Reflection.getField(entityTypes, "FIREBALL", entityTypes).get(null); + itemFrame = Reflection.getField(entityTypes, "ITEM_FRAME", entityTypes).get(null); armorStand = 1; } } @@ -76,6 +79,9 @@ public class ProtocolWrapper8 implements ProtocolWrapper { case FIREBALL: spawnType.set(packet, fireball); break; + case ITEM_FRAME: + spawnType.set(packet, itemFrame); + break; case ARMOR_STAND: spawnLivingType.set(packet, armorStand); break; diff --git a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java index b307106..3f5f205 100644 --- a/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java @@ -39,8 +39,14 @@ public class FlatteningWrapper { Material getDye(int colorCode); ItemStack setSkullOwner(String player); - Object getPose(boolean sneaking); + Object getPose(EntityPose pose); void setNamedSpawnPacketDataWatcher(Object packet); Object formatDisplayName(String displayName); } + + public enum EntityPose { + NORMAL, + SNEAKING, + SWIMMING; + } } diff --git a/SpigotCore_Main/src/de/steamwar/entity/RHologram.java b/SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java similarity index 78% rename from SpigotCore_Main/src/de/steamwar/entity/RHologram.java rename to SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java index de5f3c5..7e0e7a7 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/RHologram.java +++ b/SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java @@ -26,7 +26,7 @@ import org.bukkit.entity.EntityType; import java.util.function.Consumer; -public class RHologram extends REntity { +public class RArmorStand extends REntity { private static int sizeIndex() { switch(Core.getVersion()) { @@ -49,14 +49,28 @@ public class RHologram extends REntity { private static final Object sizeWatcher = BountifulWrapper.impl.getDataWatcherObject(sizeIndex(), Byte.class); - public RHologram(REntityServer server, Location location) { + private final Size size; + + public RArmorStand(REntityServer server, Location location, Size size) { super(server, EntityType.ARMOR_STAND, location); - setInvisible(true); + this.size = size; } @Override void spawn(Consumer packetSink) { super.spawn(packetSink); - packetSink.accept(getDataWatcherPacket(sizeWatcher, (byte) 0x10)); + packetSink.accept(getDataWatcherPacket(sizeWatcher, size.value)); + } + + public enum Size { + NORMAL((byte) 0x00), + SMALL((byte) 0x01), + MARKER((byte) 0x10); + + private final byte value; + + Size(byte value) { + this.value = value; + } } } diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 2053cb1..77a5512 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -34,7 +34,7 @@ import java.util.function.Function; public class REntity { private static final Object entityStatusWatcher = BountifulWrapper.impl.getDataWatcherObject(0, Byte.class); - private static final Object sneakingDataWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 6 : 0, FlatteningWrapper.impl.getPose(true).getClass()); + private static final Object sneakingDataWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 6 : 0, FlatteningWrapper.impl.getPose(FlatteningWrapper.EntityPose.NORMAL).getClass()); private static final Object bowDrawnWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() > 12 ? 7 : 6, Byte.class); private static final Object nameWatcher = BountifulWrapper.impl.getDataWatcherObject(2, Core.getVersion() > 12 ? Optional.class : String.class); // Optional private static final Object nameVisibleWatcher = BountifulWrapper.impl.getDataWatcherObject(3, Boolean.class); @@ -55,7 +55,7 @@ public class REntity { private byte headYaw; private boolean invisible; - private boolean sneaks; + private FlatteningWrapper.EntityPose pose; private boolean bowDrawn; private int fireTick; private String displayName; @@ -133,10 +133,10 @@ public class REntity { server.updateEntity(this, packet); } - public void sneak(boolean sneaking) { - sneaks = sneaking; + public void setPose(FlatteningWrapper.EntityPose pose) { + this.pose = pose; if(Core.getVersion() > 12) { - server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(sneaking))); + server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose))); } else { server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } @@ -195,8 +195,8 @@ public class REntity { packetSink.accept(getHeadRotationPacket()); } - if(Core.getVersion() > 12 && sneaks) { - packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(true))); + if(Core.getVersion() > 12 && pose != FlatteningWrapper.EntityPose.NORMAL) { + packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose))); } byte status = getEntityStatus(); @@ -245,7 +245,7 @@ public class REntity { if(fireTick != 0) status |= 1; - if(sneaks) + if(pose == FlatteningWrapper.EntityPose.SNEAKING) status |= 2; if(Core.getVersion() == 8 && bowDrawn) status |= 0x10; -- 2.39.2 From 81a77fd1defcaea810b144db737bdd79bb3be06e Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 10 Jan 2023 19:08:37 +0100 Subject: [PATCH 7/8] Add FallingBlock --- .../de/steamwar/core/ProtocolWrapper18.java | 3 ++ .../de/steamwar/core/ProtocolWrapper19.java | 3 ++ .../de/steamwar/core/ProtocolWrapper8.java | 52 +++++++------------ 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java index 2c11fb5..2a88cb4 100644 --- a/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java +++ b/SpigotCore_18/src/de/steamwar/core/ProtocolWrapper18.java @@ -53,6 +53,9 @@ public class ProtocolWrapper18 implements ProtocolWrapper { case ITEM_FRAME: spawnType.set(packet, EntityTypes.R); break; + case FALLING_BLOCK: + spawnType.set(packet, EntityTypes.C); + break; case ARMOR_STAND: spawnLivingType.set(packet, 1); break; diff --git a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java index 2c35efd..66cc47c 100644 --- a/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java +++ b/SpigotCore_19/src/de/steamwar/core/ProtocolWrapper19.java @@ -55,6 +55,9 @@ public class ProtocolWrapper19 implements ProtocolWrapper { case ARMOR_STAND: spawnType.set(packet, EntityTypes.d); break; + case FALLING_BLOCK: + spawnType.set(packet, EntityTypes.E); + break; default: throw new IllegalArgumentException(type.name() + " is not implemented"); } diff --git a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java index 9540a98..d0e0e03 100644 --- a/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/ProtocolWrapper8.java @@ -23,6 +23,9 @@ import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; import org.bukkit.entity.EntityType; +import java.util.HashMap; +import java.util.Map; + public class ProtocolWrapper8 implements ProtocolWrapper { private static final Reflection.FieldAccessor equipmentSlot; @@ -44,50 +47,31 @@ public class ProtocolWrapper8 implements ProtocolWrapper { private static final Reflection.FieldAccessor spawnType; private static final Reflection.FieldAccessor spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1); - private static final Object tnt; - private static final Object arrow; - private static final Object fireball; - private static final Object itemFrame; - private static final int armorStand; + private static final Map types = new HashMap<>(); + static { if(Core.getVersion() < 14) { spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, Core.getVersion() > 8 ? 6 : 9); - tnt = 50; - arrow = 60; - fireball = 63; - armorStand = 30; - itemFrame = 18; + types.put(EntityType.PRIMED_TNT, 50); + types.put(EntityType.ARMOR_STAND, 30); + types.put(EntityType.ARROW, 60); + types.put(EntityType.FIREBALL, 63); + types.put(EntityType.ITEM_FRAME, 18); + types.put(EntityType.FALLING_BLOCK, 21); } else { Class entityTypes = Reflection.getClass("{nms.world.entity}.EntityTypes"); spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0); - tnt = Reflection.getField(entityTypes, "TNT", entityTypes).get(null); - arrow = Reflection.getField(entityTypes, "ARROW", entityTypes).get(null); - fireball = Reflection.getField(entityTypes, "FIREBALL", entityTypes).get(null); - itemFrame = Reflection.getField(entityTypes, "ITEM_FRAME", entityTypes).get(null); - armorStand = 1; + types.put(EntityType.ARMOR_STAND, 1); + for(EntityType type : new EntityType[]{EntityType.PRIMED_TNT, EntityType.ARROW, EntityType.FIREBALL, EntityType.ITEM_FRAME, EntityType.FALLING_BLOCK}) + types.put(type, Reflection.getField(entityTypes, type.name(), entityTypes).get(null)); } } @Override public void setSpawnPacketType(Object packet, EntityType type) { - switch(type) { - case PRIMED_TNT: - spawnType.set(packet, tnt); - break; - case ARROW: - spawnType.set(packet, arrow); - break; - case FIREBALL: - spawnType.set(packet, fireball); - break; - case ITEM_FRAME: - spawnType.set(packet, itemFrame); - break; - case ARMOR_STAND: - spawnLivingType.set(packet, armorStand); - break; - default: - throw new IllegalArgumentException(type.name() + " is not implemented"); - } + if(type.isAlive()) + spawnLivingType.set(packet, types.get(type)); + else + spawnType.set(packet, types.get(type)); } private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClass, playerInfoPacket, GameProfile.class, int.class, enumGamemode, iChatBaseComponent); -- 2.39.2 From c340f11666bb5b85b43196e5c35c164913bb95d7 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sun, 15 Jan 2023 11:07:21 +0100 Subject: [PATCH 8/8] Move tick call responsibility to EntityServer owner --- SpigotCore_Main/src/de/steamwar/entity/REntityServer.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java b/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java index 3a63b41..2ffdcab 100644 --- a/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java +++ b/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java @@ -29,7 +29,6 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scheduler.BukkitTask; import java.util.HashMap; import java.util.HashSet; @@ -46,11 +45,8 @@ public class REntityServer implements Listener { private final HashMap> players = new HashMap<>(); private final HashMap lastLocation = new HashMap<>(); - private final BukkitTask tickTask; - public REntityServer() { Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance()); - tickTask = Core.getInstance().getServer().getScheduler().runTaskTimer(Core.getInstance(), this::tick, 1, 1); } public void addPlayer(Player player) { @@ -68,7 +64,6 @@ public class REntityServer implements Listener { for(Player player : lastLocation.keySet().toArray(new Player[0])) { removePlayer(player); } - tickTask.cancel(); HandlerList.unregisterAll(this); } @@ -186,7 +181,7 @@ public class REntityServer implements Listener { } } - private void tick() { + public void tick() { for(HashSet entitiesInChunk : entities.values()) { for(REntity entity : entitiesInChunk) { entity.tick(); -- 2.39.2