12
0

CoreEntity #220

Zusammengeführt
Lixfel hat 10 Commits von coreEntity nach master 2023-01-15 11:56:30 +01:00 zusammengeführt
16 geänderte Dateien mit 1187 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -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 {
@ -294,4 +295,31 @@ 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 swimming = entityPose.getEnumConstants()[3];
private static final Object sneaking = entityPose.getEnumConstants()[5];
@Override
public Object getPose(FlatteningWrapper.EntityPose pose) {
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

Kannst du neben standing und sneaking bitte noch swimming hinzufügen? Das ist etwas was besonders in Replays und dem FightSystem, eine Sache hinzufügt, womit wir schon 1 oder 2 mal Probleme bekommen haben wegen Reports.

Kannst du neben standing und sneaking bitte noch swimming hinzufügen? Das ist etwas was besonders in Replays und dem FightSystem, eine Sache hinzufügt, womit wir schon 1 oder 2 mal Probleme bekommen haben wegen Reports.
switch (pose) {
case SNEAKING:
return sneaking;
case SWIMMING:
return swimming;
case NORMAL:
default:
return standing;
}
}
@Override
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();
}
}

Datei anzeigen

@ -0,0 +1,72 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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<List> 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);
private static final Reflection.FieldAccessor<Integer> spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
@Override
public void setSpawnPacketType(Object packet, EntityType type) {
Lixfel markierte diese Unterhaltung als gelöst
Review

Könnte ich im gleichen Schritt auch hier Maps bekommen? Damit ich im LobbySystem die Maps endlich anpassen kann.

Könnte ich im gleichen Schritt auch hier Maps bekommen? Damit ich im LobbySystem die Maps endlich anpassen kann.
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;
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;
default:
throw new IllegalArgumentException(type.name() + " is not implemented");
}
}
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);
}
}

Datei anzeigen

@ -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")
}

Datei anzeigen

@ -0,0 +1,71 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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<List> 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;
case ITEM_FRAME:
spawnType.set(packet, EntityTypes.U);
break;
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");
}
}
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);
}
}

Datei anzeigen

@ -19,14 +19,19 @@
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;
public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper {
@Override
@ -38,4 +43,43 @@ 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<Class<?>, 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);
}
@Override
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset8) {
Reflection.FieldAccessor<Integer> posX = Reflection.getField(packetClass, int.class, fieldOffset8);
Reflection.FieldAccessor<Integer> posY = Reflection.getField(packetClass, int.class, fieldOffset8+1);
Reflection.FieldAccessor<Integer> 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 BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
return (packet, uuid) -> {};
}
}

Datei anzeigen

@ -64,4 +64,24 @@ public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper
head.setItemMeta(headmeta);
return head;
}
@Override
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");
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));
}
@Override
public Object formatDisplayName(String displayName) {
return displayName != null ? displayName : "";
}
}

Datei anzeigen

@ -0,0 +1,82 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
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;
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 Reflection.FieldAccessor<Integer> spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
private static final Map<EntityType, Object> types = new HashMap<>();
static {
if(Core.getVersion() < 14) {
spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, Core.getVersion() > 8 ? 6 : 9);
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);
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) {
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);
@Override
public Object playerInfoDataConstructor(Object packet, GameProfile profile, Object mode) {
return playerInfoDataConstructor.invoke(packet, profile, 0, mode, null);
}
}

Datei anzeigen

@ -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,40 @@ 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);
}
@Override
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset8) {
Reflection.FieldAccessor<Double> posX = Reflection.getField(packetClass, double.class, 0);
Reflection.FieldAccessor<Double> posY = Reflection.getField(packetClass, double.class, 1);
Reflection.FieldAccessor<Double> posZ = Reflection.getField(packetClass, double.class, 2);
return (packet, x, y, z) -> {
posX.set(packet, x);
posY.set(packet, y);
posZ.set(packet, z);
};
}
@Override
public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
Reflection.FieldAccessor<UUID> uuidField = Reflection.getField(packetClass, UUID.class, 0);
return uuidField::set;
}
}

Datei anzeigen

@ -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'

Datei anzeigen

@ -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,19 @@ 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);
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);
}
}

Datei anzeigen

