Skip to content

Players

Section notes in abi.h

Appended

§B player management

§I NBT binary, KvDb (thread-safe), system & server info

Player: equipment, cooldown, network (dedicated fns)

Titles

PACT_SET_TITLE (player_action opcode 6) reaches the client by running the console command title "<name>" title <text>. Three things are wrong with that and none of them are theoretical:

Text Only
1
2
3
4
5
6
- the text is pasted into a command line unquoted, so a plot named
  `He said "hi"` truncates the command;
- `title`'s text parameter is a `message`, which expands selectors —
  a plot named `@e` is a command injection, not a name;
- `/title` has no way to set fade/stay for the same call, so timing is
  whatever the client last stored.

This slot builds a real SetTitlePacket instead. No wire format crosses the FFI (the packet is constructed field-by-field on this side), so it survives protocol bumps the way spawn_particle_for does.

type is SetTitlePacketPayload::TitleType:

Text Only
0 Clear · 1 Reset · 2 Title · 3 Subtitle · 4 Actionbar · 5 Times

The TextObject variants (6..8) need a ResolvedTextObject and are refused. text is ignored for Clear/Reset/Times.

Durations are in TICKS. For 2/3/4, when all three are >= 0 a Times packet is sent first so the timing is deterministic rather than inherited from whatever the client last stored; pass -1 for all three to keep the client's current timing. Mixing (-1 with >=0) is refused rather than guessed at — a half-specified duration set has no sane meaning. Server thread only.

Same-toolchain fast lane, appended and struct_size-gated.

Five slots appended without touching PIER_ABI_VERSION: a pure append is not a version change, and struct_size is the precise gate.

Both directions hold. A new loader running an old mod: the old table is a byte-identical prefix of the new one, the mod cannot reach these five slots, and it works unchanged. A new mod on an old loader: SDK runtime init compares struct_size, finds the loader's table shorter than the one it was compiled against, and refuses to load. That is the right outcome, since a mod that reads the lane_publish cell on a loader without it would read out of bounds.

In short: the version number tracks "semantics changed", struct_size tracks "the table grew". This change is only the latter.

See the long comment at PierLaneDesc above. In one line: service is the cross-language (name, JSON) -> JSON channel, while this is a direct function-table call that holds only when both sides were built by the same toolchain; a fingerprint mismatch yields no pointer and the consumer falls back to service.

Server thread only.

Slots

get_player_position

C
PierPlayerPos (*get_player_position)(PierStr name);

Look up a connected player's feet position and dimension by name. Used to pick selection corners from where the player is standing. Server thread only.

  • Call: api->get_player_position(name)
  • Parameters:
    • name : PierStr
  • Return type: PierPlayerPos
  • Section of abi.h: Appended
  • Position in the table: slot 14, counting from 0
  • Callers in each binding:

list_players

C
void (*list_players)(void* ctx, PierStrSink snbt_sink);

One SNBT per online player: {name,xuid,uuid,dim,x,y,z}.

  • Call: api->list_players(ctx, snbt_sink)
  • Parameters:
    • ctx : void*
    • snbt_sink : PierStrSink
  • Section of abi.h: §B player management
  • Position in the table: slot 21, counting from 0
  • Callers in each binding:

player_resolve

C
bool (*player_resolve)(PierPlayerSel sel, PierActorId* out);

Resolve a player selector to their ActorUniqueID (bridges into the actor_* API).

player_send_message

C
bool (*player_send_message)(PierPlayerSel sel, PierStr msg);

player_disconnect

C
bool (*player_disconnect)(PierPlayerSel sel, PierStr reason);
  • Call: api->player_disconnect(sel, reason)
  • Parameters:
    • sel : PierPlayerSel
    • reason : PierStr
  • Return type: bool
  • Section of abi.h: §B player management
  • Position in the table: slot 24, counting from 0
  • Callers in each binding:

broadcast_message

C
void (*broadcast_message)(PierStr msg);

