Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Asynchronous, high-performance Minecraft NPC library for 1.8-1.21 servers.

License

NotificationsYou must be signed in to change notification settings

juliarn/npc-lib

Repository files navigation

GitHub LicenseMaven Central Version

GitHub forksGitHub Repo stars

Minecraft NPC-Lib

Simple & Extendable NPC library for Minecraft Java Edition Servers

Features

  • Bukkit & Forks (including Folia) supported viaProtocolLib orPacketEvents
  • FullMinestom &Fabric support (latest version only)
  • Skin (Static and Dynamic loading)
  • Attributes (Status, Pose, Skin Layers)
  • Equipment (Main & Off-Hand, Armor)
  • Interaction (Interact & Attack)
  • Action Controller (Automatic Looking at Player, Player Imitation & Spawning etc.)
  • LabyMod Extension (Sending Emotes & Sprays)
  • ...

There are someimages down below showcasing the use and features of this library.

Installation

All modules are available inmaven central:

Module artifact nameModule description
npc-lib-apiGeneral NPC-Lib API without platform specific class usage. This module should be used when the underlying implementation does not matter.
npc-lib-commonAbstract implementation of the api module. This module should be used when a new platform implementation is made.
npc-lib-bukkitPlatform specific implementation for Bukkit. This module implements the complete API (and common) to support Bukkit (and forks).
npc-lib-minestomPlatform specific implementation for Minestom. This module implements the complete API (and common) to support Minestom (and forks).
npc-lib-fabricPlatform specific implementation for Fabric. This module implements the complete API (and common) to support Fabric andmust be installed as a mod on the server.
npc-lib-labymodThis module contains helper methods for accessing LabyMod NPC features (such as emotes and stickers). See theLabyMod documentation for more information.

How to include a module

Maven:

<dependency>  <groupId>io.github.juliarn</groupId>  <artifactId>(module name from the list above)</artifactId>  <version>(latest version)</version>  <scope>compile</scope></dependency>

Gradle:

implementation("io.github.juliarn","(module name from the list above)","(latest version)")

Repositories

Depending on your setup you might need to add the following repositories as well to download all the transitivedependencies coming from the modules:

  • https://repo.papermc.io/repository/maven-public/ (ForPaperLib)
  • https://repository.derklaro.dev/releases/ (You can also usehttps://jitpack.io instead, used e.g.forProtocolLib).
  • https://repo.codemc.io/repository/maven-releases/ (ForPacketEvents)
  • https://s01.oss.sonatype.org/content/repositories/snapshots/ (For all kinds of snapshot dependencies that don't havea stable release published to maven central yet)

Shading

This library is specifically made in a way that it can be shaded into your plugin jar. Below is a list of packages thatare used by this library and that you probably want to relocate to prevent dependency issues with other pluginsincluding the same libraries. You can use the gradle or maven shade plugin to achieve this:

  • net.kyori
  • io.papermc.lib
  • io.leangen.geantyref
  • io.github.retrooper
  • com.github.retrooper
  • com.github.juliarn.npclib

Example Usage

Platform specific (server software specific) code is only needed to obtain a platform instance. The platform instancecan then be used to create NPCs, without knowing which underlying platform is actually used.

Usually all classes you need provide a builder and shouldn't be instantiated directly.To obtain a platform builder use the following:

On Bukkit

BukkitPlatform.bukkitNpcPlatformBuilder()

On Minestom

MinestomPlatform.minestomNpcPlatformBuilder()

On Fabric

FabricPlatform.fabricNpcPlatformBuilder()

Configuring the Platform

In all further examples bukkit will be used as a reference, but the api is the same on all other platforms:

