diff --git a/v1_16_R3/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R3.java b/v1_16_R3/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R3.java new file mode 100644 index 0000000..1d98281 --- /dev/null +++ b/v1_16_R3/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R3.java @@ -0,0 +1,147 @@ +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.server.v1_16_R3.EntityPlayer; +import net.minecraft.server.v1_16_R3.EnumGamemode; +import net.minecraft.server.v1_16_R3.EnumProtocolDirection; +import net.minecraft.server.v1_16_R3.MinecraftServer; +import net.minecraft.server.v1_16_R3.NetworkManager; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayInBlockDig; +import net.minecraft.server.v1_16_R3.PacketPlayInBlockPlace; +import net.minecraft.server.v1_16_R3.PacketPlayInChat; +import net.minecraft.server.v1_16_R3.PacketPlayInFlying; +import net.minecraft.server.v1_16_R3.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_16_R3.PacketPlayInTransaction; +import net.minecraft.server.v1_16_R3.PacketPlayInUpdateSign; +import net.minecraft.server.v1_16_R3.PacketPlayInWindowClick; +import net.minecraft.server.v1_16_R3.PlayerConnection; +import net.minecraft.server.v1_16_R3.PlayerInteractManager; +import net.minecraft.server.v1_16_R3.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_16_R3.CraftServer; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_16_R3 extends EntityPlayer implements ChunkLoaderNPC { + + private boolean dieCall = false; + + public ChunkLoaderNPC_v1_16_R3(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + this.playerInteractManager.setGameMode(EnumGamemode.CREATIVE); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + ((WorldServer) world).addPlayerJoin(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + if(!dieCall) { + dieCall = true; + getWorldServer().removePlayer(this); + dieCall = false; + } + else { + super.die(); + } + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("socketAddress"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + 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_16_R3/src/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_16_R3.java b/v1_16_R3/src/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_16_R3.java new file mode 100644 index 0000000..f887e02 --- /dev/null +++ b/v1_16_R3/src/com/bgsoftware/wildloaders/nms/EntityHolograms_v1_16_R3.java @@ -0,0 +1,179 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.holograms.Hologram; +import net.minecraft.server.v1_16_R3.AxisAlignedBB; +import net.minecraft.server.v1_16_R3.DamageSource; +import net.minecraft.server.v1_16_R3.EntityArmorStand; +import net.minecraft.server.v1_16_R3.EntityHuman; +import net.minecraft.server.v1_16_R3.EnumHand; +import net.minecraft.server.v1_16_R3.EnumInteractionResult; +import net.minecraft.server.v1_16_R3.EnumItemSlot; +import net.minecraft.server.v1_16_R3.IChatBaseComponent; +import net.minecraft.server.v1_16_R3.ItemStack; +import net.minecraft.server.v1_16_R3.NBTTagCompound; +import net.minecraft.server.v1_16_R3.SoundEffect; +import net.minecraft.server.v1_16_R3.Vec3D; +import net.minecraft.server.v1_16_R3.World; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftArmorStand; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_16_R3.util.CraftChatMessage; +import org.bukkit.entity.Entity; + +import javax.annotation.Nullable; + +@SuppressWarnings("unused") +public final class EntityHolograms_v1_16_R3 extends EntityArmorStand implements Hologram { + + private CraftEntity bukkitEntity; + + EntityHolograms_v1_16_R3(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(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + @Override + public void setHologramName(String name) { + super.setCustomName(CraftChatMessage.fromString(name)[0]); + } + + @Override + public void removeHologram() { + super.die(); + } + + @Override + public 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.onGround) { + this.onGround = 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.onGround) { + this.onGround = false; + } + } + + @Override + public void saveData(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean a_(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @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.PASS; + } + + @Override + public boolean a_(int i, ItemStack item) { + // Prevent stand being equipped + return false; + } + + @Override + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + // Prevent stand being equipped + } + + @Override + public void a(AxisAlignedBB boundingBox) { + // Do not change it! + } + + public void forceSetBoundingBox(AxisAlignedBB boundingBox) { + super.a(boundingBox); + } + + @Override + public void playSound(SoundEffect soundeffect, float f, float f1) { + // Remove sounds. + } + + @Override + public void die() { + // Prevent being killed. + } + + @Override + public CraftEntity getBukkitEntity() { + if (bukkitEntity == null) { + bukkitEntity = new CraftArmorStand(super.world.getServer(), this); + } + return bukkitEntity; + } + +} diff --git a/v1_16_R3/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R3.java b/v1_16_R3/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R3.java new file mode 100644 index 0000000..e41af8d --- /dev/null +++ b/v1_16_R3/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R3.java @@ -0,0 +1,296 @@ +package com.bgsoftware.wildloaders.nms; + +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.server.v1_16_R3.Block; +import net.minecraft.server.v1_16_R3.BlockPosition; +import net.minecraft.server.v1_16_R3.Chunk; +import net.minecraft.server.v1_16_R3.ChunkCoordIntPair; +import net.minecraft.server.v1_16_R3.IBlockData; +import net.minecraft.server.v1_16_R3.ITickable; +import net.minecraft.server.v1_16_R3.ItemStack; +import net.minecraft.server.v1_16_R3.NBTTagCompound; +import net.minecraft.server.v1_16_R3.NBTTagList; +import net.minecraft.server.v1_16_R3.NBTTagLong; +import net.minecraft.server.v1_16_R3.NBTTagString; +import net.minecraft.server.v1_16_R3.TileEntity; +import net.minecraft.server.v1_16_R3.TileEntityMobSpawner; +import net.minecraft.server.v1_16_R3.TileEntityTypes; +import net.minecraft.server.v1_16_R3.World; +import net.minecraft.server.v1_16_R3.WorldServer; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_16_R3.CraftChunk; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R3.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_16_R3 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @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_16_R3(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.tileEntityListTick.add(tileEntityChunkLoader); + + for(org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) { + Chunk chunk = ((CraftChunk) bukkitChunk).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, 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_16_R3::removeHologram); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + 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.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + } + + @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().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable, ITileEntityChunkLoader { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final WChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.COMMAND_BLOCK); + + this.chunkLoader = (WChunkLoader) chunkLoader; + + setLocation(world, blockPosition); + + 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 = position.getY() + 1; + for(int i = hologramLines.size(); i > 0; i--){ + EntityHolograms_v1_16_R3 hologram = new EntityHolograms_v1_16_R3(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void w() { + tick(); + } + + @Override + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(chunkLoader.isNotActive()){ + chunkLoader.remove(); + return; + } + + if(chunkLoader.isInfinite()) + return; + + List hologramLines = chunkLoader.getHologramLines(); + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHolograms_v1_16_R3 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); + } + + private void updateName(EntityHolograms_v1_16_R3 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 + "") + ); + } + + } + +}