Skip to content

Repository files navigation

NPCS

A lightweight NPC plugin for Paper servers. NPCS lets you create and configure interactive NPCs no dependencies required with SQLite persistence, click actions, and a builtin migration system for importing NPCs from Citizens and FancyNpcs.

Requirements

  • Paper 1.21.9 or newer
  • Java 25+

NPCS is built against Paper's Mannequin API, introduced in 1.21.9. Mannequin is a stable, first-party Paper API for player-look NPCs, so NPCS does not rely on packet hacks or fake-player tricks to render its NPCs. Because of this, NPCS is expected to keep working on newer Paper versions without needing an update for every release though as always, check the releases page if you're jumping to a major new Minecraft version.

NPCS works standalone. Citizens and FancyNpcs are optional and only used if you want to migrate existing NPCs from one of them see Migration below.

Features

  • SQLite-backed persistence NPCs survive restarts and chunk unloads despite entity.isPersistent being false
  • Custom skins by Minecraft player name (online or offline, resolved via Mojang profile lookup)
  • Click-triggered actions: run commands as the player or console, or send chat messages
  • Support for MiniMessage tags, legacy &/§ color codes, and JSON text components in names and messages
  • Gravity, immovability, invulnerability, and look-at-player toggles per NPC
  • Folia support
  • Migration system for importing NPCs from Citizens and FancyNpcs
  • bStats metrics

Installation

  1. Download NPCS-<version>.jar from the releases page or Modrinth.
  2. Drop it into your server's plugins/ folder.
  3. Restart the server.

No further setup is required.

Commands

All commands are under /npcs (alias /npc).

Command Description
/npcs help Show the help dialog
/npcs create <name> [entitytype] Create an NPC at your location
/npcs type <entitytype> Change the entity type of the selected NPC
/npcs select [name] Select an NPC to edit, or clear your selection
/npcs rename <newName> Rename the selected NPC
/npcs remove [name] Remove an NPC
/npcs skin <skin> Set the skin of the selected NPC (Mannequin-type NPCs only)
/npcs addcommand <action> <executor> <command> Attach a command to a click action
/npcs removecommand [targetNpc] <commandId> Remove an attached command
/npcs message <action> <msg> / /npcs sendmessage <action> <msg> Attach a chat message to a click action
/npcs removemessage [targetNpc] <messageId> Remove an attached message
/npcs gravity Toggle gravity on the selected NPC
/npcs immovable Toggle whether the selected NPC can be pushed
/npcs invulnerable Toggle invulnerability on the selected NPC
/npcs lookatme Toggle whether the selected NPC turns to face nearby players
/npcs copy <newName> Duplicate the selected NPC
/npcs teleport Teleport yourself to the selected NPC
/npcs tphere Teleport the selected NPC to you
/npcs near List NPCs near your location
/npcs list List all NPCs
/npcs migrate [migrator] Migrate NPCs from another plugin — see Migration
/npcs reload Reload the plugin

Click actions accept LEFT_CLICK, RIGHT_CLICK, or CLICK (either click) matching is case-insensitive, and LEFT/RIGHT are accepted as shorthand for the two specific-click triggers.

Migration

NPCS can import NPCs directly from Citizens or FancyNpcs, either from the live plugin if it's installed alongside NPCS, or straight from that plugin's save file if it isn't.

Run /npcs migrate with no arguments to see a list of available migrators, then run it again with one:

/npcs migrate citizens          # From a running Citizens installation
/npcs migrate citizens-file     # Directly from Citizens/saves.yml
/npcs migrate fancynpcs         # From a running FancyNpcs installation
/npcs migrate fancynpcs-file    # Directly from FancyNpcs/npcs.yml

Migration copies over each NPC's name, location, entity type, skin, and click actions into NPCS's own database. From Citizens, this means commands attached via its Command Trait. From FancyNpcs, this includes both commands and chat messages. Action types with no NPCS equivalent (for example FancyNpcs's wait or need_item actions) are skipped and logged as a warning rather than silently dropped or guessed at.

Migration is built on an extensible Migrator system — every migrator is a small class that implements a single migrate() method, and new ones can be registered at runtime. If you maintain another NPC plugin (or want to migrate from a format not listed above), you can register your own migrator against NPCS's registry without needing a core update.

API

The public API lives in its own Gradle module, api, so other plugins can depend on just the contract (a handful of interfaces, an event package, and no NMS/paperweight dependency) without pulling in the rest of the plugin. The main NPCS module implements it internally and registers itself on startup.

To use it from another plugin, depend on the api module (published as its own jar/artifact alongside the main plugin) as compileOnly, and depend on NPCS in your plugin.yml/paper-plugin.yml so it's loaded first:

dependencies {
    compileOnly(project(":NPCS:api")) 
}

Everything starts from NpcController, which is the same code the /npcs command runs on top of:

import me.hihelloy.work.npcs.control.NpcController;
import me.hihelloy.work.npcs.control.TriggerType;

NpcController npcs = NpcController.get();

// Spawn one with a builder
npcs.builder("Merchant")
    .at(player.getLocation())
    .skin("Notch")
    .invulnerable(true)
    .lookAtPlayers(true)
    .spawn()
    .thenAccept(npc -> npc.addMessage(TriggerType.RIGHT_CLICK, "Welcome, traveller!"));

// Or spawn one with defaults
npcs.spawn("Guard", player.getLocation());

// Look one up
npcs.find("Merchant").ifPresent(npc -> {
    npc.setSkin("jeb_");
    npc.addCommand(TriggerType.RIGHT_CLICK, false, "say Hello from the console!");
});

// Iterate everything that's currently spawned
for (var npc : npcs.all()) {
    System.out.println(npc.getName() + " -> " + npc.getLocation());
}

// Remove one
npcs.remove("Guard");

Each NPC you get back is a ManagedNpc a live handle that always resolves to the current entity for that name, even after a rename or a reload. It covers the same ground as the /npcs command:

ManagedNpc method Equivalent command
getEntity(), getLocation(), isSpawned()
rename(newName) /npcs rename
remove() /npcs remove
setSkin(name), getSkin() /npcs skin
setGravity(bool), hasGravity() /npcs gravity
setImmovable(bool), isImmovable() /npcs immovable
setInvulnerable(bool), isInvulnerable() /npcs invulnerable
setLookAtPlayers(bool), isLookingAtPlayers() /npcs lookatme
teleportTo(location) /npcs tphere
copyTo(newName, location) /npcs copy
addCommand(trigger, runAsPlayer, command), removeCommand(id), getCommands() /npcs addcommand, /npcs removecommand
addMessage(trigger, message), removeMessage(id), getMessages() /npcs message
selectFor(player), isSelectedBy(player) /npcs select

NpcController also covers lookup and bulk operations: find(name), require(name), exists(name), all(), count(), and nearest(location).

Three Bukkit events are fired so other plugins can react without polling:

  • NpcSpawnEvent an NPC was created or spawned back in
  • NpcRemoveEvent an NPC was removed or died
  • NpcInteractEvent (cancellable) a player clicked an NPC, before its configured commands/messages run

Metrics

NPCS reports anonymous usage statistics via bStats:

bstats.org/plugin/bukkit/NPCS-Hihelloy/33242

This can be disabled globally for all plugins on your server via plugins/bStats/config.yml.

Permissions

Access is gated by npcs.admin or npcs.use at the top level, with a permission per subcommand (npcs.command.<subcommand>, e.g. npcs.command.create, npcs.command.migrate) for granular control.

Support

Found a bug or have a feature request? Open an issue on GitHub.

About

A Paper plugin that allows players to create npcs and do stuff with them!

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages