-
Notifications
You must be signed in to change notification settings - Fork 8
Creating NPCs with Kelp
This tutorial teaches you how to create NPCs with kelp-v0.2.0 and above. The old way of creating NPCs introduced in 0.0.1 is not supported anymore.
First of all, you need a KelpNpc
object, which can then be used to manipulate data and give your fake player some abilities. New NPC instances can be created using the KelpNpcFactory
class or the static factory of KelpNpc
:
KelpNpc npc = KelpNpc.create();
Before spawning the NPC, you have to provide some essential data including the spawn location or the KelpPlayer
who owns the NPC:
npc.location(player.getLocation());
npc.player(player);
npc.spawn();
There is also more data to set, but it is not necessary to define it before the NPC spawns as there are default values for that. Here is an overview of methods to use to change NPC properties:
Name | Description | Example |
---|---|---|
itemInHand(KelpItem) |
Sets the item the NPC is currently holding in its main hand. This can be any material or block. To reset the item, simply set KelpMaterial.AIR here. |
npc.itemInHand(KelpItem.create().material(KelpMaterial.APPLE)) |
helmet(KelpItem) |
Sets the helmet the NPC is currently wearing. To reset the helmet, simply apply KelpMaterial.AIR here as well. Unlike in the other armor slots, you can also apply different hat types than a helmet such as blocks here. |
npc.helmet(KelpItem.create().material(KelpMaterial.IRON_HELMET)) |
chestPlate(KelpItem) leggings(KelpItem) boots(KelpItem)
|
Set the other armor slots of the npc. To reset an item, use KelpMaterial.AIR
|
see above |
customName(String) |
Sets the NPC's custom name, which is normally displayed above its head. If there is nothing else defined, this name will be used for the tablist as well. If no custom name is defined, a random one will be generated by Kelp. You can also hide custom names. | npc.customName("Bob der Baumeister") |
customNameShown(Boolean) |
If set to true , Kelp will display the NPC's custom name above it's head. By default this is set to false . |
customNameShown(true) hideCustomName() showCustomName()
|
tabListName(String) |
Sets the name the npc will be displayed with in the server's tablist. If this name is not set, Kelp will use the customName . |
tabListName("GommeHD") |
showInTab(boolean) |
If set to true , the NPC will be shown in the server's tablist under the name set in the above method. Default value is false . |
showInTab(true) |
uuid(UUID) uuid(String)
|
Sets the NPC's uuid, which is used in its GameProfile . So it might have an impact on which default skin is chosen. But normally it is recommended to not overwrite this value as Kelp generates a default one when the NPC is spawned. |
npc.uuid(UUID.randomUUID()) npc.uuid("30bec34e-49a9-4286-ae77-4957abef42a3")
|
titleLines(Supplier<List<String>>) |
Sets the title lines above the NPC's head. Those have the same location as the customName , which is why it's recommended to disable the custom name when using multiple title lines. Those can be updated anytime using updateTitleLines() . |
npc.titleLines(() -> Lists.newArrayList( "§2KELP DEMO", "§7Welcome, " + player.getName() )); |
sneak() unSneak() setSneaking(boolean)
|
Makes the NPC crouch or stand normally again. | setSneaking(false) |
There are multiple ways to set the skin of your NPC.
One is to set the UUID of your NPC to the UUID of the player whose skin it should take.
KelpNpc npc = npcFactory.newKelpNpc();
npc.uuid("858fdbb6-871c-48fa-b8e8-69bf66c6a102");
You can get the UUID of a player on websites like NameMC or the Mojang API. Note that the Mojang API shortens the UUID by cutting the - (dashes)
away, which are needed by Kelp. So when you want to use the API, remember to convert the UUID into the conventional format.
GET https://api.mojang.com/users/profiles/minecraft/<username>
Possible output:
{"name":"pxav","id":"858fdbb6871c48fab8e869bf66c6a102"}
The major drawback of this method is that the player whose UUID you pick can change their skin at any time, so will your NPC skin. So you do not have control over your skin. To avoid this and ensure the consistency of your skin, look at the second method.
Every skin has a texture and a signature, which can both be queried via the Mojang API:
GET https://sessionserver.mojang.com/session/minecraft/profile/<shortenedUuid>?unsigned=false
Possible output:
{
"id" : "858fdbb6871c48fab8e869bf66c6a102",
"name" : "pxav",
"properties" : [ {
"name" : "textures",
"value" : "ewogICJ0aW1lc3RhbXAiIDogMTU5MjQ4ODg1MDc0NSwKICAicHJvZmlsZUlkIiA6ICI4NThmZGJiNjg3MWM0OGZhYjhlODY5YmY2NmM2YTEwMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJweGF2IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2YyZmVhZThkMzFlYzc0NTQ4MTE5Y2E0NThmNWRhNTc2MzU0ZmE5NzUxYWE0YWU2M2Y1NDYwODJmODA2NTFiMmIiCiAgICB9CiAgfQp9",
"signature" : "sd/uN32SL73hcW1RN0fRATCLnp8Y4WJlYPwodVI5L1ekmlNIhXVobMkwv423QKz3rPtHdeveyFumZizLqcR66f9CGGqOaF9HDgnTAU/dv0svSVaYfuZjXAP5bFuJ78vpSQMVbsHfS4sJKstEJzIvcWhHoSJzhHVEHCan1ytK8rLRp+SqN/HzUV3ZQp+dRxEZ+hz+CF0RJo38a1asr7MAtNYxriLRgaxVUhf9f5JH3jY1bY2DnFXKdroWWrvlYhXaswK52UUQ8dbaiQj2bS8lj89EW8aXlLl4Vqg5Nm34xVpnIMSAuinnWBn3UK1Dwfi3/aBn55dUteTS4HbQcsK8GmhHTM1UoqL81V8iXSdWm/bAOr0wpXq8xbuIzBaFcwPWOt2jZJCLEROLZCg7IgY8hzbxmFI2UoUzEys3gUigLnOHG3rUhDX/mPAyWwqhTJ+TFcBv9n8sSPTrdiwVZfjJeT1t8HkPMP6CWwY41SadmgtYnrFrtLVA3ZJGO7SafzO5sg1fdBw/pllb8J/zmf6hADiEqappiAi1DSPOrjvYsQM0C9PwA8DGb7Gr85Qfz/8CJe8/ZT6tSZVI+4D9YbflAiHjJWnP5Tn3CnLn/16Ks/VsyD6RUY+tN/A/EkgHNdPNY7jcswKHj+X0W4BmOg7ErPuJLaZ07SblXHXj3JOtxQA="
} ]
}
The values of "value"
(which is equal to texture) and "signature"
can be passed to the npc as follows:
KelpNpc npc = npcFactory.newKelpNpc();
npc.skinTexture("yourSkinTexture");
npc.skinSignature("yourSkinSignature");
The advantage of this technique is that your skin remains constant as you can download the skin texture once and save it in a config file for example. This makes you independent of other players changing their skin.
Please note that at the current time, NPC skins can only be manipulated before the NPC is actually spawned. It is being worked on updating skins at runtime.
Kelp distinguished between despawning
and removing
an NPC. When a player walks out of range of an NPC - let's say 50 blocks away from it, it is no longer rendered by the player's client and will disappear when the player comes back. In this case, it would make sense to temporarily despawn the NPC when the player is too far away while being able to respawn it at any time (which can be done using NPC activities, which is shown later in this article).
So despawning
is when you want to make the NPC invisible for the client while keeping the data of it in the Kelp NPC repository, so the data is still stored. Removing
instead means that the NPC is not only despawned, but also removed from the repository, and all data (including listeners, etc.) is cleared. So be careful to choose the right option for your case.
// only despawns the npc
npc.deSpawn();
// -> ... so it can later be spawned again
npc.spawn();
// despawns the npc and removes it from the repository
npc.remove();
Activities are what makes Kelp NPCs extensible for plugin developers. NPCs have their own asynchronous tick system for making them walk for example. This tick system is slightly slower than the server's default one to save performance but still look fluently.
You can hook into this tick system by creating activities. There are some default activities in Kelp, which are demonstrated below, but you can also create your own activities, which is shown in another article.
Okay, actually you can make the NPC look at any location with this activity, but most of you will use it for this case I think.
npc.addActivity(
LookToActivity.create()
.target(() -> player.getLocation()) // provide any location here
);
This will make the NPC always look at the player's location. Note that the location is passed via a Supplier<T>
, so it is dynamic and will update with every NPC tick.
Lets the NPC sneak/unsneak when the given player (un-)sneaks as well.
npc.addActivity(
SneakingActivity.create()
// player to imitate the behavior of
// this does not essentially have to be the owner of the NPC
.imitatedPlayer(player)
);
As explained in the chapter about despawning/removing, when NPCs are out of range of their clients, they will be despawned automatically, which cannot be tracked by the server. When the player comes back to the location, the NPC won't respawn by default. But there is an activity preventing this behavior:
npc.addActivity(
AutoSpawnActivity.create()
.distanceThreshold(40) // optional (default: 40)
);
When the NPC is more than 40 blocks away from the player, it will be despawned, and as soon as this distance falls below the given value, it is respawned automatically again. It is recommended to apply this activity to all of your NPCs if you are not sure if the player might walk too far away.
NPC movement is still experimental and might cause unexpected behavior. NPCs might not respect the laws of physics and run into the ground or into the air.
npc.addActivity(WalkToTargetActivity.create()
.target(targetLocation)
);
npc.addActivity(WalkToDirectionActivity.create()
.direction(player.getLocation().getDirection())
.distance(10) // in blocks
);
The NPC goes to the given target location or the given direction in a linear path.
You can play some animations on an NPC to indicate certain behavior such as hitting a player.
npc.playAnimation(NpcAnimation.MAIN_HAND_SWING);
npc.playAnimation(NpcAnimation.TAKE_DAMAGE);
A detailed overview of all animations can befound in the JavaDocs of the NpcAnimation
class.
(c) 2019-2021 pxav.
Kelp is an open-source project maintained by multiple developers. If you have additions/questions/problems to report about the wiki, feel free to create an issue here on GitHub or join the Discord
- SQL Module coming soon
- Documentation in progress