BukkitPlatform.bukkitNpcPlatformBuilder()// enables debug logging in the platform. this mostly enables errors// to be printed into the console directly instead of being held back// to prevent spamming the console.// Defaults to false (debug disabled).  .debug()// sets the extension of the platform, which is usually the plugin that// uses the plugin. on bukkit this is for example used to schedule sync// tasks using the bukkit scheduler.// This option has no default and must be set.  .extension()// the logger to use for internal logging of the library. while this logger// has the possibility to log info messages, the level won't be used unless// information relevant to the user are being printed.// Defaults to a platform-specific logger instance, on bukkit it uses an// implementation which is backed by the plugin logger.  .logger()// the event manager to use for the platform. all events, such as npc interacts// or spawns are propagated using this event manager.// Defaults to an internal default implementation.  .eventManager()// the tracker for npcs that are created by this library. as explained below// a spawned npc can either be tracked or kept untracked if not needed.// the tracker is used to store all spawned npcs and automatically execute// actions on them, for example automatic spawning when a player moves into// their spawn distance. it's also used to fire npc events such as interact when// an interact packet is received.// Defaults to an internal default implementation.  .npcTracker()// a scheduler for tasks that need to be executed either sync or async. the// implementation depends on the platform, for bukkit it's backed by the bukkit// scheduler, on minestom the minestom scheduler is used.// Defaults to a platform-specific implementation.  .taskManager()// the resolver to use for npc profiles. the resolver is given an unresolved// version of a profile (for example only an uuid or name) and completes the// profile data (name, uuid, textures).// see BukkitProfileResolver for the available resolvers on bukkit (paper or spigot)// Defaults to a platform-specific implementation.  .profileResolver()// the resolver for worlds of npcs. each npc position contains a world identifier// which is resolved using this resolver. the resolver can also provide the// identifier of a world using the world instance.// see BukkitWorldAccessor for the available resolvers on bukkit (name or key based)// see MinestomWorldAccessor for the available resolver on minestom (uuid based)// Defaults to a platform-specific implementation.  .worldAccessor()// the provider for version information about the current platform. it's internally// used to determine which features can be used. an example is the profile resolver:// when on paper 1.12 or later the paper profile resolver is used, when on spigot// 1.18.2 or later the spigot profile resolver is used, else a fallback mojang api// based access is used.// see BukkitVersionAccessor for the bukkit implementations (PaperLib based)// see MinestomVersionAccessor for the minestom implementation// Defaults to a platform-specific implementation.  .versionAccessor()// the factory for packets that need to be sent in order to spawn and manage npcs.// see BukkitProtocolAdapter for the available options on Bukkit (ProtocolLib or PacketEvents)// see MinestomProtocolAdapter for the minestom implementation// Defaults to a platform-specific implementation.  .packetFactory()// configures the default action controller for the platform. if this method isn't called// during the build process, the default action controller is disabled. the method provides// a builder which can be used to modify the settings of the action controller. if the default// values should be used, don't call any methods and just provide an empty lambda.  .actionController(builder ->builder    .flag(NpcActionController.SPAWN_DISTANCE,5))// builds the final platform object which can then be used to spawn and manage npcs  .build();

The action controller

The standard action controller controls stuff like automatic npc spawning, player imitation and more. The actioncontroller implementations are controlled by flags which can be dynamically adjusted as needed. Custom flags can alsobe added to control extra stuff that isn't directly available using the default action controller. The default actioncontroller flags are the following (all located as constants in theNpcActionController class):

Flag NameDefault ValueDescription
AUTO_SYNC_POSITION_ON_SPAWNtrueControls if the npc head rotation should automatically be set when the npc is spawned for a player. This only does something when the npc is set up to look at the player.
SPAWN_DISTANCE50The distance (in blocks) in which the npc will be shown to a player.
TAB_REMOVAL_TICKS30The ticks before the player removal packet is sent when spawning an npc. This can be lowered on newer versions (1.19.3+) as the player isn't added to the tablist at all due to new protocol options.
IMITATE_DISTANCE20The distance (in blocks) in which the npc will start imitating the player actions.

Spawning a NPC

The platform object constructed in the last step contains a method to construct a NPC builder:newNpcBuilder. Thisbuilder can be used to customize and spawn a npc:

platform.newNpcBuilder()// the id to use for the entity. if not provided a random integer is generated and used instead.  .entityId()// the position where the npc should be spawned and located.// this option is required and must be set before spawning the npc.  .position()// sets resolved or unresolved profile for the npc. if an unresolved profile is used the method// returns a future completed with the current build when the profile was resolved. a specific// profile resolver to use can be provided as well.  .profile()// allows to add additional settings to the npc://  - a tracking rule to select the players which should be tracked automatically by the npc//  - a profile resolver which can dynamically resolve the npc profile when it gets spawned// if no builder callback is provided default npc settings are applied. this means that the npc will be// spawned to all players in range and the skin (via the profile) provided to this builder will always be used.  .npcSettings()// sets a value for a flag on the npc.  .flag();

There are two ways to build a final npc instance:build andbuildAndTrack. The first option only builds a raw NPCinstance, leaving the full control to the caller which actions should be executed on the NPC. The second one uses thenpc tracker of the base platform to track the npc, allowing the action controller and packet listeners to call actionsfor the NPC.

