SteamWar/SpigotCore
Archiviert
13
0

CoreEntity #220

Zusammengeführt
Lixfel hat 10 Commits von coreEntity nach master 2023-01-15 11:56:30 +01:00 zusammengeführt
14 geänderte Dateien mit 1036 neuen und 0 gelöschten Zeilen
Nur Änderungen aus Commit d890451602 werden angezeigt - Alle Commits anzeigen

Datei anzeigen

@ -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;
}
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.
@Override
public void setNamedSpawnPacketDataWatcher(Object packet) {
// field not present
}
}

Datei anzeigen

@ -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 <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);
@Override
public void setSpawnPacketType(Object packet, EntityType type) {
switch(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.
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);
}
}

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

Datei anzeigen

@ -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<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);
}
private static final Class<?> teleportPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport");
private static final Reflection.FieldAccessor<Integer> teleportX = Reflection.getField(teleportPacket, int.class, 1);
private static final Reflection.FieldAccessor<Integer> teleportY = Reflection.getField(teleportPacket, int.class, 2);
private static final Reflection.FieldAccessor<Integer> 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<Integer> namedSpawnX = Reflection.getField(namedSpawnPacket, int.class, 0);
private static final Reflection.FieldAccessor<Integer> namedSpawnY = Reflection.getField(namedSpawnPacket, int.class, 1);
private static final Reflection.FieldAccessor<Integer> 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));
}
}

Datei anzeigen

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

Datei anzeigen

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

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,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<Double> teleportX = Reflection.getField(teleportPacket, double.class, 0);
private static final Reflection.FieldAccessor<Double> teleportY = Reflection.getField(teleportPacket, double.class, 1);
private static final Reflection.FieldAccessor<Double> 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<UUID> 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<Double> namedSpawnX = Reflection.getField(namedSpawnPacket, double.class, 0);
private static final Reflection.FieldAccessor<Double> namedSpawnY = Reflection.getField(namedSpawnPacket, double.class, 1);
private static final Reflection.FieldAccessor<Double> 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);
}
}

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -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 <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 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<IChatBaseComponent>, 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<Object, ItemStack> 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<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 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<Object> packetSink) {
if(entityType == EntityType.PLAYER){
packetSink.accept(getPlayerInfoPacket(addPlayer));
packetSink.accept(getNamedSpawnPacket());
for (Map.Entry<Object, ItemStack> 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<Object, ItemStack> 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<Object> 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<Integer> metadataEntity = Reflection.getField(metadataPacket, int.class, 0);
private static final Reflection.FieldAccessor<List> metadataMetadata = Reflection.getField(metadataPacket, List.class, 0);
private 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 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);
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<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);
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<List> 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<Integer> namedSpawnEntity = Reflection.getField(namedSpawnPacket, int.class, 0);
private static final Reflection.FieldAccessor<UUID> 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<Integer> spawnLivingEntityId = Reflection.getField(spawnLivingPacket, int.class, 0); //TODO multiversioning
private static final Reflection.FieldAccessor<UUID> spawnLivingUUID = Reflection.getField(spawnLivingPacket, UUID.class, 0); //TODO multiversioning
private static final Reflection.FieldAccessor<Integer> spawnLivingEntityType = Reflection.getField(spawnLivingPacket, int.class, 1); //TODO multiversioning
private static final Reflection.FieldAccessor<Double> spawnLivingEntityX = Reflection.getField(spawnLivingPacket, double.class, 0); //TODO multiversioning
private static final Reflection.FieldAccessor<Double> spawnLivingEntityY = Reflection.getField(spawnLivingPacket, double.class, 1); //TODO multiversioning
private static final Reflection.FieldAccessor<Double> 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<Integer> 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);
}
}

Datei anzeigen

@ -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 <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 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<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<>();
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<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));
}
}
private 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;
}
}