Create NPC with paperweight 1.21.1

  • After careful consideration and due to limited usage, we’ve made the decision to discontinue the PaperMC forums. Moving forward, we recommend using Hangar for plugin uploads, and for all other community discussions and support, please join us on Discord.

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