NPC flags

Some action controller related settings can also be done by using the npc flags (provided as constants inNPC).However, flags can also be used to add custom markers or settings to any flagged object, for example to store the baseconfiguration from which a NPC was created.

Flag NameDefault ValueDescription
LOOK_AT_PLAYERfalseControls if tracked NPCs should automatically look at the player (only has an effect if the action controller is enabled on the platform)
HIT_WHEN_PLAYER_HITSfalseControls if tracked NPCs should swing their main hand in case the player swings his main hand (only has an effect if the action controller is enabled on the platform)
SNEAK_WHEN_PLAYER_SNEAKSfalseControls if tracked NPCs should sneak when the player sneaks (only has an effect if the action controller is enabled on the platform)

Events

This library provides some events to catch when certain actions are executed. Note that all events shown below are notplatform specific. Event listeners must be registered into the event manager provided by the platform and not, forexample, into the bukkit event system. Note that event listeners can be called from any thread, there is no guaranteethat events happen on a specific thread (such as the main server thread). For example, if you need to access the Bukkitapi, make sure that you manually execute the corresponding code on the main server thread.

Event Class NameDescription
AttackNpcEventFired when a player hits (attacks) a NPC
InteractNpcEventFired when the player interacts with a NPC. Note that due to Minecraft weirdness this event is always called for both player hands at the moment.
ShowNpcEvent.PreFired before a NPC is spawned for a player. This event can be cancelled to prevent this action.
ShowNpcEvent.PostFired after a NPC was successfully spawned to a player.
HideNpcEvent.PreFired before a NPC is despawned for a player. This event can be cancelled to prevent this action.
HideNpcEvent.PostFired after a NPC was successfully despawned for a player.

Unfortunately events cannot be fired typesafe. Therefore, the player instance must be known to the event receiver andmust be put into a typed variable to give it a specific type rather than object:

publicfinalclassAttackEventConsumerimplementsNpcEventConsumer<AttackNpcEvent> {@Overridepublicvoidhandle(AttackNpcEventevent) {varbadPlayer =event.player();// player type is object as the type couldn't be inferredPlayergoodPlayer =event.player();// player type is Player, but it must be known at compile time  }}

Code Examples

Minimal Example

The following code is a minimal example how a platform object is created and an NPC is spawned. In this example thedefault action controller is enabled which means that after spawning the npc usingbuildAndTrack the npc willautomatically be spawned and shown to players in a 50 block range:

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .actionController(builder -> {})// enable action controller without changing the default config    .build();publicvoidspawnNpc(Locationlocation) {this.platform.newNpcBuilder()      .position(BukkitPlatformUtil.positionFromBukkitLegacy(location))      .profile(Profile.unresolved("derklaro"))      .thenAccept(builder -> {varnpc =builder.buildAndTrack();// continue using the npc...npc.unlink();// when the npc is no longer needed it can be completely removed using this method      });  }}

Configuring the action controller

As mentioned above the action controller can be changed using flags. In this example the action controller of theplatform is configured in a way that NPCs are shown to players being 100 blocks away and simulation already starts whenthe player is 50 block away:

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .actionController(builder ->builder      .flag(NpcActionController.SPAWN_DISTANCE,100)      .flag(NpcActionController.IMITATE_DISTANCE,50))    .build();}

Explicitly setting the protocol adapter and world resolver

Sometimes it's crucial that specific stuff is always setup the same, which isn't the case for some settings. In thisexample the protocol adapter will be set to always use PacketEvents (rather than preferring ProtocolLib when availableon the server) and the world resolver will always use the world name (instead of using the world key in modern versionson paper):

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .packetFactory(BukkitProtocolAdapter.packetEvents())    .worldAccessor(BukkitWorldAccessor.nameBasedAccessor())    .build();}

Using the NPC settings to use player skins and only track specific players

In this example the spawned NPC is configured to use the skin of the player it is being spawned to, as well as onlybeing spawned to players that have reached an exp count of 50 or higher:

