Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ if (std::optional<falcon::Item> held = inventory.item(inventory.selectedSlot()))
```

Java plugins reach the same store through `plugin.data()`. `Item::nbt` and `Item::setNbt` read and replace an
item's whole tag as little-endian NBT, the game's own format, and `Entity::nbt` returns what the server would save
for an entity.
item's whole tag as little-endian NBT, the game's own format. `Entity::nbt` returns what the server would save
for an entity, and `Entity::setNbt` loads a mob or any other non-player entity back from such data.

### Typed commands, running commands and recipes

Expand Down Expand Up @@ -249,7 +249,7 @@ falcon::Server::setDifficulty(falcon::Difficulty::Hard);
### Entities and players

Mobs expose their equipment, riding, tags and attack target; players their spawn point, language, platform, skin,
camera and item cooldowns:
camera and item cooldowns. `Player::setSkin` gives a player a classic skin of at most 128 by 128 pixels:

```cpp
zombie.setEquipment(falcon::EquipmentSlot::Head, *falcon::Item::create("minecraft:diamond_helmet"));
Expand Down
12 changes: 12 additions & 0 deletions java/src/main/java/falcon/api/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,18 @@ public byte[] nbt() {
}
}

/**
* Loads the entity from little-endian NBT, as the server does from a save, and shows the result to the players
* who see it. Returns false for data that is not a compound, and for players.
*/
public boolean setNbt(byte[] data) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment bytes = data.length == 0 ? MemorySegment.NULL : arena.allocateFrom(ValueLayout.JAVA_BYTE,
data);
return api().entitySetNbt(mHandle, bytes, data.length) != 0;
}
}

/**
* What a mob wears or holds; empty for an empty slot or an entity without equipment, players included.
*/
Expand Down
14 changes: 14 additions & 0 deletions java/src/main/java/falcon/api/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,20 @@ public SkinImage skinImage() {
}
}

/**
* Gives the player a classic skin and shows it to everyone. The image is RGBA, four bytes a pixel, at most 128
* by 128. Returns false when the image does not have the size it announces.
*/
public boolean setSkin(String skinId, SkinImage image) {
try (Arena arena = Arena.ofConfined()) {
byte[] rgba = image.rgba();
MemorySegment bytes = rgba.length == 0 ? MemorySegment.NULL : arena.allocateFrom(ValueLayout.JAVA_BYTE,
rgba);
return api().playerSetSkin(mHandle, Interop.text(arena, skinId), bytes, rgba.length, image.width(),
image.height()) != 0;
}
}

/**
* Switches the camera to a preset such as minecraft:free, easing over easeTime seconds when above 0.
*/
Expand Down
8 changes: 8 additions & 0 deletions native/include/falcon/Entity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ namespace falcon {
return std::vector<uint8_t>(data, data + length);
}

/**
* Loads the entity from little-endian NBT, as the server does from a save, and shows the result to the
* players who see it. Returns false for data that is not a compound, and for players.
*/
bool setNbt(const std::vector<uint8_t> &data) const {
return detail::api().entitySetNbt(mHandle, data.data(), static_cast<uint32_t>(data.size())) != 0;
}

/** What a mob wears or holds; nothing for an empty slot or an entity without equipment, players included. */
std::optional<Item> equipment(EquipmentSlot slot) const {
const FalconEquipmentSlot raw = static_cast<FalconEquipmentSlot>(slot);
Expand Down
10 changes: 10 additions & 0 deletions native/include/falcon/Player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,16 @@ namespace falcon {
return image;
}

/**
* Gives the player a classic skin and shows it to everyone. The image is RGBA, four bytes a pixel, at most
* 128 by 128. Returns false when the image does not have the size it announces.
*/
bool setSkin(const std::string &skinId, const SkinImage &image) const {
return detail::api().playerSetSkin(mHandle, skinId.c_str(), image.rgba.data(),
static_cast<uint32_t>(image.rgba.size()), image.width,
image.height) != 0;
}

/** Switches the camera to a preset such as minecraft:free, easing over easeTime seconds when above 0. */
bool setCamera(const std::string &preset, float easeTime = 0.0f, CameraEase ease = CameraEase::Linear) const {
return detail::api().playerSetCamera(mHandle, preset.c_str(), nullptr, nullptr, easeTime,
Expand Down
4 changes: 4 additions & 0 deletions native/include/falcon/falcon_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,10 @@ typedef struct FalconServerApi {
int (*serverPluginEnabled)(const char *name);

int (*eventEffect)(FalconEvent *event, FalconEffect *out);

int (*entitySetNbt)(FalconEntity *entity, const uint8_t *data, uint32_t length);
int (*playerSetSkin)(FalconPlayer *player, const char *skinId, const uint8_t *rgba, uint32_t length,
uint32_t width, uint32_t height);
} FalconServerApi;

typedef int (*FalconPluginEntry)(const FalconServerApi *api, FalconPlugin *plugin, FalconPluginCallbacks *callbacks);
Expand Down
4 changes: 4 additions & 0 deletions wasm/falcon.wit
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ interface players {
player-device-os: func(player: player) -> s32;
player-skin-id: func(player: player) -> string;
player-skin-image: func(player: player) -> skin-image;
/// A classic skin, at most 128 by 128, shown to everyone.
player-set-skin: func(player: player, skin-id: string, image: skin-image) -> bool;
/// `ease`: one of the `FALCON_CAMERA_EASE_*` values, used when `ease-time` is above 0.
player-set-camera: func(player: player, preset: string, position: option<vec3>, rotation: option<vec3>,
ease-time: f32, ease: u32) -> bool;
Expand Down Expand Up @@ -270,6 +272,8 @@ interface entities {
entity-data-key: func(entity: entity, index: u32) -> option<string>;
/// What the server would save for the entity, as little-endian NBT.
entity-nbt: func(entity: entity) -> list<u8>;
/// Loads the entity from little-endian NBT and shows the result to the players who see it; not for players.
entity-set-nbt: func(entity: entity, data: list<u8>) -> bool;
/// `slot`: one of the `FALCON_EQUIPMENT_*` values; mobs only, `none` for an empty slot.
entity-equipment: func(entity: entity, slot: u32) -> option<item>;
entity-set-equipment: func(entity: entity, slot: u32, item: option<borrow<item>>) -> bool;
Expand Down
Loading