Fake-player NPCs · building a re-skinnable, clickable fake player on the server¶
playerNpc* creates a pure-protocol fake player: the client sees a complete player model with a nametag, and it shows up in the Tab list, but the server has no such player — no AI, no place in the entity system, nothing written to the save. It suits characters that simply stand there: shopkeepers, quest givers, statues, human-shaped standees.
A skin gets in four ways: registering a PNG file, capturing it from an online player, importing a directory in bulk, or restoring a base64 blob. A skin must be registered before you create an NPC, otherwise creation fails. The library writes nothing to disk: after a restart you register the skins again yourself and rebuild the NPCs from your own save data.
Clicking this fake player does not go into a callback; fetch it by polling ghost* (with domain=npc in the entry).
Quick start¶
const registerSkin = ll.import("HologramLib", "playerNpcRegisterSkin");
const create = ll.import("HologramLib", "playerNpcCreate");
const setNametag = ll.import("HologramLib", "playerNpcSetNametag");
// 1. Register a PNG skin (pick the skinId yourself; every later call refers to it)
registerSkin("E:/server/skins/girl.png", "girl", "", "slim");
// 2. Create a fake player in the Overworld wearing that skin
const npc = create(100.5, 64, 100.5, 0, "§eMia", "girl");
// 3. Change the nametag (supports § colour codes)
setNametag(npc, "§eMia §7<Shopkeeper>");
npc is this fake player's id, and every later playerNpc* function uses it.
The skin registry¶
Register a PNG texture as a skin¶
playerNpcRegisterSkin(pngPath, skinId, geometry, armSize)
- Parameters:
pngPath:String
the PNG file path (an absolute path is recommended). The image must be square, with a side length that is a power of two and no smaller than 64 (64 / 128 / 256 / 512 / 1024)skinId:String
the name you give this skin; an empty string = use the file name (without the extension)geometry:String
the geometry model name; an empty string = use the standard player modelgeometry.humanoid.customarmSize:String
arm thickness,"wide"(thick, the default) or"slim"(thin)- Return value:
true= registered; returnsfalsewhen the file cannot be opened or the size is not allowed. Registering the sameskinIdagain overwrites the old skin - Return type:
Boolean -
Example:
Capture an online player's skin¶
playerNpcCaptureSkin(skinId, playerName)
- Parameters:
skinId:String
the registration name; it cannot be emptyplayerName:String
the online player's name (realName)- Return value: what is captured is a complete snapshot of that moment, so a later re-skin by that player does not affect it; returns
falsewhenskinIdis empty or the player is not online. Capturing under the sameskinIdagain overwrites - Return type:
Boolean -
Example:
Import the skins in a directory in bulk¶
playerNpcImportSkins(dirPath)
- Parameters:
dirPath:String
the directory path. The name of each subfolder is theskinId, and the folder must hold one.pngtexture (required) and one.jsongeometry model (optional; the standard player model is used when it is missing)- Return value: how many were imported successfully; returns
-1whendirPathis not a directory; a subfolder with no PNG in it is skipped and does not count - Return type:
Integer -
Example:
Check whether a skin is registered¶
playerNpcHasSkin(skinId)
- Parameters:
skinId:String
the skin name- Return value:
true= registered - Return type:
Boolean -
Example:
List every registered skin name¶
playerNpcGetSkinIds()
- Return value: every
skinId; an empty array when there are none - Return type:
Array<String> -
Example:
Unregister a registered skin¶
playerNpcUnregisterSkin(skinId)
- Parameters:
skinId:String
the skin name to unregister- Return value:
true= unregistered; it is refused while an NPC still uses this skin (it returnsfalse; destroy those NPCs or re-skin them first), and a skin that never existed returnsfalseas well - Return type:
Boolean -
Example:
Export a skin as a base64 string¶
playerNpcGetSkinBlobB64(skinId)
- Parameters:
skinId:String
the skin name- Return value: the base64 text of this skin's complete snapshot (you can write it straight into a file or a database); returns the empty string
""when the skin is not registered - Return type:
String -
Example:
Restore a skin from a base64 string¶
playerNpcRegisterSkinFromBlobB64(b64)
- Parameters:
b64:String
the string exported byplayerNpcGetSkinBlobB64- Return value:
true= registered (the skin keeps the name it had when it was exported); returnsfalsewhen the string cannot be decoded, is empty or is malformed - Return type:
Boolean -
Example:
Send a skin to online players to re-skin them¶
playerNpcInjectSkin(viewerName, targetName, skinId)
- Parameters:
viewerName:String
who gets to see it; an empty string = every online player, the re-skinned player included (this is the proper route for "let players see themselves re-skinned")targetName:String
the name of the online player being re-skinned (realName)skinId:String
a registered skin name- Return value:
true= sent; returnsfalsewhen the target is offline, whenskinIdis not registered, or when the viewer named byviewerNameis offline. It does not resend automatically: if the client drops one now and then, call it once more after a moment - Return type:
Boolean -
Example:
Send a skin to every online player¶
playerNpcInjectSkinAll(targetName, skinId)
- Parameters:
targetName:String
the name of the online player being re-skinnedskinId:String
a registered skin name- Return value: equivalent to
playerNpcInjectSkinwith an emptyviewerName, and it fails for the same reasons - Return type:
Boolean -
Example:
Creation & destruction¶
Create a fake player at a coordinate¶
playerNpcCreate(x, y, z, dim, name, skinId)
- Parameters:
x:Float
world coordinate Xy:Float
world coordinate Yz:Float
world coordinate Zdim:Integer
the dimension id,0/1/2name:String
the display name, which is also the name above its head and in the Tab list; an empty string = use the default nameNPCskinId:String
a registered skin name; an empty string = use the defaultdefault(it fails when that one was never registered)- Return value: the fake-player id; returns a negative number on failure (
-3= the skin is not registered) - Return type:
Integer -
Example:
Create a fake player with a random id¶
playerNpcCreateRandom(x, y, z, dim, name, skinId)
- Parameters: the same as
playerNpcCreate. - Return value: the fake-player id from the random range (starting at
0x10000000), which never collides with an incrementing id; returns a negative number on failure - Return type:
Integer -
Example:
Create under a chosen id to restore an NPC¶
playerNpcCreateWithId(x, y, z, dim, name, skinId, desiredId)
- Parameters:
- The first 6 parameters: the same as
playerNpcCreate. desiredId:Integer
the id you want; it must be greater than0and must not be taken right now- Return value: on success it is exactly
desiredId;desiredId <= 0, or an id that is already taken, returns-2; an unregistered skin returns-3 - Return type:
Integer -
Example:
Delete one fake player¶
playerNpcDestroy(id)
- Parameters:
id:Integer
the fake-player id- Return value:
true= deleted (players who could already see it watch it disappear); returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Delete every fake player¶
playerNpcDestroyAll()
- Return value: no return value
- Return type:
Null -
Example:
Properties¶
Move the fake player and optionally switch dimension¶
playerNpcSetPos(id, x, y, z, dim)
- Parameters:
id:Integer
the fake-player idx:Float
the new world coordinate Xy:Float
the new world coordinate Yz:Float
the new world coordinate Zdim:Integer
the target dimension; a negative number = change the coordinates only and keep the current dimension- Return value:
true= changed; returnsfalsewhen the id does not exist. Changes merge into a single resend at the end of the tick, so it never flickers repeatedly - Return type:
Boolean -
Example:
Set the fake player's facing¶
playerNpcSetRotation(id, yaw)
- Parameters:
id:Integer
the fake-player idyaw:Float
the facing angle in degrees- Return value:
true= changed; returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Change the nametag above the fake player's head¶
playerNpcSetNametag(id, text)
- Parameters:
id:Integer
the fake-player idtext:String
the nametag text, supporting§colour codes; an empty string = clear the nametag. Setting the same text again does not trigger a resend- Return value:
true= changed; returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Give the fake player a different skin¶
playerNpcSetSkin(id, skinId)
- Parameters:
id:Integer
the fake-player idskinId:String
a registered skin name- Return value:
true= changed; returnsfalsewhen the skin is not registered or the id does not exist. A re-skin makes players who could already see it watch it rebuild once - Return type:
Boolean -
Example:
Scale the model and sync the hitbox¶
playerNpcSetScale(id, scale)
- Parameters:
id:Integer
the fake-player idscale:Float
the scaling factor; the client hard limit is0.0625 ~ 10- Return value:
true= set; returnsfalsewhen it is below0.0625or above10, and when the id does not exist. Changes merge into a single resend at the end of the tick - Return type:
Boolean -
Example:
Set the fake player's visible distance¶
playerNpcSetViewDistance(id, dist)
- Parameters:
id:Integer
the fake-player iddist:Float
the distance in blocks;<= 0= unlimited distance (the default is 96 blocks)- Return value:
true= set; returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Turn this fake player on or off¶
playerNpcSetEnabled(id, enabled)
- Parameters:
id:Integer
the fake-player idenabled:Booleanfalse= players cannot see it (the id stays valid, so you can turn it back on at any time),true= show it again- Return value:
true= set; returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Visible-player whitelist¶
Show it only to the players on the list¶
playerNpcSetVisiblePlayers(id, players)
- Parameters:
id:Integer
the fake-player idplayers:Array<String>
an array of player names, matched byrealName; an empty array = clear the restriction and go back to visible to everyone- Return value:
true= set; returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Put a single player on the whitelist¶
playerNpcSetVisiblePlayer(id, playerName)
- Parameters:
id:Integer
the fake-player idplayerName:String
the only player who can see it (realName)- Return value:
true= set; returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Clear the whitelist and go back to visible to everyone¶
playerNpcClearVisiblePlayers(id)
- Parameters:
id:Integer
the fake-player id- Return value:
true= cleared (it counts as success even when there was no restriction to begin with); returnsfalsewhen the id does not exist - Return type:
Boolean -
Example:
Queries¶
Check whether the fake player still exists¶
playerNpcExists(id)
- Parameters:
id:Integer
the fake-player id- Return value:
true= it exists - Return type:
Boolean -
Example:
Check whether an id is already taken¶
playerNpcIsIdUsed(id)
- Parameters:
id:Integer
the id to check- Return value:
true= already taken (in which caseplayerNpcCreateWithIdreturns-2) - Return type:
Boolean -
Example:
List the ids of all fake players¶
playerNpcGetAllIds()
- Return value: every fake-player id; an empty array when there are none
- Return type:
Array<Integer> -
Example:
Get a runtime summary for troubleshooting¶
playerNpcGetDebugInfo(id)
- Parameters:
id:Integer
the fake-player id- Return value: the summary text, containing the id, name, skin, coordinates, dimension, facing, view distance, on/off state, the number of players that have seen it and the state of the visible list; returns
not_foundwhen the id does not exist - Return type:
String -
Example:
Things to know¶
- Register the skin before building the character: when a
skinIdwas never registered, creation fails (-3). If a skin is unregistered or goes missing while the server runs, this NPC will simply never spawn, and there is no error on the script side. - The library writes nothing to disk: both skins and NPCs live in memory only. After a server or script restart you register the skins again (or save them with
playerNpcGetSkinBlobB64/playerNpcRegisterSkinFromBlobB64and restore them on restart), and rebuild the NPCs from your own saved records (playerNpcCreateWithIdcan restore the original id). - A fake player stays in the Tab list: the library does not send a PlayerList removal entry (removing it while the skin entry is still alive crashes the client), so once a player has seen this NPC, its name stays in the Tab list until the NPC is destroyed.
- At most 2 NPCs spawn per tick (spawning several at once makes the client receive a burst of data and disconnect), so visibility can be up to one tick late; entering and leaving the view distance carries 4 blocks of hysteresis, so a skin is not re-sent over and over on the boundary.
- Property changes merge: changing position / facing / scale / skin in the same tick rebuilds only once (no flicker). Every rebuild does resend the skin, though, so high-frequency tracking movement does not belong in a script tick — the LSE side has no lightweight position channel, so that kind of requirement belongs on the C++ side with
setPositionLight. - Clicking needs polling: a fake player's interaction does not go into a callback; fetch it with
ghostPollInteractions(), where the entry looks likeplayer=Steve action=2 domain=npc id=7(action=1is a right-click interact,action=2an attack). - Some capabilities have no script interface:
setRotationLight/setPositionLight/playAnimation/playAnimationTo/setEntitySpawnCallbackare C++-only for now, soll.importreturnsnullfor them in a script; check first withll.hasExported("HologramLib", "...")before deciding whether to use one. - The whitelist matches by name: it uses player names (LSE's
realName), not uuid; once a player renames, the old list stops matching.