publicfinalclassTestPluginextendsJavaPlugin {publicvoidspawnNpc(Locationlocation) {this.platform.newNpcBuilder()      .position(BukkitPlatformUtil.positionFromBukkitLegacy(location))      .npcSettings(builder ->builder        .profileResolver((player,npc) -> {// copy the profile properties into the profile of the npc to preserve the name// and uuid of the npc and not use the player name / uuid instead.varplayerProfile =Profile.unresolved(player.getUniqueId());returnthis.platform.profileResolver()            .resolveProfile(playerProfile)            .thenApply(profile ->npc.profile().withProperties(profile.properties()));        })        .trackingRule((npc,player) ->player.getExp() >=50))      .profile(Profile.unresolved("derklaro"))      .thenAccept(builder -> {varnpc =builder.buildAndTrack();// continue using the npc...      });  }}

Adding a custom flag to a NPC

Custom flags can be quite helpful when spawning a NPC, for example if the NPC was spawned based off a configuration: aflag can be used to keep the original configuration entry around, for example to easily allow modification or to resolveadditional settings on an NPC interact:

publicfinalclassTestPluginextendsJavaPlugin {privatestaticfinalNpcFlag<NpcEntry>CONFIG_ENTRY_FLAG =NpcFlag.flag("npc_config_entry",null);publicvoidspawnNpc(NpcEntryconfigEntry) {this.platform.newNpcBuilder()      .flag(CONFIG_ENTRY_FLAG,configEntry)      .position(BukkitPlatformUtil.positionFromBukkitLegacy(configEntry.location()))      .profile(Profile.unresolved(configEntry.profileName()))      .thenAccept(builder -> {varnpc =builder.buildAndTrack();varconfigEntry =npc.flagValue(CONFIG_ENTRY_FLAG).orElseThrow();// continue using the npc...      });  }}

Customizing the imitation actions of the NPC

By default, a NPC will not imitate any actions the players and will not look at the player. In this example the flagswill be set to enable that behaviour: the NPC will look at the player and imitate if the player hits or sneaks:

publicfinalclassTestPluginextendsJavaPlugin {publicvoidspawnNpc(Locationlocation) {this.platform.newNpcBuilder()      .flag(Npc.LOOK_AT_PLAYER,true)// look at the player      .flag(Npc.HIT_WHEN_PLAYER_HITS,true)// swing main hand when the player does so      .flag(Npc.SNEAK_WHEN_PLAYER_SNEAKS,true)// sneak when the player does so      .position(BukkitPlatformUtil.positionFromBukkitLegacy(location))      .profile(Profile.unresolved("derklaro"))      .thenAccept(builder -> {varnpc =builder.buildAndTrack();// continue using the npc...      });  }}

Handling interactions with a NPC

When a NPC is tracked when being spawned the protocol will catch interacts with it and fire a corresponding lib eventdepending on the action that was taken. When being attacked (usually triggered by a left click) aAttackNpcEvent eventis fired, when being interacted with (usually triggered by a right click) aInteractNpcEvent event is set off:

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .actionController(builder -> {})// enable action controller without changing the default config    .build();publicvoidregisterNpcListeners() {vareventManager =this.platform.eventManager();eventManager.registerEventHandler(AttackNpcEvent.class,attackEvent -> {varnpc =attackEvent.npc();Playerplayer =attackEvent.player();player.sendMessage("You attacked NPC " +npc.profile().name() +"! That's not nice!");    });eventManager.registerEventHandler(InteractNpcEvent.class,interactEvent -> {varnpc =interactEvent.npc();Playerplayer =interactEvent.player();player.sendMessage("[" +npc.profile().name() +"]: Move along citizen!");    });  }}

Enabling skin layers & changing the entity status

Some Minecraft functionalities are controlled by metadata. This includes for example sneaking, skin layers or theentity status. Note: in the example shown below the entity status will be reset when the npc meta of sneaking isupdated. Therefore, the auto sneaking action is basically exclusive with the entity status settings:

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .actionController(builder -> {})// enable action controller without changing the default config    .build();publicvoidregisterNpcListeners() {vareventManager =this.platform.eventManager();eventManager.registerEventHandler(ShowNpcEvent.Post.class,showEvent -> {varnpc =showEvent.npc();Playerplayer =showEvent.player();// enable all skin playersnpc.changeMetadata(EntityMetadataFactory.skinLayerMetaFactory(),true).schedule(player);// set the npc on fire// note: the glowing entity status must be sent in order for the entity to appear glowing. The logic of adding// the npc into a team and setting the glowing color must be handled by your plugin.varentityStatus =EnumSet.of(EntityStatus.ON_FIRE);npc.changeMetadata(EntityMetadataFactory.entityStatusMetaFactory(),entityStatus).schedule(player);    });  }}

Adding items into the inventory of a npc

You can add items into the main hand, off hand & armor inventory item slots of a NPC:

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .actionController(builder -> {})// enable action controller without changing the default config    .build();publicvoidregisterNpcListeners() {vareventManager =this.platform.eventManager();eventManager.registerEventHandler(ShowNpcEvent.Post.class,showEvent -> {varnpc =showEvent.npc();Playerplayer =showEvent.player();// puts a dragon egg into the main hand of the npcvardragonEggItem =newItemStack(Material.DRAGON_EGG);npc.changeItem(ItemSlot.MAIN_HAND,dragonEggItem).schedule(player);    });  }}

Additional protocol-related methods in NPC

The NPC class contains a lot of utility methods to work with a NPC, some of them were shown already. This collectionhere shows some additional useful methods that can be used when working with the NPC. Note that all constructed packetsbelow are only sent to one specific player. If you want other players to see the change as well you can either providea list of players to send the change to or schedule it for all players that are tracked by the NPC (only works when theaction controller is enabled in the platform):

publicfinalclassTestPluginextendsJavaPlugin {privatefinalPlatform<World,Player,ItemStack,Plugin>platform =BukkitPlatform    .bukkitNpcPlatformBuilder()    .extension(this)    .actionController(builder -> {})// enable action controller without changing the default config    .build();publicvoidregisterNpcListeners() {vareventManager =this.platform.eventManager();eventManager.registerEventHandler(ShowNpcEvent.Post.class,showEvent -> {varnpc =showEvent.npc();Playerplayer =showEvent.player();// let the NPC look into the direction of 0, 50, 0varspawnLocation =Position.position(0,50,0,npc.position().worldId());npc.lookAt(spawnLocation).schedule(player);// lets the NPC swing his main armnpc.playAnimation(EntityAnimation.SWING_MAIN_ARM).schedule(player);// sets the player head rotation to yaw=0 & pitch=90npc.rotate(0,90).schedule(player);    });  }}

Manually (de-) spawning a NPC

Sometimes you don't want to leave the job of automatically (de-) spawning a npc to the action controller. In these casesyou need to track and untrack the players manually from a NPC:

publicfinalclassTestPluginextendsJavaPlugin {publicvoidspawnNpcToPlayer(Npc<World,Player,ItemStack,Plugin>npc,Playerplayer) {// use this method if you want to spawn the npc to the player, but only if all preconditions// (such as the player tracking rule) are met. You can manually check if a player would be// tracked by a npc by calling: `npc.shouldIncludePlayer(player)`.npc.trackPlayer(player);// Use this method if you want to spawn the npc to the player even is preconditions aren't met.// Note: this still calls the ShowNpc.Pre event which can still be cancelled by one of your event// listeners causing the npc to not get spawned for the player.npc.forceTrackPlayer(player);  }publicvoidremoveNpcForPlayer(Npc<World,Player,ItemStack,Plugin>npc,Playerplayer) {// despawns the npc for the given player unless the npc is not spawned for the player. you can always// check the players for which a NPC is spawned by calling `npc.trackedPlayers()` or check for a// specific player by using `npc.tracksPlayer(player)`.// Note: this method calls the HideNpcEvent.Pre event which means that an event listener can// prevent the npc despawn process from being executed.npc.stopTrackingPlayer(player);  }}

Letting the NPC do a LabyMod emote

Using the LabyMod extension module, packets can be constructed to make the players do LabyMod emotes and Sprays. Notethat NPCs must be spawned with their second uuid half being zeroed to use this feature. The factory only supportsLabyMod version 4 (neo). A detailed explanation of the mentioned limitation and a list of available emotes can be foundin theLabyMod Developer Documentation:

publicfinalclassTestPluginextendsJavaPlugin {publicvoidplayLabyModEmoteForPlayer(Npc<World,Player,ItemStack,Plugin>npc,Playerplayer,int...emoteIds) {LabyModExtension.createEmotePacket(npc.platform().packetFactory(),emoteIds).schedule(player,npc);  }}

Compiling from source

Follow these steps to build and publish the library to your local maven repository:

git clone https://github.com/juliarn/npc-lib.gitcd npc-libgradlew publishToMavenLocal

Images

S1

S2

S3

S4

S5

Feel free to open a pull request to add your image to this list.


[8]ページ先頭

©2009-2025 Movatter.jp