sendMessage to every online player.

  • Call: api->broadcast_message(msg)
  • Parameters:
    • msg : PierStr
  • Section of abi.h: §B player management
  • Position in the table: slot 25, counting from 0
  • Callers in each binding:

player_set_gamemode

C
bool (*player_set_gamemode)(PierPlayerSel sel, int32_t mode);

0=survival 1=creative 2=adventure 6=spectator, native (Player::setPlayerGameType).

player_teleport

C
bool (*player_teleport)(PierPlayerSel sel, int32_t dim, double x, double y, double z);

Teleport natively (Actor::teleport). Custom dimensions (id >= 3) are allowed; the dimension bridge must produce an engine instance whose id matches, or the call fails instead of sending the player into a mismatched dimension.

  • Call: api->player_teleport(sel, dim, x, y, z)
  • Parameters:
    • sel : PierPlayerSel
    • dim : int32_t
    • x : double
    • y : double
    • z : double
  • Return type: bool
  • Section of abi.h: §B player management
  • Position in the table: slot 27, counting from 0
  • Callers in each binding:

player_get_num

C
bool (*player_get_num)(PierPlayerSel sel, int32_t prop, double* out);

The four *_get_num slots share one convention. The return is whether the host has an answer, and *out is the answer; a false leaves *out untouched.

False and "the value is zero" are different results and a caller must not collapse them (contract §5.2). False has two causes it does not distinguish: the subject was not found, and the property is one this engine version does not expose. Neither is an error the host logs, because a caller polling a property it cannot read would fill the log with it; the list of properties that always answer false on a given version is in CHANGELOG.md under the release.

player_get_str

C
bool (*player_get_str)(PierPlayerSel sel, int32_t prop, void* ctx, PierStrSink sink);

player_set_num

C
bool (*player_set_num)(PierPlayerSel sel, int32_t prop, double v);

player_action

C
bool (*player_action)(
    PierPlayerSel sel,
    int32_t action,
    PierStr sarg,
    double a,
    double b,
    double c,
    void* ctx,
    PierStrSink out
);

player_send_message_typed

C
bool (*player_send_message_typed)(PierPlayerSel sel, PierStr msg, int32_t type);

Group note

Send a message of a specific TextPacketType to one player (additive, gated by struct_size). type is a TextPacketType value:

Text Only
1
2
3
0 Raw · 1 Chat · 2 Translate · 3 Popup · 4 JukeboxPopup · 5 Tip ·
6 SystemMessage · 7 Whisper · 8 Announcement · 9 TextObjectWhisper ·
10 TextObject · 11 TextObjectAnnouncement.

Out-of-range falls back to Raw. Single-string body (like LSE tell): the author/param kinds (Chat/Whisper/Translate) arrive as plain text. plain player_send_message remains the Raw/Chat convenience path.

  • Call: api->player_send_message_typed(sel, msg, type)
  • Parameters:
    • sel : PierPlayerSel
    • msg : PierStr
    • type : int32_t
  • Return type: bool
  • Section of abi.h: §I NBT binary, KvDb (thread-safe), system & server info
  • Position in the table: slot 91, counting from 0
  • Callers in each binding:

player_get_carried_item

C
bool (*player_get_carried_item)(PierPlayerSel sel, void* ctx, PierStrSink sink);
  • Call: api->player_get_carried_item(sel, ctx, sink)
  • Parameters:
    • sel : PierPlayerSel
    • ctx : void*
    • sink : PierStrSink
  • Return type: bool
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 102, counting from 0
  • Callers in each binding:

player_get_item

C
bool (*player_get_item)(PierPlayerSel sel, int32_t slot, void* ctx, PierStrSink sink);
  • Call: api->player_get_item(sel, slot, ctx, sink)
  • Parameters:
    • sel : PierPlayerSel
    • slot : int32_t
    • ctx : void*
    • sink : PierStrSink
  • Return type: bool
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 103, counting from 0
  • Callers in each binding:

player_set_item