@ -38,5 +38,15 @@ public class FlatteningWrapper {
Material getMaterial(String material);
Material getDye(int colorCode);
ItemStack setSkullOwner(String player);
Object getPose(EntityPose pose);
void setNamedSpawnPacketDataWatcher(Object packet);
Object formatDisplayName(String displayName);
}
public enum EntityPose {
NORMAL,
SNEAKING,
SWIMMING;
}
}

Datei anzeigen

@ -0,0 +1,48 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
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<?> 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};
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);
}

Datei anzeigen

@ -0,0 +1,76 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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 RArmorStand 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);
private final Size size;
public RArmorStand(REntityServer server, Location location, Size size) {
super(server, EntityType.ARMOR_STAND, location);
this.size = size;
}
@Override
void spawn(Consumer<Object> packetSink) {
super.spawn(packetSink);
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;
}
}
}

Datei anzeigen

@ -0,0 +1,341 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.entity;
import com.comphenix.tinyprotocol.Reflection;
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.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(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<IChatBaseComponent>
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;
protected final int entityId;
protected final UUID uuid;
protected double x;
protected double y;
protected double z;
private byte yaw;
private byte pitch;
private byte headYaw;
private boolean invisible;
private FlatteningWrapper.EntityPose pose;
private boolean bowDrawn;
private int fireTick;
private String displayName;
protected final Map<Object, ItemStack> itemSlots;
public REntity(REntityServer server, EntityType entityType, Location location) {
this(server, entityType, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L), location);
}
protected REntity(REntityServer server, EntityType entityType, UUID uuid, Location location) {
this.server = server;
this.entityType = entityType;
this.entityId = entityIdCounter--;
this.uuid = uuid;
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);
server.updateEntity(this, getTeleportPacket());
if(this.headYaw != headYaw) {
this.headYaw = headYaw;
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<Integer> animationEntity = Reflection.getField(animationPacket, int.class, Core.getVersion() > 15 ? 6 : 0);
private static final Reflection.FieldAccessor<Integer> 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<Integer> velocityEntity = Reflection.getField(velocityPacket, int.class, 0);
private static final Reflection.FieldAccessor<Integer> velocityX = Reflection.getField(velocityPacket, int.class, 1);
private static final Reflection.FieldAccessor<Integer> velocityY = Reflection.getField(velocityPacket, int.class, 2);
private static final Reflection.FieldAccessor<Integer> 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<Integer> statusEntity = Reflection.getField(statusPacket, int.class, 0);
private static final Reflection.FieldAccessor<Byte> 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 setPose(FlatteningWrapper.EntityPose pose) {
this.pose = pose;
if(Core.getVersion() > 12) {
server.updateEntity(this, getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose)));
} else {
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
}
public void setOnFire(boolean perma) {
fireTick = perma ? -1 : 21;
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
public void setInvisible(boolean invisible) {
this.invisible = invisible;
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, getEntityStatus()));
}
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
server.updateEntity(this, getDataWatcherPacket(
nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName),
nameVisibleWatcher, displayName != null
));
}
public void setItem(Object slot, ItemStack stack) {
itemSlots.put(slot, stack);
server.updateEntity(this, getEquipmentPacket(slot, stack));
}
public void die() {
server.removeEntity(this);
}
private static final Function<REntity, Object> livingSpawnPacketGenerator = entitySpawnPacketGenerator(ProtocolWrapper.spawnLivingPacket, 2);
private static final Function<REntity, Object> spawnPacketGenerator = entitySpawnPacketGenerator(ProtocolWrapper.spawnPacket, 1);
void spawn(Consumer<Object> packetSink) {
if(entityType.isAlive()) {
packetSink.accept(livingSpawnPacketGenerator.apply(this));
} else {
packetSink.accept(spawnPacketGenerator.apply(this));
}
postSpawn(packetSink);
}
protected void postSpawn(Consumer<Object> packetSink) {
if(headYaw != 0) {
packetSink.accept(getHeadRotationPacket());
}
if(Core.getVersion() > 12 && pose != FlatteningWrapper.EntityPose.NORMAL) {
packetSink.accept(getDataWatcherPacket(sneakingDataWatcher, FlatteningWrapper.impl.getPose(pose)));
}
byte status = getEntityStatus();
if(status != 0) {
packetSink.accept(getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
if(displayName != null) {
packetSink.accept(getDataWatcherPacket(nameWatcher, FlatteningWrapper.impl.formatDisplayName(displayName), nameVisibleWatcher, true));
}
}
void tick() {
if(fireTick > 0) {
fireTick--;
if(fireTick == 0) {
server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus()));
}
}
}
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<Object> packetSink){
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 byte getEntityStatus() {
byte status = 0;
if(fireTick != 0)
status |= 1;
if(pose == FlatteningWrapper.EntityPose.SNEAKING)
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<Integer> metadataEntity = Reflection.getField(metadataPacket, int.class, 0);
private static final Reflection.FieldAccessor<List> metadataMetadata = Reflection.getField(metadataPacket, List.class, 0);
protected Object getDataWatcherPacket(Object... dataWatcherKeyValues) {
Object packet = Reflection.newInstance(metadataPacket);
metadataEntity.set(packet, entityId);
ArrayList<Object> 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<Integer> teleportEntity = Reflection.getField(teleportPacket, int.class, 0);
private static final BountifulWrapper.PositionSetter teleportPosition = BountifulWrapper.impl.getPositionSetter(teleportPacket, 1);
private static final Reflection.FieldAccessor<Byte> teleportYaw = Reflection.getField(teleportPacket, byte.class, 0);
private static final Reflection.FieldAccessor<Byte> teleportPitch = Reflection.getField(teleportPacket, byte.class, 1);
private Object getTeleportPacket(){
Object packet = Reflection.newInstance(teleportPacket);
teleportEntity.set(packet, entityId);
teleportPosition.set(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<Integer> headRotationEntity = Reflection.getField(headRotationPacket, int.class, 0);
private static final Reflection.FieldAccessor<Byte> 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<Integer> 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);
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 Function<REntity, Object> entitySpawnPacketGenerator(Class<?> spawnPacket, int posOffset8) {
BountifulWrapper.UUIDSetter uuid = BountifulWrapper.impl.getUUIDSetter(spawnPacket);
Function<REntity, Object> packetGenerator = spawnPacketGenerator(spawnPacket, posOffset8);
return entity -> {
Object packet = packetGenerator.apply(entity);
uuid.set(packet, entity.uuid);
ProtocolWrapper.impl.setSpawnPacketType(packet, entity.entityType);
return packet;
};
}
protected static Function<REntity, Object> spawnPacketGenerator(Class<?> spawnPacket, int posOffset8) {
Reflection.FieldAccessor<Integer> entityId = Reflection.getField(spawnPacket, int.class, 0);
BountifulWrapper.PositionSetter position = BountifulWrapper.impl.getPositionSetter(spawnPacket, posOffset8);
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) {
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);
}
}

Datei anzeigen

@ -0,0 +1,212 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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 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<REntity> emptyEntities = new HashSet<>(0);
private static final HashSet<Player> emptyPlayers = new HashSet<>(0);
private final HashMap<Long, HashSet<REntity>> entities = new HashMap<>();
private final HashMap<Long, HashSet<Player>> players = new HashMap<>();
private final HashMap<Player, Location> lastLocation = new HashMap<>();
public REntityServer() {
Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance());
}
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);
}
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<REntity> 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<Player> of, HashSet<Player> in, Consumer<Consumer<Object>> 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<Integer, Integer> 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));
}
}
public void tick() {
for(HashSet<REntity> 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;
}
}

Datei anzeigen

@ -0,0 +1,126 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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<Object> packetSink) {
packetSink.accept(getPlayerInfoPacket(addPlayer));
packetSink.accept(getNamedSpawnPacket());
packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F));
for (Map.Entry<Object, ItemStack> entry : itemSlots.entrySet()) {
packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue()));
}
postSpawn(packetSink);
}
@Override
void despawn(Consumer<Object> packetSink) {
//team.removeEntry(name);
super.despawn(packetSink);
packetSink.accept(getPlayerInfoPacket(removePlayer));
}
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(ProtocolWrapper.playerInfoPacket, playerInfoActionClass, 0);
private static final Object removePlayer = playerInfoActionClass.getEnumConstants()[4];
private static final Reflection.FieldAccessor<List> 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(ProtocolWrapper.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<REntity, Object> namedSpawnPacketGenerator = spawnPacketGenerator(namedSpawnPacket, 1);
private static final Reflection.FieldAccessor<UUID> 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;
}
}