From e9341f373e9c72c2bc75a0ae0d65482cab7f0077 Mon Sep 17 00:00:00 2001 From: OmerBenGera Date: Fri, 11 Jun 2021 22:15:19 +0300 Subject: [PATCH] Add 1.17 nms support --- .../nms/ChunkLoaderNPC_v1_17_R1.java | 133 +++++++ .../nms/EntityHolograms_v1_17_R1.java | 169 +++++++++ .../wildloaders/nms/NMSAdapter_v1_17_R1.java | 343 ++++++++++++++++++ 3 files changed, 645 insertions(+) create mode 100644 v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_17_R1.java create mode 100644 v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_17_R1.java create mode 100644 v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_17_R1.java diff --git a/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_17_R1.java b/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_17_R1.java new file mode 100644 index 0000000..29591c3 --- /dev/null +++ b/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_17_R1.java @@ -0,0 +1,133 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.protocol.EnumProtocolDirection; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayInBlockDig; +import net.minecraft.network.protocol.game.PacketPlayInBlockPlace; +import net.minecraft.network.protocol.game.PacketPlayInChat; +import net.minecraft.network.protocol.game.PacketPlayInFlying; +import net.minecraft.network.protocol.game.PacketPlayInHeldItemSlot; +import net.minecraft.network.protocol.game.PacketPlayInUpdateSign; +import net.minecraft.network.protocol.game.PacketPlayInWindowClick; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.EntityPlayer; +import net.minecraft.server.level.WorldServer; +import net.minecraft.server.network.PlayerConnection; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.EnumGamemode; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; + +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_17_R1 extends EntityPlayer implements ChunkLoaderNPC { + + private boolean dieCall = false; + + public ChunkLoaderNPC_v1_17_R1(MinecraftServer minecraftServer, Location location, UUID uuid){ + super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName())); + + this.b = new DummyPlayerConnection(minecraftServer, this); + + this.d.setGameMode(EnumGamemode.b); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(getWorld()); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + ((WorldServer) getWorld()).addPlayerJoin(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void a(Entity.RemovalReason removalReason) { + if(!dieCall) { + dieCall = true; + removePlayer(getWorldServer(), this); + dieCall = false; + } + else { + super.a(removalReason); + } + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + private static void removePlayer(WorldServer worldServer, EntityPlayer entityPlayer){ +// Chunk currentChunk = entityPlayer.getCurrentChunk(); +// if (currentChunk != null) +// currentChunk.b(entityPlayer); +// TODO: Paper + + worldServer.a(entityPlayer, RemovalReason.d); + } + + private static class DummyNetworkManager extends NetworkManager { + + DummyNetworkManager(){ + super(EnumProtocolDirection.a); + this.k = new DummyChannel(); + this.l = null; + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_17_R1.java b/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_17_R1.java new file mode 100644 index 0000000..eaa2345 --- /dev/null +++ b/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_17_R1.java @@ -0,0 +1,169 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.holograms.Hologram; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.sounds.SoundEffect; +import net.minecraft.world.EnumHand; +import net.minecraft.world.EnumInteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EnumItemSlot; +import net.minecraft.world.entity.decoration.EntityArmorStand; +import net.minecraft.world.entity.player.EntityHuman; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.World; +import net.minecraft.world.phys.AxisAlignedBB; +import net.minecraft.world.phys.Vec3D; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftArmorStand; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_17_R1.util.CraftChatMessage; + +import javax.annotation.Nullable; + +@SuppressWarnings("unused") +public final class EntityHolograms_v1_17_R1 extends EntityArmorStand implements Hologram { + + private static final AxisAlignedBB EMPTY_BOUND = new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D); + + private CraftEntity bukkitEntity; + + public EntityHolograms_v1_17_R1(World world, double x, double y, double z){ + super(world, x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + super.collides = false; + super.setCustomNameVisible(true); + super.a(EMPTY_BOUND); + } + + @Override + public void setHologramName(String name) { + super.setCustomName(CraftChatMessage.fromString(name)[0]); + } + + @Override + public void removeHologram() { + super.a(Entity.RemovalReason.b); + } + + @Override + public org.bukkit.entity.Entity getEntity() { + return getBukkitEntity(); + } + + @Override + public void tick() { + // Disable normal ticking for this entity. + + // Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity. + if (this.z) { + this.z = false; + } + } + + @Override + public void inactiveTick() { + // Disable normal ticking for this entity. + + // Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity. + if (this.z) { + this.z = false; + } + } + + @Override + public void saveData(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return nbttagcompound; + } + + @Override + public void load(NBTTagCompound nbttagcompound) { + // Do not load NBT. + } + + @Override + public void loadData(NBTTagCompound nbttagcompound) { + // Do not load NBT. + } + + @Override + public boolean isInvulnerable(DamageSource source) { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public boolean isCollidable() { + return false; + } + + @Override + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean flag) { + // Locks the custom name. + } + + @Override + public EnumInteractionResult a(EntityHuman human, Vec3D vec3d, EnumHand enumhand) { + // Prevent stand being equipped + return EnumInteractionResult.d; + } + + @Override + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + // Prevent stand being equipped + } + + @Override + public AxisAlignedBB cs() { + return EMPTY_BOUND; + } + + public void forceSetBoundingBox(AxisAlignedBB boundingBox) { + super.a(boundingBox); + } + + @Override + public void playSound(SoundEffect soundeffect, float f, float f1) { + // Remove sounds. + } + + @Override + public void a(Entity.RemovalReason entity_removalreason) { + // Prevent being killed. + } + + @Override + public CraftEntity getBukkitEntity() { + if (bukkitEntity == null) { + bukkitEntity = new CraftArmorStand(super.getWorld().getServer(), this); + } + return bukkitEntity; + } + +} diff --git a/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_17_R1.java b/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_17_R1.java new file mode 100644 index 0000000..ed102fe --- /dev/null +++ b/v1_17_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_17_R1.java @@ -0,0 +1,343 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.common.reflection.ReflectMethod; +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.holograms.Hologram; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.core.BlockPosition; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagLong; +import net.minecraft.nbt.NBTTagString; +import net.minecraft.server.level.WorldServer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ChunkCoordIntPair; +import net.minecraft.world.level.World; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import net.minecraft.world.level.block.entity.TileEntity; +import net.minecraft.world.level.block.entity.TileEntityMobSpawner; +import net.minecraft.world.level.block.entity.TileEntityTypes; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.chunk.Chunk; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_17_R1.CraftServer; +import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_17_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + private static final ReflectMethod CREATE_TICKING_BLOCK = new ReflectMethod<>( + Chunk.class, "a", TileEntity.class, BlockEntityTicker.class); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagString.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagLong.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_17_R1(((CraftServer) Bukkit.getServer()).getServer(), location, uuid); + } + + @Override + public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.a(tileEntityChunkLoader.ticker); + + for(org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) { + Chunk chunk = ((CraftChunk) bukkitChunk).getHandle(); + chunk.l.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().n = -1); + + world.setForceLoaded(chunk.getPos().b, chunk.getPos().c, true); + } + + return tileEntityChunkLoader; + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(EntityHolograms_v1_17_R1::removeHologram); + tileEntityChunkLoader.removed = true; + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + for(org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) { + Chunk chunk = ((CraftChunk) bukkitChunk).getHandle(); + chunk.l.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().n = 16); + + world.setForceLoaded(chunk.getPos().b, chunk.getPos().c, false); + } + } + + @Override + public void updateSpawner(Location location, boolean reset) { + assert location.getWorld() != null; + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + IBlockData blockData = world.getType(blockPosition); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().n = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITileEntityChunkLoader { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final WChunkLoader chunkLoader; + private final Block loaderBlock; + private final TileEntityChunkLoaderTicker ticker; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.v, blockPosition, world.getType(blockPosition)); + + this.chunkLoader = (WChunkLoader) chunkLoader; + this.ticker = new TileEntityChunkLoaderTicker(this); + + setWorld(world); + + loaderBlock = world.getType(blockPosition).getBlock(); + +// try { +// // Not a method of Spigot - fixes https://github.com/OmerBenGera/WildLoaders/issues/2 +// setCurrentChunk(world.getChunkAtWorldCoords(blockPosition)); +// }catch (Throwable ignored){} +// TODO: Paper + + if(!this.chunkLoader.isInfinite()) { + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + } + + tileEntityChunkLoaderMap.put(ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + List hologramLines = this.chunkLoader.getHologramLines(); + + double currentY = getPosition().getY() + 1; + for(int i = hologramLines.size(); i > 0; i--){ + EntityHolograms_v1_17_R1 hologram = new EntityHolograms_v1_17_R1(world, + getPosition().getX() + 0.5, currentY, getPosition().getZ() + 0.5); + updateName(hologram, hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + assert this.n != null; + if(chunkLoader.isNotActive() || this.n.getType(getPosition()).getBlock() != loaderBlock){ + chunkLoader.remove(); + return; + } + + if(chunkLoader.isInfinite()) + return; + + List hologramLines = chunkLoader.getHologramLines(); + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHolograms_v1_17_R1 hologram = holograms.get(hologramsAmount - i); + updateName(hologram, hologramLines.get(i - 1)); + } + + chunkLoader.tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + @Override + public Collection getHolograms() { + return Collections.unmodifiableList(holograms); + } + + @Override + public boolean isRemoved() { + return removed || super.isRemoved(); + } + + private void updateName(EntityHolograms_v1_17_R1 hologram, String line){ + assert chunkLoader.getWhoPlaced().getName() != null; + hologram.setHologramName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class TileEntityChunkLoaderTicker implements TickingBlockEntity{ + + private final TileEntityChunkLoader tileEntityChunkLoader; + + TileEntityChunkLoaderTicker(TileEntityChunkLoader tileEntityChunkLoader){ + this.tileEntityChunkLoader = tileEntityChunkLoader; + } + + @Override + public void a() { + tileEntityChunkLoader.tick(); + } + + @Override + public boolean b() { + return tileEntityChunkLoader.isRemoved(); + } + + @Override + public BlockPosition c() { + return tileEntityChunkLoader.getPosition(); + } + + @Override + public String d() { + return TileEntityTypes.a(tileEntityChunkLoader.getTileType()) + ""; + } + } + +}