C
bool (*player_set_item)(PierPlayerSel sel, int32_t slot, PierStr item_snbt);
  • Call: api->player_set_item(sel, slot, item_snbt)
  • Parameters:
    • sel : PierPlayerSel
    • slot : int32_t
    • item_snbt : PierStr
  • Return type: bool
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 104, counting from 0
  • Callers in each binding:

player_get_equipment

C
bool (*player_get_equipment)(PierPlayerSel sel, void* ctx, PierStrSink sink);

All equipment as SNBT: [{slot, item_snbt},…] slot: 0=mainhand 1=offhand 2-5=armor

  • Call: api->player_get_equipment(sel, ctx, sink)
  • Parameters:
    • sel : PierPlayerSel
    • ctx : void*
    • sink : PierStrSink
  • Return type: bool
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 105, counting from 0
  • Callers in each binding:

player_get_cooldown

C
int32_t (*player_get_cooldown)(PierPlayerSel sel, PierStr item_name);

Ticks remaining for an item cooldown (-1 if not on cooldown / player offline).

  • Call: api->player_get_cooldown(sel, item_name)
  • Parameters:
    • sel : PierPlayerSel
    • item_name : PierStr
  • Return type: int32_t
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 106, counting from 0
  • Callers in each binding:

player_start_cooldown

C
bool (*player_start_cooldown)(PierPlayerSel sel, PierStr item_name, int32_t ticks);
  • Call: api->player_start_cooldown(sel, item_name, ticks)
  • Parameters:
    • sel : PierPlayerSel
    • item_name : PierStr
    • ticks : int32_t
  • Return type: bool
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 107, counting from 0
  • Callers in each binding:

player_get_network_status

C
bool (*player_get_network_status)(PierPlayerSel sel, void* ctx, PierStrSink sink);
  • Call: api->player_get_network_status(sel, ctx, sink)
  • Parameters:
    • sel : PierPlayerSel
    • ctx : void*
    • sink : PierStrSink
  • Return type: bool
  • Section of abi.h: Player: equipment, cooldown, network (dedicated fns)
  • Position in the table: slot 108, counting from 0
  • Callers in each binding:

player_send_title

C
1
2
3
bool (*player_send_title)(
    PierPlayerSel sel, int32_t type, PierStr text, int32_t fade_in_ticks,
    int32_t stay_ticks, int32_t fade_out_ticks);

player_conn_id

C
uint64_t (*player_conn_id)(PierPlayerSel who);

This player's connection id — the same number packet interceptors see in the packet context.

A packet callback has only conn_id, not a player, so per-player rewriting of outbound packets is impossible without this: locking the sky color needs the dimension of the person on that connection, which needs to know who they are.

The alternative is to periodically send packets that override the server's, and that is wrong: the server sends real time while the mod sends locked time, the two kinds interleave, and the client's sky flickers between them. Rewriting is correct, and rewriting needs this slot.

@return the connection id; 0 if the player is offline or their network

Text Only
identifier is unavailable.

Pure append: PIER_ABI_VERSION is unchanged, struct_size is the gate.

  • Call: api->player_conn_id(who)
  • Parameters:
    • who : PierPlayerSel
  • Return type: uint64_t
  • Section of abi.h: Same-toolchain fast lane, appended and struct_size-gated.
  • Position in the table: slot 180, counting from 0
  • Callers in each binding:

PierPlayerNumProp

player_get_num / player_set_num keys. (G)=get-only, (S)=settable.

