Create NPC with paperweight 1.21.1

zaralx

New member
Jan 31, 2025
1
0
1
after working with npc on spigot 1.20.1 nms I decided to try to write npcManager on 1.21.1 paperweight, but already 2 hours I sit and can not do something working, after a bunch of promt chat gpt I came to the forum.
The current npc class looks like this:
Java:
package ru.zaralx.evoSurvival.utils.npc;

import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.Getter;
import lombok.Setter;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.players.PlayerList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import ru.zaralx.evoSurvival.EvoSurvival;

import java.util.UUID;

@Getter
public abstract class NpcBase {
    protected final UUID uuid;
    protected Location location;
    @Setter protected String displayName;
    @Setter protected Skin skin;
    protected ServerPlayer npcEntity;

    public NpcBase(Location location, String displayName, Skin skin) {
        this.uuid = UUID.randomUUID();
        this.location = location;
        this.displayName = displayName;
        this.skin = skin;
    }

    public void spawn() {
        GameProfile profile = new GameProfile(uuid, displayName);
        profile.getProperties().put("textures", new Property("textures", skin.getTexture(), skin.getSignature()));

        MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
        ServerLevel world = ((CraftWorld) location.getWorld()).getHandle();
        npcEntity = new ServerPlayer(server, world, profile, ClientInformation.createDefault());

        npcEntity.setPos(location.getX(), location.getY(), location.getZ());

        PlayerList playerList = server.getPlayerList();
        Connection connection = new Connection(null);
        CommonListenerCookie cookie = CommonListenerCookie.createInitial(profile, true);
        playerList.placeNewPlayer(connection, npcEntity, cookie);

        showToNearbyPlayers();
        startLookingAtPlayers();
    }

    public void remove() {
        if (npcEntity != null) {
            PlayerList playerList = ((CraftServer) Bukkit.getServer()).getHandle();
            playerList.remove(npcEntity);
        }
    }

    public void showToNearbyPlayers() {
        for (Player player : Bukkit.getOnlinePlayers()) {
            if (player.getLocation().distance(location) <= 64) {
                showToPlayer(player);
            }
        }
    }

    protected void showToPlayer(Player player) {
        ServerGamePacketListenerImpl connection = ((CraftPlayer) player).getHandle().connection;

        connection.send(new ClientboundPlayerInfoUpdatePacket(
                ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER,
                npcEntity
        ));

        // Cannot resolve symbol 'ClientboundAddPlayerPacket'
        connection.send(new ClientboundAddPlayerPacket(npcEntity));
    }

    private void startLookingAtPlayers() {
        new BukkitRunnable() {
            @Override
            public void run() {
                Player nearest = getNearestPlayer();
                if (nearest != null) {
                    npcEntity.setYRot(nearest.getLocation().getYaw());
                    npcEntity.setXRot(nearest.getLocation().getPitch());
                }
            }
        }.runTaskTimer(EvoSurvival.getInstance(), 0L, 10L);
    }

    private Player getNearestPlayer() {
        Player nearest = null;
        double minDistance = Double.MAX_VALUE;

        for (Player player : Bukkit.getOnlinePlayers()) {
            double distance = player.getLocation().distance(location);
            if (distance < minDistance) {
                minDistance = distance;
                nearest = player;
            }
        }
        return nearest;
    }
}

For context, its npcManager:

Java:
package ru.zaralx.evoSurvival.utils.npc;

import java.util.HashMap;
import java.util.Map;

public class NpcManager {
    private final Map<Integer, NpcBase> npcMap = new HashMap<>();

    public void addNpc(NpcBase npc) {
        npcMap.put(npc.getNpcEntity().getId(), npc);
    }

    public void removeNpc(NpcBase npc) {
        npcMap.remove(npc.getNpcEntity().getId());
    }

    public void removeAllNpc() {
        for (NpcBase npc : npcMap.values()) {
            npc.remove();
        }
        npcMap.clear();
    }

    public NpcBase getNpc(Integer id) {
        return npcMap.get(id);
    }
}
and Skin class

Java:
package ru.zaralx.evoSurvival.utils.npc;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;

public class Skin {
    private String signature = "";
    private String texture = "";

    public Skin(String texture, String signature) {
        this.signature = signature;
        this.texture = texture;
    }

    public Skin(String nickname) {
        getSkin(nickname);
    }

    private void getSkin(String nickname) {
        try {
            URL mojangUrl = new URL("https://api.mojang.com/users/profiles/minecraft/" + nickname);
            BufferedReader reader = new BufferedReader(new InputStreamReader(mojangUrl.openStream()));
            MojangProfile profile = new Gson().fromJson(reader, MojangProfile.class);
            reader.close();

            URL skinUrl = new URL("https://sessionserver.mojang.com/session/minecraft/profile/"+profile.getId()+"?unsigned=false");
            BufferedReader skinReader = new BufferedReader(new InputStreamReader(skinUrl.openStream()));
            JsonObject skinJson = new Gson().fromJson(skinReader, JsonObject.class);
            skinReader.close();

            JsonArray properties = skinJson.getAsJsonArray("properties");
            if (properties != null && !properties.isEmpty()) {
                JsonObject property = properties.get(0).getAsJsonObject();
                String value = property.get("value").getAsString();
                String signature = property.get("signature").getAsString();
                this.texture = value;
                this.signature = signature;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getSignature() {
        return signature;
    }

    public String getTexture() {
        return texture;
    }

    private static class MojangProfile {
        private String id;
        private String name;

        public String getId() {
            return id;
        }
    }
}

I apologize if I wrote a complete nonsense in NpcBase, I have rewritten this code 1000 times