Name Value Description
PIER_PPROP_GAME_TYPE 0 (G) Player::getPlayerGameType; write via player_set_gamemode
PIER_PPROP_LEVEL 1 (S) attribute Player::LEVEL()
PIER_PPROP_EXPERIENCE 2 (S) attribute Player::EXPERIENCE() (progress 0..1)
PIER_PPROP_HUNGER 3 (S) attribute Player::HUNGER()
PIER_PPROP_SATURATION 4 (S) attribute Player::SATURATION()
PIER_PPROP_EXHAUSTION 5 (S) attribute Player::EXHAUSTION()
PIER_PPROP_XP_NEEDED_NEXT_LEVEL 6 (G) Player::getXpNeededForNextLevel
PIER_PPROP_LUCK 7 (G) Player::getLuck
PIER_PPROP_SELECTED_SLOT 8 (G) Player::getSelectedItemSlot; set via PIER_PACT_SET_SELECTED_SLOT
PIER_PPROP_IS_OPERATOR 9 (G) Player::isOperator
PIER_PPROP_CAN_USE_OPERATOR_BLOCKS 10 (G) Player::canUseOperatorBlocks
PIER_PPROP_IS_FLYING 11 (G) Player::isFlying
PIER_PPROP_CAN_JUMP 12 (G) Player::canJump
PIER_PPROP_IS_EMOTING 13 (G) Player::isEmoting
PIER_PPROP_IS_IN_RAID 14 (G) Player::isInRaid
PIER_PPROP_IS_HURT 15 (G) Player::isHurt
PIER_PPROP_IS_SCOPING 16 (G) Player::isScoping
PIER_PPROP_CAN_SLEEP 17 (G) Player::canSleep
PIER_PPROP_HAS_RESPAWN_POSITION 18 (G) Player::hasRespawnPosition
PIER_PPROP_CLIENT_SUB_ID 19 (G) Player::getClientSubId
PIER_PPROP_CAN_USE_ABILITY 20 (G) Player::canUseAbility; the ability index is passed through the player_action GET path, see PIER_PACT_CAN_USE_ABILITY
PIER_PPROP_DIRECTION 21 (G) Player::getDirection (0=S,1=W,2=N,3=E)
PIER_PPROP_CHUNK_RADIUS 22 (G) Player::getChunkRadius
PIER_PPROP_NETWORK_RTT 23 (G) getNetworkStatus().mPing (ms)
PIER_PPROP_PLATFORM 24 (G) Player::getPlatform
PIER_PPROP_ENCHANTMENT_SEED 25 (G) Player::getEnchantmentSeed
PIER_PPROP_IS_USING_ITEM 26 (G) Player::isUsingItem
PIER_PPROP_IS_BLOCKING 27 (G) Player::isBlocking
PIER_PPROP_IS_GLIDING 28 (G) Player::isGliding
PIER_PPROP_IS_SWIMMING 29 (G) Player::isSwimming
PIER_PPROP_PERMISSION_LEVEL 30 (G) Player::getPlayerPermissionLevel
PIER_PPROP_SCORE 31 (G) Player::getScore
PIER_PPROP_FALL_DISTANCE 32 (G) Actor::getFallDistance
PIER_PPROP_IS_DEAD 33 (G) Actor::isDead
PIER_PPROP_HAS_DIED_BEFORE 34 (G) Player::hasDiedBefore
PIER_PPROP_DIMENSION 35 (G) Actor::getDimensionId

PierPlayerStrProp

player_get_str keys.

Name Value Description
PIER_PSTR_REAL_NAME 0 Player::getRealName
PIER_PSTR_UUID 1 Player::getUuid().asString()
PIER_PSTR_XUID 2 Player::getXuid
PIER_PSTR_IP_AND_PORT 3 Player::getIPAndPort
PIER_PSTR_LOCALE_CODE 4 Player::getLocaleCode
PIER_PSTR_NAME_TAG 5 Actor::getNameTag (display name)
PIER_PSTR_LAST_DEATH_POS 6 SNBT {x,y,z} or "" if none
PIER_PSTR_LAST_DEATH_DIMENSION 7 dimension id as string
PIER_PSTR_NETWORK_STATUS 8 SNBT {ping,avg_ping,packet_loss,max_ping}
PIER_PSTR_PLATFORM_ONLINE_ID 9 Player::getPlatformOnlineId
PIER_PSTR_RESPAWN_POS 10 SNBT {x,y,z,dim}: where this player would respawn. Never empty; a player with no bed reports the world spawn, and the two are not distinguishable.

PierPlayerAction

player_action verbs. Args are (sarg, a, b, c); unused args are ignored. out (when non-NULL) receives a result string where noted.

Name Value Description
PIER_PACT_SET_ABILITY 0 a=AbilitiesIndex, b=0/1 (bool slots) or float (FlySpeed etc.). Restores PlayerPermissionLevel to its pre-write value afterwards: the engine's LayeredAbilities::setAbility is the "switch to custom permissions" path and pushes the player to Custom, and that level ships to the client inside UpdateAbilitiesPacket together with the ability layer. To change the level, use PIER_PACT_SET_PERMISSION_LEVEL. Refused (false) until the player has finished joining (Player::isPlayerInitialized): an ability written while the client is still loading desynchronizes until the player rejoins. Simulated players are exempt.
PIER_PACT_CAN_USE_ABILITY 1 a=AbilitiesIndex → out "0"/"1" Player::canUseAbility
PIER_PACT_SET_SELECTED_SLOT 2 a=slot Player::setSelectedSlot
PIER_PACT_GIVE_ITEM 3 sarg=item SNBT ItemStack::fromTag + Player::addAndRefresh
PIER_PACT_SET_SPAWN_POINT 4 a,b,c=pos, sarg=dim id (any registered dim); native Player::setRespawnPosition
PIER_PACT_CLEAR_TITLE 5 native SetTitlePacket(Clear)
PIER_PACT_SET_TITLE 6 sarg=text, a=slot(0 title,1 subtitle,2 actionbar); native SetTitlePacket, text sent verbatim
PIER_PACT_ADD_EXPERIENCE 7 a=xp Player::addExperience
PIER_PACT_ADD_LEVELS 8 a=levels Player::addLevels
PIER_PACT_START_COOLDOWN 9 sarg=item name, a=ticks Player::startItemCooldown
PIER_PACT_START_RIDING 10 a=vehicle ActorUniqueID (lower 64b) Player::startRiding
PIER_PACT_STOP_RIDING 11 Player::stopRiding
PIER_PACT_ATTACK 12 a=target ActorUniqueID (lower 64b) Player::attack
PIER_PACT_DROP 13 sarg=item SNBT, a=random(0/1) Player::drop
PIER_PACT_INTERACT 14 a=target ActorUniqueID Player::interact
PIER_PACT_START_USING_ITEM 15 sarg=item SNBT, a=duration Player::startUsingItem
PIER_PACT_STOP_USING_ITEM 16 Player::stopUsingItem
PIER_PACT_SET_CHUNK_RADIUS 17 a=radius Player::setChunkRadius
PIER_PACT_SET_ENCHANTMENT_SEED 18 a=seed Player::setEnchantmentSeed
PIER_PACT_REGISTER_TRACKED_BOSS 19 a=boss ActorUniqueID Player::registerTrackedBoss
PIER_PACT_UNREGISTER_TRACKED_BOSS 20 a=boss ActorUniqueID Player::unRegisterTrackedBoss
PIER_PACT_PLAY_EMOTE 21 sarg=piece id Player::playEmote
PIER_PACT_RESEND_ALL_CHUNKS 22 Player::resendAllChunks
PIER_PACT_OPEN_INVENTORY 23 Player::openInventory
PIER_PACT_SIDEBAR_SET 24 sarg="obj\ntitle\nline…" per-player sidebar
PIER_PACT_SIDEBAR_CLEAR 25 sarg=objective RemoveObjectivePacket
PIER_PACT_SET_PERMISSION_LEVEL 26 a=PlayerPermissionLevel (0 Visitor, 1 Member, 2 Operator, 3 Custom). LayeredAbilities::setPlayerPermissions plus UpdateAbilitiesPacket. The read side is PIER_PPROP_PERMISSION_LEVEL. Refused until the player has finished joining, like PIER_PACT_SET_ABILITY.