From 8316610b26a21c770e6c59d07282c28ecac7b18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:24:58 +0800 Subject: [PATCH 01/17] refactor: fix and enhance BinaryStream and Packet --- docs/apis/GameAPI/Packet.md | 213 +++++++++++++++------ docs/apis/GameAPI/Packet.zh.md | 221 +++++++++++++++------ src/legacy/api/PacketAPI.cpp | 340 ++++++++++++++++++--------------- src/legacy/api/PacketAPI.h | 13 +- src/legacy/api/PlayerAPI.cpp | 2 +- src/lse/api/NetworkPacket.h | 13 +- 6 files changed, 529 insertions(+), 273 deletions(-) diff --git a/docs/apis/GameAPI/Packet.md b/docs/apis/GameAPI/Packet.md index fb9ab5a0..8ffbc4ec 100644 --- a/docs/apis/GameAPI/Packet.md +++ b/docs/apis/GameAPI/Packet.md @@ -1,18 +1,16 @@ -# 🎓 Packet API +# 🎓 Packet API -The following objects and APIs provide the basic BDS packet interface for scripts. +The following objects and APIs provide the basic BDS packet interface for scripts. -(Please refer to Nukkit, PokcetMine, BDS Reverse to know the packet structure) If the client crashes, it is a packet structure error, not a bug. +(Please refer to Nukkit, PokcetMine, BDS Reverse to know the packet structure) If the client crashes, it is a packet structure error, not a bug. The documentation does not list the packet ID and its structure, please check it yourself. - ## 目录 + - 🔉 [Packet Object API](#-packet-object-api) - 🔌 [Binary stream object API](#-binary-stream-object-api) - - ## 🔉 Packet Object API In LLSE, 「Packet Object」 is used to get information about packets. @@ -21,10 +19,8 @@ In LLSE, 「Packet Object」 is used to get information about packets. #### Get from API -Call some **return packet object** function to get to the packet object given by BDS -See [Binary Stream Objects](#-binary-stream-object-api) for details - - +Call some **return packet object** function to get to the packet object given by BDS +See [Binary Stream Objects](#-binary-stream-object-api) for details ### Packet Objects - Functions @@ -35,9 +31,7 @@ Every packet object contains some member functions (member methods) that can be `pkt.getName()` - Return value:packet name -- Return value type: `String` - - +- Return value type: `String` #### Get packet ID @@ -46,15 +40,55 @@ Every packet object contains some member functions (member methods) that can be - Return value:packet id - Return value type: `Integer` +#### Send packet to specified target + +!!! warning +This function is only available in 0.19.0 and later. + +`pkt.sendTo(pos)` +`pkt.sendTo(x,y,z,dimid)` +`pkt.sendTo(target)` + +- Parameters: + + - pos : `IntPos` / `FloatPos` + The coordinates of the packet send target (or use x, y, z, dimid to determine the target position) + - target : `Player` / `Entity` + Packet send target + +- Return value:success or not +- Return value type: `Boolean` + +If `target` is `Player`, the packet will be sent to the specified player. +If `target` is `Entity`, the packet will be sent to players around the specified entity. +#### Send packet to all clients + +!!! warning +This function is only available in 0.19.0 and later. + +`pkt.sendToClients()` + +- Return value:success or not +- Return value type: `Boolean` + +#### Send packet to server + +!!! warning +This function is only available in 0.19.0 and later. + +`pkt.sendToServer()` + +- Return value:success or not +- Return value type: `Boolean` ## 🔌 Binary Stream Object API ### Create a binary stream object -[JavaScript] ```new BinaryStream()``` +[JavaScript] `new BinaryStream()` -[Lua] ```BinaryStream()``` +[Lua] `BinaryStream()` - Return value: Binary stream object - Return value type: `BinaryStream` @@ -70,72 +104,141 @@ Every binary stream object contains some member functions (member methods) that - Return value: success or not - Return value type: `Boolean` +#### Get binary stream read pointer + +!!! warning +This function is only available in 0.19.0 and later. + +`bs.getReadPointer()` + +- Return value: current read pointer +- Return value type: `Integer` + +#### Set binary stream read pointer +!!! warning +This function is only available in 0.19.0 and later. + +`bs.setReadPointer(pos)` + +- Parameters: + + - pos : `Integer` + New read pointer + +- Return value: success or not +- Return value type: `Boolean` + +#### Get binary stream data + +!!! warning +The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.0 and later. + +`bs.getData([clear])` + +- Parameters: + + - clear : `Boolean` (Optional parameter) + Whether to clear stream data after reading. The default value is `true` + +- Return value: binary stream data +- Return value type: `ByteBuffer` + +Before 0.19.0, this function can only be used as `bs.getData()`. +At that time, it always cleared the stream data after reading, and the return value type was `String`. + +Because the old return value was `String`, in JavaScript it may be forcibly encoded as UTF-8, causing binary data corruption and incorrect data content. + +#### Set binary stream data + +!!! warning +This function is only available in 0.19.0 and later. + +`bs.setData(data)` + +- Parameters: + + - data : `ByteBuffer` + Binary stream data + +- Return value: success or not +- Return value type: `Boolean` #### Write to binary stream -`bs.writexxxx(value)` +`bs.writexxxx(value)` - Parameters: + - value : `NULL` Refer to the table below + For some numeric write functions, `String` is also accepted - Return value:success or not - Return value type: `Boolean` -| Available functions | Parameter Type | -| ------------------------------ | -------------- | -| writeBool | `Boolean` | -| writeByte | `Integer` | -| writeDouble | `Number` | -| writeFloat | `Float` | -| writeSignedBigEndianInt | `Number` | -| writeSignedInt | `Number` | -| writeSignedInt64 | `Number` | -| writeSignedShort | `Integer` | -| writeString | `String` | -| writeUnsignedChar | `Integer` | -| writeUnsignedInt | `Number` | -| writeUnsignedInt64 | `Number` | -| writeUnsignedShort | `Integer` | -| writeUnsignedVarInt | `Number` | -| writeUnsignedVarInt64 | `Number` | -| writeVarInt | `Number` | -| writeVarInt64 | `Number` | -| writeVec3 | `FloatPos` | -| writeBlockPos (Added in 0.9.5) | `BlockPos` | -| writeCompoundTag | `NbtCompound` | -| writeItem (Added in 0.9.5) | `Item` | - - +| Available functions | Parameter Type | +| -------------------------------------- | -------------------- | +| writeBool | `Boolean` | +| writeByte | `Integer` | +| writeBytes (Added in 0.19.0) | `ByteBuffer` | +| writeDouble | `Number` / `String` | +| writeFloat | `Float` / `String` | +| writeNormalizedFloat (Added in 0.19.0) | `Float` / `String` | +| writeSignedBigEndianInt | `Number` / `String` | +| writeSignedInt | `Number` / `String` | +| writeSignedInt64 | `Number` / `String` | +| writeSignedShort | `Integer` / `String` | +| writeString | `String` | +| writeUnsignedChar | `Integer` | +| writeUnsignedInt | `Number` / `String` | +| writeUnsignedInt64 | `Number` / `String` | +| writeUnsignedShort | `Integer` / `String` | +| writeUnsignedVarInt | `Number` / `String` | +| writeUnsignedVarInt64 | `Number` / `String` | +| writeVarInt | `Number` / `String` | +| writeVarInt64 | `Number` / `String` | +| writeVec3 | `FloatPos` | +| writeBlockPos (Added in 0.9.5) | `BlockPos` | +| writeCompoundTag | `NbtCompound` | +| writeItem (Added in 0.9.5) | `Item` | +| writeUuid (Added in 0.19.0) | `String` | #### Building packet from binary stream -`bs.createPacket(pktid)` +!!! warning +The optional parameter of this function is only available in 0.19.0 and later. + +`bs.createPacket(pktid[,raw])` - Parameters: + - pktid : `Integer` Packet ID + - raw : `Boolean` (Optional parameter) + Create raw network packet from current binary stream data. The default value is `false` - Return value:Packet object - Return value type: `Packet` - +Before 0.19.0, this function can only be used as `bs.createPacket(pktid)`. ### Dome Code Send TextPacket packets to a player + ```js -mc.listen("onChat",function(pl,msg){ - const bs = new BinaryStream() - var text = "LLSE Packet Test" - bs.reserve(text.length + 8) - bs.writeUnsignedChar(0) - bs.writeBool(true) - bs.writeString(text) - bs.writeString("") - bs.writeString("") - var pkt = bs.createPacket(9) - pl.sendPacket(pkt) -}) +mc.listen("onChat", (player, message) => { + const text = "LLSE Packet Test"; + const bs = new BinaryStream(); + bs.reserve(text.length + 8); + bs.writeBool(false); + bs.writeByte(/* TextPacketPayload::mBody::MessageOnly (Variant Index) */0); + bs.writeByte(/* TextPacketType::Raw (Enum) */0); + bs.writeString(text); + bs.writeString(""); // xuid + bs.writeString(""); // platformId + bs.writeString(""); // filtered message + bs.createPacket(9).sendTo(player); +}); ``` diff --git a/docs/apis/GameAPI/Packet.zh.md b/docs/apis/GameAPI/Packet.zh.md index dac8b728..793b395d 100644 --- a/docs/apis/GameAPI/Packet.zh.md +++ b/docs/apis/GameAPI/Packet.zh.md @@ -1,30 +1,26 @@ -# 🎓 数据包 API +# 🎓 数据包 API -下面这些对象与API为脚本提供了基本的BDS数据包接口。 +下面这些对象与 API 为脚本提供了基本的 BDS 数据包接口。 -温馨提示:此类API需要部分逆向基础,了解数据包结构(可通过参考Nukkit,PokcetMine,BDS逆向得知数据包结构)如出现客户端崩溃,为数据包结构错误,并非BUG。 - -文档不列出数据包ID与其结构,请自行查询。 +温馨提示:此类 API 需要部分逆向基础,了解数据包结构(可通过参考 Nukkit,PokcetMine,BDS 逆向得知数据包结构)如出现客户端崩溃,为数据包结构错误,并非 BUG。 +文档不列出数据包 ID 与其结构,请自行查询。 ## 目录 + - 🔉 [数据包对象 API](#-数据包对象-api) - 🔌 [二进制流对象 API](#-二进制流对象-api) - - ## 🔉 数据包对象 API 在脚本引擎中,使用「数据包对象」来获取数据包的相关信息。 ### 获取一个数据包对象 -#### 从API获取 - -调用某些**返回数据包对象**的函数,来获取到BDS给出的数据包对象 -详见 [二进制流对象](#-二进制流对象-api) - +#### 从 API 获取 +调用某些**返回数据包对象**的函数,来获取到 BDS 给出的数据包对象 +详见 [二进制流对象](#-二进制流对象-api) ### 数据包对象 - 函数 @@ -37,24 +33,62 @@ - 返回值:数据包名称 - 返回值类型: `String` - - -#### 获取数据包ID +#### 获取数据包 ID `pkt.getId()` -- 返回值:数据包ID +- 返回值:数据包 ID - 返回值类型: `Integer` +#### 发送数据包到指定目标 + +!!! warning +此函数仅在 0.19.0 及以后版本可用 + +`pkt.sendTo(pos)` +`pkt.sendTo(x,y,z,dimid)` +`pkt.sendTo(target)` + +- 参数: + + - pos : `IntPos` / `FloatPos` + 数据包发送目标所在坐标(或者使用 x, y, z, dimid 来确定目标位置) + - target : `Player` / `Entity` + 数据包发送目标 + +- 返回值:是否成功 +- 返回值类型: `Boolean` + +如果 `target` 是 `Player`,则数据包会发送到指定玩家。 +如果 `target` 是 `Entity`,则数据包会发送到指定实体周围的玩家。 +#### 发送数据包到所有客户端 + +!!! warning +此函数仅在 0.19.0 及以后版本可用 + +`pkt.sendToClients()` + +- 返回值:是否成功 +- 返回值类型: `Boolean` + +#### 发送数据包到服务端 + +!!! warning +此函数仅在 0.19.0 及以后版本可用 + +`pkt.sendToServer()` + +- 返回值:是否成功 +- 返回值类型: `Boolean` ## 🔌 二进制流对象 API ### 创建一个二进制流对象 -[JavaScript] ```new BinaryStream()``` +[JavaScript] `new BinaryStream()` -[Lua] ```BinaryStream()``` +[Lua] `BinaryStream()` - 返回值:二进制流对象 - 返回值类型: `BinaryStream` @@ -70,72 +104,141 @@ - 返回值:是否成功 - 返回值类型: `Boolean` +#### 获取二进制流读指针 + +!!! warning +此函数仅在 0.19.0 及以后版本可用 + +`bs.getReadPointer()` + +- 返回值:当前读指针 +- 返回值类型: `Integer` + +#### 设置二进制流读指针 +!!! warning +此函数仅在 0.19.0 及以后版本可用 + +`bs.setReadPointer(pos)` + +- 参数: + + - pos : `Integer` + 新的读指针 + +- 返回值:是否成功 +- 返回值类型: `Boolean` + +#### 获取二进制流数据 + +!!! warning +此函数的可选参数和 `ByteBuffer` 返回值仅在 0.19.0 及以后版本可用 + +`bs.getData([clear])` + +- 参数: + + - clear : `Boolean`(可选参数) + 获取后是否清空流数据。默认值为 `true` + +- 返回值:二进制流数据 +- 返回值类型: `ByteBuffer` + +在 0.19.0 之前,此函数只能使用 `bs.getData()` 形式调用。 +当时它在获取后总是会清空流数据,且返回值类型为 `String`。 + +由于旧版返回的是 `String`,在 JavaScript 中可能会被强制按 UTF-8 编码处理,导致二进制数据损坏,拿到的数据不正确。 + +#### 设置二进制流数据 + +!!! warning +此函数仅在 0.19.0 及以后版本可用 + +`bs.setData(data)` + +- 参数: + + - data : `ByteBuffer` + 二进制流数据 + +- 返回值:是否成功 +- 返回值类型: `Boolean` #### 写入二进制流 -`bs.writexxxx(value)` +`bs.writexxxx(value)` - 参数: + - value : `NULL` 参考下面表格 + 部分数值写入函数也允许传入 `String` - 返回值:是否成功 - 返回值类型: `Boolean` -| 可用函数 | 参数类型 | -| ---------------------------- | ------------- | -| writeBool | `Boolean` | -| writeByte | `Integer` | -| writeDouble | `Number` | -| writeFloat | `Float` | -| writeSignedBigEndianInt | `Number` | -| writeSignedInt | `Number` | -| writeSignedInt64 | `Number` | -| writeSignedShort | `Integer` | -| writeString | `String` | -| writeUnsignedChar | `Integer` | -| writeUnsignedInt | `Number` | -| writeUnsignedInt64 | `Number` | -| writeUnsignedShort | `Integer` | -| writeUnsignedVarInt | `Number` | -| writeUnsignedVarInt64 | `Number` | -| writeVarInt | `Number` | -| writeVarInt64 | `Number` | -| writeVec3 | `FloatPos` | -| writeBlockPos (0.9.5 时加入) | `BlockPos` | -| writeCompoundTag | `NbtCompound` | -| writeItem (0.9.5 时加入) | `Item` | - - +| 可用函数 | 参数类型 | +| ------------------------------------ | -------------------- | +| writeBool | `Boolean` | +| writeByte | `Integer` | +| writeBytes (0.19.0 时加入) | `ByteBuffer` | +| writeDouble | `Number` / `String` | +| writeFloat | `Float` / `String` | +| writeNormalizedFloat (0.19.0 时加入) | `Float` / `String` | +| writeSignedBigEndianInt | `Number` / `String` | +| writeSignedInt | `Number` / `String` | +| writeSignedInt64 | `Number` / `String` | +| writeSignedShort | `Integer` / `String` | +| writeString | `String` | +| writeUnsignedChar | `Integer` | +| writeUnsignedInt | `Number` / `String` | +| writeUnsignedInt64 | `Number` / `String` | +| writeUnsignedShort | `Integer` / `String` | +| writeUnsignedVarInt | `Number` / `String` | +| writeUnsignedVarInt64 | `Number` / `String` | +| writeVarInt | `Number` / `String` | +| writeVarInt64 | `Number` / `String` | +| writeVec3 | `FloatPos` | +| writeBlockPos (0.9.5 时加入) | `BlockPos` | +| writeCompoundTag | `NbtCompound` | +| writeItem (0.9.5 时加入) | `Item` | +| writeUuid (0.19.0 时加入) | `String` | #### 通过二进制流构建数据包 -`bs.createPacket(pktid)` +!!! warning +此函数的可选参数仅在 0.19.0 及以后版本可用 + +`bs.createPacket(pktid[,raw])` - 参数: + - pktid : `Integer` - 数据包ID + 数据包 ID + - raw : `Boolean`(可选参数) + 是否从当前二进制流数据创建原始网络数据包。默认值为 `false` - 返回值:数据包对象 - 返回值类型: `Packet` - +在 0.19.0 之前,此函数只能使用 `bs.createPacket(pktid)` 形式调用。 ### 演示代码 -向一个玩家发送TextPacket数据包 +向一个玩家发送 TextPacket 数据包 + ```js -mc.listen("onChat",function(pl,msg){ - const bs = new BinaryStream() - var text = "LLSE Packet Test" - bs.reserve(text.length + 8) - bs.writeUnsignedChar(0) - bs.writeBool(true) - bs.writeString(text) - bs.writeString("") - bs.writeString("") - var pkt = bs.createPacket(9) - pl.sendPacket(pkt) +mc.listen("onChat", (player, message) => { + const text = "LLSE Packet Test"; + const bs = new BinaryStream(); + bs.reserve(text.length + 8); + bs.writeBool(false); + bs.writeByte(/* TextPacketPayload::mBody::MessageOnly (Variant Index) */0); + bs.writeByte(/* TextPacketType::Raw (Enum) */0); + bs.writeString(text); + bs.writeString(""); // xuid + bs.writeString(""); // platformId + bs.writeString(""); // filtered message + bs.createPacket(9).sendTo(player); }); ``` diff --git a/src/legacy/api/PacketAPI.cpp b/src/legacy/api/PacketAPI.cpp index 9380143f..7fa76dea 100644 --- a/src/legacy/api/PacketAPI.cpp +++ b/src/legacy/api/PacketAPI.cpp @@ -2,8 +2,11 @@ #include "legacy/api/APIHelp.h" #include "legacy/api/BaseAPI.h" +#include "legacy/api/EntityAPI.h" #include "legacy/api/ItemAPI.h" #include "legacy/api/NbtAPI.h" +#include "legacy/api/PlayerAPI.h" +#include "lse/api/NetworkPacket.h" #include "lse/api/helper/ItemStackSerializerHelpers.h" #include "mc/deps/core/utility/BinaryStream.h" #include "mc/network/MinecraftPackets.h" @@ -16,25 +19,33 @@ ClassDefine PacketClassBuilder = defineClass("LLSE_Pac .constructor(nullptr) .instanceFunction("getName", &PacketClass::getName) .instanceFunction("getId", &PacketClass::getId) + .instanceFunction("sendTo", &PacketClass::sendTo) + .instanceFunction("sendToClients", &PacketClass::sendToClients) + .instanceFunction("sendToServer", &PacketClass::sendToServer) .build(); ClassDefine BinaryStreamClassBuilder = defineClass("BinaryStream") .constructor(&BinaryStreamClass::constructor) - .instanceFunction("getData", &BinaryStreamClass::getAndReleaseData) + .instanceFunction("getReadPointer", &BinaryStreamClass::getReadPointer) + .instanceFunction("setReadPointer", &BinaryStreamClass::setReadPointer) + .instanceFunction("getData", &BinaryStreamClass::getData) + .instanceFunction("setData", &BinaryStreamClass::setData) .instanceFunction("reset", &BinaryStreamClass::reset) .instanceFunction("reserve", &BinaryStreamClass::reserve) .instanceFunction("writeBool", &BinaryStreamClass::writeBool) .instanceFunction("writeByte", &BinaryStreamClass::writeByte) + .instanceFunction("writeBytes", &BinaryStreamClass::writeBytes) .instanceFunction("writeDouble", &BinaryStreamClass::writeDouble) .instanceFunction("writeFloat", &BinaryStreamClass::writeFloat) + .instanceFunction("writeNormalizedFloat", &BinaryStreamClass::writeNormalizedFloat) .instanceFunction("writeSignedBigEndianInt", &BinaryStreamClass::writeSignedBigEndianInt) .instanceFunction("writeSignedInt", &BinaryStreamClass::writeSignedInt) .instanceFunction("writeSignedInt64", &BinaryStreamClass::writeSignedInt64) .instanceFunction("writeSignedShort", &BinaryStreamClass::writeSignedShort) .instanceFunction("writeString", &BinaryStreamClass::writeString) - .instanceFunction("writeUnsignedChar", &BinaryStreamClass::writeUnsignedChar) + .instanceFunction("writeUnsignedChar", &BinaryStreamClass::writeByte) .instanceFunction("writeUnsignedInt", &BinaryStreamClass::writeUnsignedInt) .instanceFunction("writeUnsignedInt64", &BinaryStreamClass::writeUnsignedInt64) .instanceFunction("writeUnsignedShort", &BinaryStreamClass::writeUnsignedShort) @@ -46,6 +57,7 @@ ClassDefine BinaryStreamClassBuilder = .instanceFunction("writeBlockPos", &BinaryStreamClass::writeBlockPos) .instanceFunction("writeCompoundTag", &BinaryStreamClass::writeCompoundTag) .instanceFunction("writeItem", &BinaryStreamClass::writeItem) + .instanceFunction("writeUuid", &BinaryStreamClass::writeUuid) .instanceFunction("createPacket", &BinaryStreamClass::createPacket) .build(); @@ -89,189 +101,144 @@ Local PacketClass::getId() { CATCH_AND_THROW } -//////////////////// BinaryStream Classes //////////////////// - -BinaryStreamClass::BinaryStreamClass(std::shared_ptr const& bs) -: ScriptClass(ConstructFromCpp{}) { - set(bs); -} - -// generating function -Local BinaryStreamClass::newBinaryStream() { - auto out = new BinaryStreamClass(std::make_shared()); - return out->getScriptObject(); -} - -// member function - -Local BinaryStreamClass::getAndReleaseData() { +Local PacketClass::sendTo(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); try { - auto stream = get(); - if (!stream) { + std::shared_ptr pkt = get(); + if (!pkt) { return {}; } - std::string data; - stream->mBuffer.swap(data); - return String::newString(data); - } - CATCH_AND_THROW -} -BinaryStreamClass* BinaryStreamClass::constructor(Arguments const& args) { - try { - return new BinaryStreamClass(args.thiz()); - } - CATCH_AND_THROW -} - -Local BinaryStreamClass::reset() { - try { - auto stream = get(); - if (!stream) { - return {}; + if (IsInstanceOf(args[0])) { + auto* player = PlayerClass::extract(args[0]); + pkt->sendTo(*player); + } else if (IsInstanceOf(args[0])) { + auto* entity = EntityClass::extract(args[0]); + pkt->sendTo(*entity); + } else if (IsInstanceOf(args[0])) { + auto* pos = IntPos::extractPos(args[0]); + pkt->sendTo(pos->getBlockPos(), pos->getDimensionId()); + } else if (IsInstanceOf(args[0])) { + auto* pos = FloatPos::extractPos(args[0]); + pkt->sendTo(pos->getVec3(), pos->getDimensionId()); + } else if (args.size() >= 4 && std::ranges::all_of(std::views::iota(0ull, 4ull), [&](auto index) { + return args[index].getKind() == ValueKind::kNumber; + })) { + pkt->sendTo( + BlockPos{args[0].asNumber().toInt32(), args[1].asNumber().toInt32(), args[2].asNumber().toInt32()}, + args[3].asNumber().toInt32() + ); + } else { + throw WrongArgTypeException(__FUNCTION__); } - stream->mBuffer.clear(); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::reserve(Arguments const& args) { +Local PacketClass::sendToClients(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { - return Boolean::newBoolean(true); - } - CATCH_AND_THROW -} - -Local BinaryStreamClass::writeBool(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kBoolean); - try { - auto stream = get(); - if (!stream) { + std::shared_ptr pkt = get(); + if (!pkt) { return {}; } - stream->writeBool(args[0].asBoolean().value(), nullptr, nullptr); + + pkt->sendToClients(); + return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeByte(Arguments const& args) { +Local PacketClass::sendToServer(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { - auto stream = get(); - if (!stream) { + std::shared_ptr pkt = get(); + if (!pkt) { return {}; } - stream->writeByte(args[0].asNumber().toInt32(), nullptr, nullptr); + + pkt->sendToServer(); + return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeDouble(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeDouble(args[0].asNumber().toDouble(), nullptr, nullptr); - return Boolean::newBoolean(true); - } - CATCH_AND_THROW +//////////////////// BinaryStream Classes //////////////////// + +BinaryStreamClass::BinaryStreamClass(std::shared_ptr const& bs) +: ScriptClass(ConstructFromCpp{}) { + set(bs); } -Local BinaryStreamClass::writeFloat(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeFloat(args[0].asNumber().toFloat(), nullptr, nullptr); - return Boolean::newBoolean(true); - } - CATCH_AND_THROW +// generating function +Local BinaryStreamClass::newBinaryStream() { + auto out = new BinaryStreamClass(std::make_shared()); + return out->getScriptObject(); } -Local BinaryStreamClass::writeSignedBigEndianInt(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); +// member function + +Local BinaryStreamClass::getData(Arguments const& args) { + if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kBoolean); try { + auto stream = get(); if (!stream) { return {}; } - stream->writeSignedBigEndianInt(args[0].asNumber().toInt32(), nullptr, nullptr); - return Boolean::newBoolean(true); - } - CATCH_AND_THROW -} -Local BinaryStreamClass::writeSignedInt(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; + auto result = ByteBuffer::newByteBuffer(stream->mBuffer.data(), stream->mBuffer.size()); + if (args.size() < 1 || args[0].asBoolean().value()) { + stream->mBuffer.clear(); + stream->mView = stream->mBuffer; + stream->mReadPointer = 0; } - stream->writeSignedInt(args[0].asNumber().toInt32(), nullptr, nullptr); - return Boolean::newBoolean(true); + return result; } CATCH_AND_THROW } -Local BinaryStreamClass::writeSignedInt64(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); +Local BinaryStreamClass::setData(Arguments const& args) { + if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kByteBuffer); try { + auto stream = get(); if (!stream) { return {}; } - stream->writeSignedInt64(args[0].asNumber().toInt64(), nullptr, nullptr); + + auto buffer = args[0].asByteBuffer(); + stream->mBuffer = std::string_view{reinterpret_cast(buffer.getRawBytes()), buffer.byteLength()}; + stream->mView = stream->mBuffer; + stream->mReadPointer = 0; + return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeSignedShort(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); +BinaryStreamClass* BinaryStreamClass::constructor(Arguments const& args) { try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeSignedShort(args[0].asNumber().toInt32(), nullptr, nullptr); - return Boolean::newBoolean(true); + return new BinaryStreamClass(args.thiz()); } CATCH_AND_THROW } -Local BinaryStreamClass::writeString(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kString); +Local BinaryStreamClass::getReadPointer(Arguments const& args) { try { auto stream = get(); if (!stream) { return {}; } - stream->writeString(args[0].asString().toString(), nullptr, nullptr); - return Boolean::newBoolean(true); + return Number::newNumber(static_cast(stream->mReadPointer)); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedChar(Arguments const& args) { +Local BinaryStreamClass::setReadPointer(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { @@ -279,27 +246,27 @@ Local BinaryStreamClass::writeUnsignedChar(Arguments const& args) { if (!stream) { return {}; } - stream->writeByte(args[0].asNumber().toInt32(), nullptr, nullptr); + stream->mReadPointer = args[0].asNumber().toInt64(); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedInt(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); +Local BinaryStreamClass::reset() { try { auto stream = get(); if (!stream) { return {}; } - stream->writeUnsignedInt(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); + stream->mBuffer.clear(); + stream->mView = stream->mBuffer; + stream->mReadPointer = 0; return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedInt64(Arguments const& args) { +Local BinaryStreamClass::reserve(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { @@ -307,27 +274,27 @@ Local BinaryStreamClass::writeUnsignedInt64(Arguments const& args) { if (!stream) { return {}; } - stream->writeUnsignedInt64(static_cast(args[0].asNumber().toInt64()), nullptr, nullptr); + stream->mBuffer.reserve(args[0].asNumber().toInt32()); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedShort(Arguments const& args) { +Local BinaryStreamClass::writeBool(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + CHECK_ARG_TYPE(args[0], ValueKind::kBoolean); try { auto stream = get(); if (!stream) { return {}; } - stream->writeUnsignedShort(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); + stream->writeBool(args[0].asBoolean().value(), nullptr, nullptr); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedVarInt(Arguments const& args) { +Local BinaryStreamClass::writeByte(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { @@ -335,53 +302,90 @@ Local BinaryStreamClass::writeUnsignedVarInt(Arguments const& args) { if (!stream) { return {}; } - stream->writeUnsignedVarInt(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); + stream->writeByte(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedVarInt64(Arguments const& args) { +Local BinaryStreamClass::writeBytes(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + CHECK_ARG_TYPE(args[0], ValueKind::kByteBuffer); try { auto stream = get(); if (!stream) { return {}; } - stream->writeUnsignedVarInt64(static_cast(args[0].asNumber().toInt64()), nullptr, nullptr); + auto buffer = args[0].asByteBuffer(); + stream->mBuffer.append(reinterpret_cast(buffer.getRawBytes()), buffer.byteLength()); + stream->mView = stream->mBuffer; return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeVarInt(Arguments const& args) { +Local BinaryStreamClass::writeString(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + CHECK_ARG_TYPE(args[0], ValueKind::kString); try { auto stream = get(); if (!stream) { return {}; } - stream->writeVarInt(args[0].asNumber().toInt32(), nullptr, nullptr); + stream->writeString(args[0].asString().toString(), nullptr, nullptr); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeVarInt64(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeVarInt64(args[0].asNumber().toInt64(), nullptr, nullptr); - return Boolean::newBoolean(true); +#define WRITE_MACRO(FUNC_NAME, INDEX) \ + Local BinaryStreamClass::FUNC_NAME(Arguments const& args) { \ + CHECK_ARGS_COUNT(args, 1); \ + CHECK_ARG_TYPE(args[0], ValueKind::kNumber && args[0].getKind() != ValueKind::kString); \ + try { \ + auto stream = get(); \ + if (!stream) { \ + return {}; \ + } \ + using T = ll::traits::function_traits::arg<0>; \ + T value{}; \ + if (args[0].isNumber()) { \ + value = static_cast(args[0].asNumber().toInt64()); \ + } else if (args[0].isString()) { \ + if (auto res = ll::string_utils::svtonum(args[0].asString().toString(), nullptr, INDEX); res) { \ + value = res.value(); \ + } else { \ + throw WrongArgTypeException(__FUNCTION__); \ + } \ + } \ + stream->FUNC_NAME(value, nullptr, nullptr); \ + return Boolean::newBoolean(true); \ + } \ + CATCH_AND_THROW \ } - CATCH_AND_THROW -} + +#define WRITE_INTEGER_MACRO(FUNC_NAME) WRITE_MACRO(FUNC_NAME, 10) +#define WRITE_FLOAT_MACRO(FUNC_NAME) WRITE_MACRO(FUNC_NAME, std::chars_format::general) + +WRITE_INTEGER_MACRO(writeSignedBigEndianInt); +WRITE_INTEGER_MACRO(writeSignedInt); +WRITE_INTEGER_MACRO(writeSignedInt64); +WRITE_INTEGER_MACRO(writeSignedShort); +WRITE_INTEGER_MACRO(writeUnsignedInt); +WRITE_INTEGER_MACRO(writeUnsignedInt64); +WRITE_INTEGER_MACRO(writeUnsignedShort); +WRITE_INTEGER_MACRO(writeUnsignedVarInt); +WRITE_INTEGER_MACRO(writeUnsignedVarInt64); +WRITE_INTEGER_MACRO(writeVarInt); +WRITE_INTEGER_MACRO(writeVarInt64); + +WRITE_FLOAT_MACRO(writeFloat) +WRITE_FLOAT_MACRO(writeDouble) +WRITE_FLOAT_MACRO(writeNormalizedFloat) + +#undef WRITE_MACRO +#undef WRITE_INTEGER_MACRO +#undef WRITE_FLOAT_MACRO Local BinaryStreamClass::writeVec3(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); @@ -414,7 +418,7 @@ Local BinaryStreamClass::writeBlockPos(Arguments const& args) { } IntPos* posObj = IntPos::extractPos(args[0]); stream->writeVarInt(posObj->getBlockPos().x, nullptr, nullptr); - stream->writeUnsignedVarInt(posObj->getBlockPos().y, nullptr, nullptr); + stream->writeVarInt(posObj->getBlockPos().y, nullptr, nullptr); stream->writeVarInt(posObj->getBlockPos().z, nullptr, nullptr); return Boolean::newBoolean(true); } @@ -455,15 +459,49 @@ Local BinaryStreamClass::writeItem(Arguments const& args) { CATCH_AND_THROW } +Local BinaryStreamClass::writeUuid(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + auto uuidStr = args[0].asString().toString(); + if (!mce::UUID::canParse(uuidStr)) { + throw Exception(fmt ::format("Invalid UUID: {}", uuidStr)); + } + + try { + auto stream = get(); + if (!stream) { + return {}; + } + + auto uuid = mce::UUID::fromString(uuidStr); + stream->writeUnsignedInt64(uuid.a, nullptr, nullptr); + stream->writeUnsignedInt64(uuid.b, nullptr, nullptr); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + Local BinaryStreamClass::createPacket(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); + if (args.size() >= 2) { + CHECK_ARG_TYPE(args[1], ValueKind::kBoolean); + } try { auto stream = get(); if (!stream) { return {}; } - auto pkt = MinecraftPackets::createPacket(static_cast(args[0].asNumber().toInt32())); - pkt->read(*stream); + std::shared_ptr pkt = + args.size() < 2 || !args[1].asBoolean().value() + ? MinecraftPackets::createPacket(static_cast(args[0].asNumber().toInt32())) + : std::make_shared( + static_cast(args[0].asNumber().toInt32()), + "" + ); + + if (auto res = pkt->read(*stream); !res) { + throw Exception(fmt ::format("{}\nfunction: {}", res.error().code().message(), __func__)); + } return PacketClass::newPacket(pkt); } CATCH_AND_THROW diff --git a/src/legacy/api/PacketAPI.h b/src/legacy/api/PacketAPI.h index 3b711cb7..a814aa51 100644 --- a/src/legacy/api/PacketAPI.h +++ b/src/legacy/api/PacketAPI.h @@ -22,6 +22,10 @@ class PacketClass : public ScriptClass { Local getId(); Local getName(); + + Local sendTo(Arguments const& args); + Local sendToClients(Arguments const& args); + Local sendToServer(Arguments const& args); }; extern ClassDefine PacketClassBuilder; @@ -42,20 +46,24 @@ class BinaryStreamClass : public ScriptClass { static Local newBinaryStream(); static BinaryStreamClass* constructor(Arguments const& args); - Local getAndReleaseData(); + Local getReadPointer(Arguments const& args); + Local setReadPointer(Arguments const& args); + Local getData(Arguments const& args); + Local setData(Arguments const& args); Local reset(); Local reserve(Arguments const& args); Local writeBool(Arguments const& args); Local writeByte(Arguments const& args); + Local writeBytes(Arguments const& args); Local writeDouble(Arguments const& args); Local writeFloat(Arguments const& args); + Local writeNormalizedFloat(Arguments const& args); Local writeSignedBigEndianInt(Arguments const& args); Local writeSignedInt(Arguments const& args); Local writeSignedInt64(Arguments const& args); Local writeSignedShort(Arguments const& args); Local writeString(Arguments const& args); - Local writeUnsignedChar(Arguments const& args); Local writeUnsignedInt(Arguments const& args); Local writeUnsignedInt64(Arguments const& args); Local writeUnsignedShort(Arguments const& args); @@ -67,6 +75,7 @@ class BinaryStreamClass : public ScriptClass { Local writeBlockPos(Arguments const& args); Local writeCompoundTag(Arguments const& args); Local writeItem(Arguments const& args); + Local writeUuid(Arguments const& args); Local createPacket(Arguments const& args); }; diff --git a/src/legacy/api/PlayerAPI.cpp b/src/legacy/api/PlayerAPI.cpp index f94eeb01..d9af6297 100644 --- a/src/legacy/api/PlayerAPI.cpp +++ b/src/legacy/api/PlayerAPI.cpp @@ -2404,7 +2404,7 @@ Local PlayerClass::setBossBar(Arguments const& args) const { bs.writeUnsignedVarInt(0, nullptr, nullptr); // Links bs.writeUnsignedVarInt(0, nullptr, nullptr); - auto addPkt = lse::api::NetworkPacket(std::move(bs.mBuffer)); + auto addPkt = lse::api::NetworkPacket(MinecraftPacketIds::AddActor, std::move(bs.mBuffer)); BossBarColor color = static_cast(args[3].asNumber().toInt32()); auto pkt = diff --git a/src/lse/api/NetworkPacket.h b/src/lse/api/NetworkPacket.h index ed0e7bce..f1719671 100644 --- a/src/lse/api/NetworkPacket.h +++ b/src/lse/api/NetworkPacket.h @@ -10,10 +10,9 @@ namespace lse::api { -template class NetworkPacket final : public Packet { public: - NetworkPacket(std::string data) : mData(std::move(data)) {} + NetworkPacket(MinecraftPacketIds id, std::string data) : Packet(), mPacketId(id), mData(std::move(data)) {} NetworkPacket() = default; NetworkPacket(NetworkPacket&&) = default; @@ -23,16 +22,20 @@ class NetworkPacket final : public Packet { NetworkPacket(NetworkPacket const&) = delete; NetworkPacket& operator=(NetworkPacket const&) = delete; - [[nodiscard]] MinecraftPacketIds getId() const override { return packetId; } + [[nodiscard]] MinecraftPacketIds getId() const override { return mPacketId; } [[nodiscard]] std::string_view getName() const override { return "NetworkPacket"; } void write(BinaryStream& stream) const override { stream.mBuffer.append(mData); } - Bedrock::Result _read(class ReadOnlyBinaryStream& /*stream*/) override { return Bedrock::Result{}; } + Bedrock::Result _read(ReadOnlyBinaryStream& stream) override { + mData = stream.mView.substr(stream.mReadPointer); + return {}; + } private: - std::string mData; + MinecraftPacketIds mPacketId; + std::string mData; }; } // namespace lse::api From 19e109264b17b912eaf55822b31fe4c03e83c95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:30:02 +0800 Subject: [PATCH 02/17] fix: fix command parameter result --- src/legacy/api/CommandAPI.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/legacy/api/CommandAPI.cpp b/src/legacy/api/CommandAPI.cpp index 511b3850..05cd1453 100644 --- a/src/legacy/api/CommandAPI.cpp +++ b/src/legacy/api/CommandAPI.cpp @@ -170,6 +170,9 @@ Local convertResult(ParamStorageType const& result, CommandOrigin const& if (result.hold(ParamKind::Kind::String)) { return String::newString(std::get(result.value())); } + if (result.hold(ParamKind::Kind::Dimension)) { + return Number::newNumber(std::get(result.value()).id); + } return {}; } From 5e39cd8a0264a7c3e98e97f201c0ad76ee5f4560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Mon, 29 Jun 2026 22:05:55 +0800 Subject: [PATCH 03/17] feat: add some apis --- CHANGELOG.md | 12 ++++++ docs/apis/GameAPI/Server.md | 69 ++++++++++++++++++++++++++++++++++ docs/apis/GameAPI/Server.zh.md | 69 ++++++++++++++++++++++++++++++++++ src/legacy/api/McAPI.cpp | 5 +++ src/legacy/api/McAPI.h | 6 +++ src/legacy/api/ServerAPI.cpp | 64 +++++++++++++++++++++++++++++++ 6 files changed, 225 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 865534c6..faa2f95f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added packet sending APIs: `Packet::sendTo`, `Packet::sendToClients` and `Packet::sendToServer` @zimuya4153 +- Added `BinaryStream` APIs: `getReadPointer`, `setReadPointer`, `setData`, `writeBytes`, `writeUuid` and `writeNormalizedFloat` @zimuya4153 +- Added server APIs: `mc.getMotd`, `mc.getOnlinePlayerNum`, `mc.getMaxNumPlayers`, `mc.getDimensionId` and `mc.getDimensionName` @zimuya4153 + +### Changed + +- `BinaryStream::getData([clear])` now supports the optional `clear` parameter and returns `ByteBuffer` instead of `String` @zimuya4153 +- `BinaryStream::createPacket(pktid[,raw])` now supports creating raw packets @zimuya4153 +- Allowed string arguments for several `BinaryStream::write*` numeric APIs to better support BigInt values @zimuya4153 + ## [0.18.2] - 2026-04-17 ### Fixed diff --git a/docs/apis/GameAPI/Server.md b/docs/apis/GameAPI/Server.md index aea886fe..92395cc4 100644 --- a/docs/apis/GameAPI/Server.md +++ b/docs/apis/GameAPI/Server.md @@ -20,6 +20,18 @@ The following APIs provide interfaces for customizing some server settings: +### Get Server Motd String + +!!! warning +This function is only available in 0.19.0 and later. + +`mc.getMotd()` + +- Return value: Current server Motd string +- Return value type: `String` + + + ### Set Server Motd String `mc.setMotd(motd)` @@ -44,6 +56,33 @@ The following APIs provide interfaces for customizing some server settings: +### Get Online Player Count + +!!! warning +This function is only available in 0.19.0 and later. + +`mc.getOnlinePlayerNum([ignoreSimulatedPlayer])` + +- Parameters: + - ignoreSimulatedPlayer : `Boolean` = `false` + Whether to ignore simulated players +- Return value: Current number of online players +- Return value type: `Number` + + + +### Get Server Maximum Player Count + +!!! warning +This function is only available in 0.19.0 and later. + +`mc.getMaxNumPlayers()` + +- Return value: Current maximum number of players on the server +- Return value type: `Number` + + + ### Get Sever time `mc.getTime(TimeID)` @@ -90,3 +129,33 @@ Among them, daytime is the number of game ticks since dawn, gametime is the age - Return value type: `Boolean` + +### Get Dimension ID + +!!! warning +This function is only available in 0.19.0 and later. + +`mc.getDimensionId(name)` + +- Parameters: + - name : `String` + Dimension name +- Return value: Dimension ID corresponding to the dimension name, or `null` if the dimension name is invalid +- Return value type: `Number` + + + +### Get Dimension Name + +!!! warning +This function is only available in 0.19.0 and later. + +`mc.getDimensionName(dimid)` + +- Parameters: + - dimid : `Integer` + Dimension ID +- Return value: Dimension name corresponding to the dimension ID, or `null` if the dimension ID is invalid +- Return value type: `String` + + diff --git a/docs/apis/GameAPI/Server.zh.md b/docs/apis/GameAPI/Server.zh.md index 4f0a1b49..52908743 100644 --- a/docs/apis/GameAPI/Server.zh.md +++ b/docs/apis/GameAPI/Server.zh.md @@ -20,6 +20,18 @@ +### 获取服务器MOTD字符串 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`mc.getMotd()` + +- 返回值:当前服务器MOTD字符串 +- 返回值类型:`String` + + + ### 设置服务器MOTD字符串 `mc.setMotd(motd)` @@ -44,6 +56,33 @@ +### 获取在线玩家数量 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`mc.getOnlinePlayerNum([ignoreSimulatedPlayer])` + +- 参数: + - ignoreSimulatedPlayer : `Boolean` = `false` + 是否忽略模拟玩家 +- 返回值:当前在线玩家数量 +- 返回值类型:`Number` + + + +### 获取服务器最大玩家数 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`mc.getMaxNumPlayers()` + +- 返回值:当前服务器最大玩家数 +- 返回值类型:`Number` + + + ### 获取服务器游戏时间 `mc.getTime(TimeID)` @@ -90,3 +129,33 @@ - 返回值类型:`Boolean` + +### 获取维度ID + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`mc.getDimensionId(name)` + +- 参数: + - name : `String` + 维度名称 +- 返回值:对应维度名称的维度ID,若维度名称无效则返回`null` +- 返回值类型:`Number` + + + +### 获取维度名称 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`mc.getDimensionName(dimid)` + +- 参数: + - dimid : `Integer` + 维度ID +- 返回值:对应维度ID的维度名称,若维度ID无效则返回`null` +- 返回值类型:`String` + + diff --git a/src/legacy/api/McAPI.cpp b/src/legacy/api/McAPI.cpp index edb03ce4..7f276b80 100644 --- a/src/legacy/api/McAPI.cpp +++ b/src/legacy/api/McAPI.cpp @@ -26,6 +26,7 @@ ClassDefine McClassBuilder = defineClass("mc") .function("newSimpleForm", &McClass::newSimpleForm) .function("newCustomForm", &McClass::newCustomForm) .function("regConsoleCmd", &McClass::regConsoleCmd) + .function("getMotd", &McClass::getMotd) .function("setMotd", &McClass::setMotd) .function("sendCmdOutput", &McClass::sendCmdOutput) .function("newIntPos", &McClass::newIntPos) @@ -52,6 +53,10 @@ ClassDefine McClassBuilder = defineClass("mc") .function("setTime", &McClass::setTime) .function("getWeather", &McClass::getWeather) .function("setWeather", &McClass::setWeather) + .function("getDimensionId", &McClass::getDimensionId) + .function("getDimensionName", &McClass::getDimensionName) + .function("getOnlinePlayerNum", &McClass::getOnlinePlayerNum) + .function("getMaxNumPlayers", &McClass::getMaxNumPlayers) // For Compatibility .function("getAllScoreObjective", &McClass::getAllScoreObjectives) .function("getDisplayObjectives", &McClass::getDisplayObjective) diff --git a/src/legacy/api/McAPI.h b/src/legacy/api/McAPI.h index db54356a..b506325d 100644 --- a/src/legacy/api/McAPI.h +++ b/src/legacy/api/McAPI.h @@ -35,10 +35,13 @@ class McClass { static Local newCustomForm(Arguments const& args); static Local regConsoleCmd(Arguments const& args); + static Local getMotd(Arguments const& args); static Local setMotd(Arguments const& args); static Local sendCmdOutput(Arguments const& args); static Local crashBDS(Arguments const& args); + static Local getOnlinePlayerNum(Arguments const& args); + static Local getMaxNumPlayers(Arguments const& args); static Local setMaxNumPlayers(Arguments const& args); static Local newIntPos(Arguments const& args); @@ -69,5 +72,8 @@ class McClass { static Local setTime(Arguments const& args); static Local getWeather(Arguments const& args); static Local setWeather(Arguments const& args); + + static Local getDimensionId(Arguments const& args); + static Local getDimensionName(Arguments const& args); }; extern ClassDefine<> McClassBuilder; diff --git a/src/legacy/api/ServerAPI.cpp b/src/legacy/api/ServerAPI.cpp index 2c416e1d..221b31e1 100644 --- a/src/legacy/api/ServerAPI.cpp +++ b/src/legacy/api/ServerAPI.cpp @@ -8,10 +8,18 @@ #include "mc/common/SharedConstants.h" #include "mc/network/ServerNetworkHandler.h" #include "mc/network/packet/SetTimePacket.h" +#include "mc/world/level/dimension/VanillaDimensions.h" #include "mc/world/level/storage/LevelData.h" #include +Local McClass::getMotd(Arguments const& args) { + try { + return String::newString(ll::service::getServerNetworkHandler().and_then(&ServerNetworkHandler::mServerName)); + } + CATCH_AND_THROW +} + Local McClass::setMotd(Arguments const& args) { CHECK_ARGS_COUNT(args, 1) CHECK_ARG_TYPE(args[0], ValueKind::kString) @@ -24,6 +32,30 @@ Local McClass::setMotd(Arguments const& args) { Local McClass::crashBDS(Arguments const&) { return Boolean::newBoolean(false); } +Local McClass::getOnlinePlayerNum(Arguments const& args) { + if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kBoolean); + + try { + if (args.size() >= 1 && args[0].asBoolean().value()) { + return Number::newNumber(ll::service::getServerNetworkHandler().and_then([](auto& handler) { + return static_cast(handler.mClients->size()); + })); + } else { + return Number::newNumber(ll::service::getLevel().and_then(&Level::getUserCount)); + } + } + CATCH_AND_THROW +} + +Local McClass::getMaxNumPlayers(Arguments const& args) { + try { + return Number::newNumber( + ll::service::getServerNetworkHandler().and_then(&ServerNetworkHandler::mMaxNumPlayers) + ); + } + CATCH_AND_THROW +} + Local McClass::setMaxNumPlayers(Arguments const& args) { CHECK_ARGS_COUNT(args, 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber) @@ -128,3 +160,35 @@ Local McClass::setWeather(Arguments const& args) { return Boolean::newBoolean(true); } + +Local McClass::getDimensionId(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1) + CHECK_ARG_TYPE(args[0], ValueKind::kString) + + try { + if (auto dimid = VanillaDimensions::fromString(args[0].asString().toString()); + dimid != VanillaDimensions::Undefined()) { + return Number::newNumber(dimid); + } + return {}; + } + CATCH_AND_THROW + + return Boolean::newBoolean(true); +} + +Local McClass::getDimensionName(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1) + CHECK_ARG_TYPE(args[0], ValueKind::kNumber) + + try { + auto& map = VanillaDimensions::DimensionMap().mLeft; + if (auto it = map.find(args[0].asNumber().toInt32()); it != map.end()) { + return String::newString(it->second); + } + return {}; + } + CATCH_AND_THROW + + return Boolean::newBoolean(true); +} From 5238bb661fc9c66890fd3edd24a3baf434180a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Tue, 30 Jun 2026 12:39:27 +0800 Subject: [PATCH 04/17] feat: add some apis --- CHANGELOG.md | 26 +++- docs/apis/GameAPI/Entity.md | 59 ++++++++ docs/apis/GameAPI/Entity.zh.md | 56 +++++++ docs/apis/GameAPI/Item.md | 42 ++++++ docs/apis/GameAPI/Item.zh.md | 42 ++++++ docs/apis/GameAPI/Packet.md | 92 ++++++++---- docs/apis/GameAPI/Packet.zh.md | 92 ++++++++---- docs/apis/SystemAPI/File.md | 18 ++- docs/apis/SystemAPI/File.zh.md | 18 ++- docs/apis/SystemAPI/SystemInfo.md | 12 ++ docs/apis/SystemAPI/SystemInfo.zh.md | 12 ++ src/legacy/api/EntityAPI.cpp | 116 ++++++++++++++- src/legacy/api/EntityAPI.h | 1 + src/legacy/api/FileSystemAPI.cpp | 20 ++- src/legacy/api/ItemAPI.cpp | 45 +++++- src/legacy/api/ItemAPI.h | 3 + src/legacy/api/McAPI.cpp | 2 + src/legacy/api/McAPI.h | 2 + src/legacy/api/PacketAPI.cpp | 210 ++++++++++++++++++--------- src/legacy/api/PacketAPI.h | 21 ++- src/legacy/api/SystemAPI.cpp | 3 + src/legacy/api/SystemAPI.h | 1 + src/legacy/utils/IniHelper.cpp | 1 + xmake.lua | 3 +- 24 files changed, 751 insertions(+), 146 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faa2f95f..55c4fe9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,15 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added packet sending APIs: `Packet::sendTo`, `Packet::sendToClients` and `Packet::sendToServer` @zimuya4153 -- Added `BinaryStream` APIs: `getReadPointer`, `setReadPointer`, `setData`, `writeBytes`, `writeUuid` and `writeNormalizedFloat` @zimuya4153 -- Added server APIs: `mc.getMotd`, `mc.getOnlinePlayerNum`, `mc.getMaxNumPlayers`, `mc.getDimensionId` and `mc.getDimensionName` @zimuya4153 +- Added packet APIs: + `Packet::sendTo`, `Packet::sendToClients`, `Packet::sendToServer` @zimuya4153 +- Added `BinaryStream` APIs: + `getReadPointer`, `setReadPointer`, `setData`, `writeBytes`, `readBytes`, + `writeUuid` @zimuya4153 +- Added server APIs: + `mc.getMotd`, `mc.getOnlinePlayerNum`, `mc.getMaxNumPlayers`, + `mc.getDimensionId`, `mc.getDimensionName` @zimuya4153 +- Added gameplay helpers: + `en.setCustomName`, `mc.summonMob`, `mc.loadMob`, `it.addCount`, + `it.removeCount`, `it.setCount`, `system.randomUuid` @zimuya4153 ### Changed -- `BinaryStream::getData([clear])` now supports the optional `clear` parameter and returns `ByteBuffer` instead of `String` @zimuya4153 -- `BinaryStream::createPacket(pktid[,raw])` now supports creating raw packets @zimuya4153 -- Allowed string arguments for several `BinaryStream::write*` numeric APIs to better support BigInt values @zimuya4153 +- Improved `BinaryStream`: + `getData([clear])` now supports the optional `clear` parameter and returns + `ByteBuffer`; `createPacket(pktid[,raw])` now supports raw packets; several + numeric `write*` APIs now accept `String` for BigInt-friendly input + @zimuya4153 +- Improved file and item behavior: + `File.readFrom(path[,isBinary])` now supports binary reads; + `File.writeTo(path, text)` now accepts `ByteBuffer`; + `it.setLore([])` now clears custom lore instead of failing @zimuya4153 ## [0.18.2] - 2026-04-17 diff --git a/docs/apis/GameAPI/Entity.md b/docs/apis/GameAPI/Entity.md index 3b9f5b99..487d8a85 100644 --- a/docs/apis/GameAPI/Entity.md +++ b/docs/apis/GameAPI/Entity.md @@ -73,6 +73,52 @@ Through this function, generate a new creature at the specified location and get > Therefore, if there is a need to operate an entity for a long time, please obtain the real-time entity object through > the above methods. +#### Summon New Creature and Get Its Entity Object + +!!! warning +This function is only available in 0.19.0 and later. + +Through this function, summon a new creature at the specified location and get its corresponding entity object. + +`mc.summonMob(name,pos[,event])` +`mc.summonMob(name,x,y,z,dimid[,event])` + +- Parameters: + - name : `String` + The namespace name of the creature, such as `minecraft:creeper` + - pos : `IntPos `/ `FloatPos` + A coordinate object of where the mob is spawned (or use x, y, z, dimid to determine where to spawn). + - event : `String` + Optional initialization event name +- Return value: The generated entity object. +- Return value type: `Entity` + - If the return value is `Null` it means that the generation failed + +> Note: Do not save an entity object **long-term**. +> When the entity corresponding to the entity object is destroyed, the corresponding entity object will become invalid. +> Therefore, if there is a need to operate an entity for a long time, please obtain the real-time entity object through +> the above methods. + +#### Load Creature From NBT and Get Its Entity Object + +!!! warning +This function is only available in 0.19.0 and later. + +Through this function, spawn a creature with the specified NBT data at the specified location and get its corresponding +entity object. + +`mc.loadMob(nbt,pos)` +`mc.loadMob(nbt,x,y,z,dimid)` + +- Parameters: + - nbt : `NbtCompound` + Creature NBT object + - pos : `IntPos `/ `FloatPos` + A coordinate object of where the mob is spawned (or use x, y, z, dimid to determine where to spawn). +- Return value: The generated entity object. +- Return value type: `Entity` + - If the return value is `Null` it means that the generation failed + #### Clone A Creature and Get Its Entity Object Through this function, generate a new creature at the specified location and get its corresponding entity object. @@ -393,6 +439,19 @@ Note that the damage dealt here is real damage and cannot be reduced by protecti - Return value: Whether the entity was scaled. - Return value type: `Boolean` +#### Set Entity Custom Name + +!!! warning +This function is only available in 0.19.0 and later. + +`en.setCustomName(name)` + +- Parameters: + - name : `String` + The entity's new custom name +- Return value: Whether setting was successful +- Return value type: `Boolean` + #### Get Entity Distance To Pos `en.distanceTo(pos)` diff --git a/docs/apis/GameAPI/Entity.zh.md b/docs/apis/GameAPI/Entity.zh.md index c905ffb6..8b81ee54 100644 --- a/docs/apis/GameAPI/Entity.zh.md +++ b/docs/apis/GameAPI/Entity.zh.md @@ -69,6 +69,49 @@ > 注意:不要**长期保存**一个实体对象 > 当实体对象对应的实体被销毁时,对应的实体对象将同时释放。因此,如果有长期操作某个实体的需要,请通过上述途径获取实时的实体对象 +#### 召唤新生物并获取 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +通过此函数,在指定的位置召唤一个新生物,并获取它对应的实体对象 + +`mc.summonMob(name,pos[,event])` +`mc.summonMob(name,x,y,z,dimid[,event])` + +- 参数: + - name : `String` + 生物的命名空间名称,如 `minecraft:creeper` + - pos : `IntPos `/ `FloatPos` + 生成生物的位置的坐标对象(或者使用x, y, z, dimid来确定生成位置) + - event : `String` + 可选的初始化事件名称 +- 返回值:生成的实体对象 +- 返回值类型:`Entity` + - 如返回值为 `Null` 则表示生成失败 + +> 注意:不要**长期保存**一个实体对象 +> 当实体对象对应的实体被销毁时,对应的实体对象将同时释放。因此,如果有长期操作某个实体的需要,请通过上述途径获取实时的实体对象 + +#### 通过NBT加载生物并获取 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +通过此函数,使用指定的NBT数据在指定位置生成一个生物,并获取它对应的实体对象 + +`mc.loadMob(nbt,pos)` +`mc.loadMob(nbt,x,y,z,dimid)` + +- 参数: + - nbt : `NbtCompound` + 生物的NBT对象 + - pos : `IntPos `/ `FloatPos` + 生成生物的位置的坐标对象(或者使用x, y, z, dimid来确定生成位置) +- 返回值:生成的实体对象 +- 返回值类型:`Entity` + - 如返回值为 `Null` 则表示生成失败 + #### 复制生物并获取 通过此函数,在指定的位置复制另一个实体,并获取它对应的实体对象 @@ -383,6 +426,19 @@ - 返回值: 实体是否成功地被缩放 - 返回值类型: `Boolean` +#### 设置实体自定义名称 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`en.setCustomName(name)` + +- 参数: + - name : `String` + 实体新的自定义名称 +- 返回值: 是否设置成功 +- 返回值类型: `Boolean` + #### 获取实体到坐标的距离 `en.distanceTo(pos)` diff --git a/docs/apis/GameAPI/Item.md b/docs/apis/GameAPI/Item.md index c47b5288..5e970dad 100644 --- a/docs/apis/GameAPI/Item.md +++ b/docs/apis/GameAPI/Item.md @@ -205,6 +205,9 @@ For more usage of NBT objects, please refer to [NBT Interface Documentation](../ - Return value: Whether setting the lore was successful. - Return value type: `Boolean` +!!! warning +In 0.19.0 and later, passing an empty array will clear the item's lore. + #### Set Custom Item Name @@ -229,4 +232,43 @@ For more usage of NBT objects, please refer to [NBT Interface Documentation](../ - Return value: is the same kind of item - Return value type: `Boolean` +#### Increase Item Count + +!!! warning +This function is only available in 0.19.0 and later. + +`it.addCount(count)` + +- Parameters: + - count : `Integer` + Amount to add +- Return value: Whether setting was successful +- Return value type: `Boolean` + +#### Decrease Item Count + +!!! warning +This function is only available in 0.19.0 and later. + +`it.removeCount(count)` + +- Parameters: + - count : `Integer` + Amount to remove +- Return value: Whether setting was successful +- Return value type: `Boolean` + +#### Set Item Count + +!!! warning +This function is only available in 0.19.0 and later. + +`it.setCount(count)` + +- Parameters: + - count : `Integer` + Amount to set +- Return value: Whether setting was successful +- Return value type: `Boolean` + diff --git a/docs/apis/GameAPI/Item.zh.md b/docs/apis/GameAPI/Item.zh.md index 457a28bd..adb59da0 100644 --- a/docs/apis/GameAPI/Item.zh.md +++ b/docs/apis/GameAPI/Item.zh.md @@ -206,6 +206,9 @@ - 返回值:是否设置成功 - 返回值类型: `Boolean` +!!! warning + 在0.19.0及以后版本中,传入空数组会清空物品Lore。 + #### 设置自定义物品名称 @@ -230,4 +233,43 @@ - 返回值: 是否为同类物品 - 返回值类型: `Boolean` +#### 增加物品数量 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`it.addCount(count)` + +- 参数: + - count : `Integer` + 要增加的数量 +- 返回值: 是否设置成功 +- 返回值类型: `Boolean` + +#### 减少物品数量 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`it.removeCount(count)` + +- 参数: + - count : `Integer` + 要减少的数量 +- 返回值: 是否设置成功 +- 返回值类型: `Boolean` + +#### 设置物品数量 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`it.setCount(count)` + +- 参数: + - count : `Integer` + 要设置的数量 +- 返回值: 是否设置成功 +- 返回值类型: `Boolean` + diff --git a/docs/apis/GameAPI/Packet.md b/docs/apis/GameAPI/Packet.md index 8ffbc4ec..91852718 100644 --- a/docs/apis/GameAPI/Packet.md +++ b/docs/apis/GameAPI/Packet.md @@ -177,32 +177,72 @@ This function is only available in 0.19.0 and later. - Return value:success or not - Return value type: `Boolean` -| Available functions | Parameter Type | -| -------------------------------------- | -------------------- | -| writeBool | `Boolean` | -| writeByte | `Integer` | -| writeBytes (Added in 0.19.0) | `ByteBuffer` | -| writeDouble | `Number` / `String` | -| writeFloat | `Float` / `String` | -| writeNormalizedFloat (Added in 0.19.0) | `Float` / `String` | -| writeSignedBigEndianInt | `Number` / `String` | -| writeSignedInt | `Number` / `String` | -| writeSignedInt64 | `Number` / `String` | -| writeSignedShort | `Integer` / `String` | -| writeString | `String` | -| writeUnsignedChar | `Integer` | -| writeUnsignedInt | `Number` / `String` | -| writeUnsignedInt64 | `Number` / `String` | -| writeUnsignedShort | `Integer` / `String` | -| writeUnsignedVarInt | `Number` / `String` | -| writeUnsignedVarInt64 | `Number` / `String` | -| writeVarInt | `Number` / `String` | -| writeVarInt64 | `Number` / `String` | -| writeVec3 | `FloatPos` | -| writeBlockPos (Added in 0.9.5) | `BlockPos` | -| writeCompoundTag | `NbtCompound` | -| writeItem (Added in 0.9.5) | `Item` | -| writeUuid (Added in 0.19.0) | `String` | +| Available functions | Parameter Type | +| ------------------------------ | -------------------- | +| writeBool | `Boolean`/ `String` | +| writeByte | `Integer` / `String` | +| writeBytes (Added in 0.19.0) | `ByteBuffer` | +| writeDouble | `Number` / `String` | +| writeFloat | `Float` / `String` | +| writeSignedBigEndianInt | `Number` / `String` | +| writeSignedInt | `Number` / `String` | +| writeSignedInt64 | `Number` / `String` | +| writeSignedShort | `Integer` / `String` | +| writeString | `String` | +| writeUnsignedChar | `Integer` / `String` | +| writeUnsignedInt | `Number` / `String` | +| writeUnsignedInt64 | `Number` / `String` | +| writeUnsignedShort | `Integer` / `String` | +| writeUnsignedVarInt | `Number` / `String` | +| writeUnsignedVarInt64 | `Number` / `String` | +| writeVarInt | `Number` / `String` | +| writeVarInt64 | `Number` / `String` | +| writeVec3 | `FloatPos` | +| writeBlockPos (Added in 0.9.5) | `BlockPos` | +| writeCompoundTag | `NbtCompound` | +| writeItem (Added in 0.9.5) | `Item` | +| writeUuid (Added in 0.19.0) | `String` | + +#### Read from binary stream + +!!! warning +This function group is only available in 0.19.0 and later. + +`bs.readxxxx([asString])` +`bs.readBytes(length)` + +- Parameters: + + - asString : `Boolean` (Optional parameter) + Only available for numeric read functions. When set to `true`, the return value is converted to `String` + - length : `Integer` + Only available for `readBytes`. Number of bytes to read. Must be greater than `0` + +- Return value: + - Read result + +| Available functions | Parameter Type | Return Type | +| ---------------------- | -------------- | ------------------- | +| readBool | None | `Boolean` | +| readByte | `Boolean` | `Number` / `String` | +| readBytes | `Integer` | `ByteBuffer` | +| readUnsignedChar | `Boolean` | `Number` / `String` | +| readDouble | `Boolean` | `Number` / `String` | +| readFloat | `Boolean` | `Number` / `String` | +| readSignedBigEndianInt | `Boolean` | `Number` / `String` | +| readSignedInt | `Boolean` | `Number` / `String` | +| readSignedInt64 | `Boolean` | `Number` / `String` | +| readSignedShort | `Boolean` | `Number` / `String` | +| readString | None | `String` | +| readUnsignedInt | `Boolean` | `Number` / `String` | +| readUnsignedInt64 | `Boolean` | `Number` / `String` | +| readUnsignedShort | `Boolean` | `Number` / `String` | +| readUnsignedVarInt | `Boolean` | `Number` / `String` | +| readUnsignedVarInt64 | `Boolean` | `Number` / `String` | +| readVarInt | `Boolean` | `Number` / `String` | +| readVarInt64 | `Boolean` | `Number` / `String` | + +When `asString` is `true`, numeric `read*` functions return `String`; otherwise they return `Number`. #### Building packet from binary stream diff --git a/docs/apis/GameAPI/Packet.zh.md b/docs/apis/GameAPI/Packet.zh.md index 793b395d..250d91ff 100644 --- a/docs/apis/GameAPI/Packet.zh.md +++ b/docs/apis/GameAPI/Packet.zh.md @@ -177,32 +177,72 @@ - 返回值:是否成功 - 返回值类型: `Boolean` -| 可用函数 | 参数类型 | -| ------------------------------------ | -------------------- | -| writeBool | `Boolean` | -| writeByte | `Integer` | -| writeBytes (0.19.0 时加入) | `ByteBuffer` | -| writeDouble | `Number` / `String` | -| writeFloat | `Float` / `String` | -| writeNormalizedFloat (0.19.0 时加入) | `Float` / `String` | -| writeSignedBigEndianInt | `Number` / `String` | -| writeSignedInt | `Number` / `String` | -| writeSignedInt64 | `Number` / `String` | -| writeSignedShort | `Integer` / `String` | -| writeString | `String` | -| writeUnsignedChar | `Integer` | -| writeUnsignedInt | `Number` / `String` | -| writeUnsignedInt64 | `Number` / `String` | -| writeUnsignedShort | `Integer` / `String` | -| writeUnsignedVarInt | `Number` / `String` | -| writeUnsignedVarInt64 | `Number` / `String` | -| writeVarInt | `Number` / `String` | -| writeVarInt64 | `Number` / `String` | -| writeVec3 | `FloatPos` | -| writeBlockPos (0.9.5 时加入) | `BlockPos` | -| writeCompoundTag | `NbtCompound` | -| writeItem (0.9.5 时加入) | `Item` | -| writeUuid (0.19.0 时加入) | `String` | +| 可用函数 | 参数类型 | +| ---------------------------- | -------------------- | +| writeBool | `Boolean` | +| writeByte | `Integer` / `String` | +| writeBytes (0.19.0 时加入) | `ByteBuffer` | +| writeDouble | `Number` / `String` | +| writeFloat | `Float` / `String` | +| writeSignedBigEndianInt | `Number` / `String` | +| writeSignedInt | `Number` / `String` | +| writeSignedInt64 | `Number` / `String` | +| writeSignedShort | `Integer` / `String` | +| writeString | `String` | +| writeUnsignedChar | `Integer` | +| writeUnsignedInt | `Number` / `String` | +| writeUnsignedInt64 | `Number` / `String` | +| writeUnsignedShort | `Integer` / `String` | +| writeUnsignedVarInt | `Number` / `String` | +| writeUnsignedVarInt64 | `Number` / `String` | +| writeVarInt | `Number` / `String` | +| writeVarInt64 | `Number` / `String` | +| writeVec3 | `FloatPos` | +| writeBlockPos (0.9.5 时加入) | `BlockPos` | +| writeCompoundTag | `NbtCompound` | +| writeItem (0.9.5 时加入) | `Item` | +| writeUuid (0.19.0 时加入) | `String` | + +#### 从二进制流读取 + +!!! warning +此类函数仅在 0.19.0 及以后版本可用。 + +`bs.readxxxx([asString])` +`bs.readBytes(length)` + +其中 `length` 为 `Integer` 类型,仅 `readBytes` 可用,表示要读取的字节数,必须大于 `0`。 + +- 参数: + + - asString : `Boolean`(可选参数) + 仅数字读取函数可用。传入 `true` 时,返回值会转换为 `String` + +- 返回值: + - 读取结果 + +| 可用函数 | 参数类型 | 返回值类型 | +| ---------------------- | --------- | ------------------- | +| readBool | 无 | `Boolean`/ `String` | +| readByte | `Boolean` | `Number` / `String` | +| readBytes | `Integer` | `ByteBuffer` | +| readUnsignedChar | `Boolean` | `Number` / `String` | +| readDouble | `Boolean` | `Number` / `String` | +| readFloat | `Boolean` | `Number` / `String` | +| readSignedBigEndianInt | `Boolean` | `Number` / `String` | +| readSignedInt | `Boolean` | `Number` / `String` | +| readSignedInt64 | `Boolean` | `Number` / `String` | +| readSignedShort | `Boolean` | `Number` / `String` | +| readString | 无 | `String` | +| readUnsignedInt | `Boolean` | `Number` / `String` | +| readUnsignedInt64 | `Boolean` | `Number` / `String` | +| readUnsignedShort | `Boolean` | `Number` / `String` | +| readUnsignedVarInt | `Boolean` | `Number` / `String` | +| readUnsignedVarInt64 | `Boolean` | `Number` / `String` | +| readVarInt | `Boolean` | `Number` / `String` | +| readVarInt64 | `Boolean` | `Number` / `String` | + +当 `asString` 为 `true` 时,数字 `read*` 函数返回 `String`;否则返回 `Number`。 #### 通过二进制流构建数据包 diff --git a/docs/apis/SystemAPI/File.md b/docs/apis/SystemAPI/File.md index 934b1b3a..44b97513 100644 --- a/docs/apis/SystemAPI/File.md +++ b/docs/apis/SystemAPI/File.md @@ -14,15 +14,24 @@ If you need to manipulate files frequently, use the file classes below to improv ### Read in All the Contents of the File -`File.readFrom(path)` +!!! warning +The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.0 and later. + +`File.readFrom(path[,isBinary])` - Parameter: - path : `String` The path of the target file, the relative path is based on the BDS root directory. + - isBinary : `Boolean` + Whether to read in binary mode. The default value is `false` - Return value: All data in the file -- Return value type: `String` +- Return value type: `String` / `ByteBuffer` - If the return value is `Null`, the read failed. +If `isBinary` is `true`, this function returns `ByteBuffer`; otherwise it returns `String`. + +Before 0.19.0, this function could only be used as `File.readFrom(path)`, and the return value type was always `String`. + ### Write Content to the Specified File @@ -33,7 +42,7 @@ If you need to manipulate files frequently, use the file classes below to improv - path : `String` The path of the target file, the relative path is based on the BDS root directory. - - text : `String` + - text : `String` / `ByteBuffer` What will be written to the file. - Return value: Whether the write is successful or not. @@ -42,6 +51,9 @@ If you need to manipulate files frequently, use the file classes below to improv > Note: If the file does not exist, it will be created automatically. If it exists, it will be **emptied** before writing. +!!! warning +Passing `ByteBuffer` is only available in 0.19.0 and later. + ### Append a Line to the Specified File diff --git a/docs/apis/SystemAPI/File.zh.md b/docs/apis/SystemAPI/File.zh.md index 10100a20..97857170 100644 --- a/docs/apis/SystemAPI/File.zh.md +++ b/docs/apis/SystemAPI/File.zh.md @@ -14,15 +14,24 @@ ### 读入文件的所有内容 -`File.readFrom(path)` +!!! warning + 此函数的可选参数和 `ByteBuffer` 返回值仅在0.19.0及以后版本可用 + +`File.readFrom(path[,isBinary])` - 参数: - path : `String` 目标文件的路径,相对路径以BDS根目录为基准 + - isBinary : `Boolean` + 是否按二进制方式读取,默认为`false` - 返回值:文件的所有数据 -- 返回值类型:`String` +- 返回值类型:`String` / `ByteBuffer` - 如返回值为 `Null` 则表示读取失败 +如果 `isBinary` 为 `true`,则返回 `ByteBuffer`,否则返回 `String`。 + +在0.19.0之前,此函数只能使用 `File.readFrom(path)` 形式调用,返回值类型固定为 `String`。 + ### 向指定文件写入内容 @@ -33,7 +42,7 @@ - path : `String` 目标文件的路径,相对路径以BDS根目录为基准 - - text : `String` + - text : `String` / `ByteBuffer` 要写入的内容 - 返回值:是否写入成功 @@ -42,6 +51,9 @@ > 注:若文件不存在会自动创建,若存在则会先将其**清空**再写入 +!!! warning + 传入 `ByteBuffer` 仅在0.19.0及以后版本可用 + ### 向指定文件追加一行 diff --git a/docs/apis/SystemAPI/SystemInfo.md b/docs/apis/SystemAPI/SystemInfo.md index b0192efb..81c520b0 100644 --- a/docs/apis/SystemAPI/SystemInfo.md +++ b/docs/apis/SystemAPI/SystemInfo.md @@ -42,3 +42,15 @@ The following APIs provide interfaces to obtain necessary system information: - Return value type: `String` + +### Randomly Generate a UUID String + +!!! warning +This function is only available in 0.19.0 and later. + +`system.randomUuid()` + +- Return value: A randomly generated unique identifier UUID. +- Return value type: `String` + + diff --git a/docs/apis/SystemAPI/SystemInfo.zh.md b/docs/apis/SystemAPI/SystemInfo.zh.md index 07a3d1de..8692c5eb 100644 --- a/docs/apis/SystemAPI/SystemInfo.zh.md +++ b/docs/apis/SystemAPI/SystemInfo.zh.md @@ -42,3 +42,15 @@ - 返回值类型: `String` + +### 随机生成一个 UUID 字符串 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`system.randomUuid()` + +- 返回值:一个随机生成的唯一标识符UUID +- 返回值类型: `String` + + diff --git a/src/legacy/api/EntityAPI.cpp b/src/legacy/api/EntityAPI.cpp index 181fde20..cb34c4d5 100644 --- a/src/legacy/api/EntityAPI.cpp +++ b/src/legacy/api/EntityAPI.cpp @@ -12,6 +12,7 @@ #include "ll/api/service/Bedrock.h" #include "lse/api/MoreGlobal.h" #include "lse/api/helper/AttributeHelper.h" +#include "mc/common/Globals.h" #include "mc/deps/core/math/Vec2.h" #include "mc/deps/nbt/CompoundTag.h" #include "mc/deps/shared_types/legacy/actor/ActorDamageCause.h" @@ -39,7 +40,7 @@ #include "mc/world/actor/provider/ActorEquipment.h" #include "mc/world/actor/provider/SynchedActorDataAccess.h" #include "mc/world/attribute/Attribute.h" -#include "mc/world/attribute/AttributeInstance.h" // IWYU pragma: keep +#include "mc/world/attribute/AttributeInstance.h" // IWYU pragma: keep #include "mc/world/attribute/AttributeInstanceHandle.h" // IWYU pragma: keep #include "mc/world/attribute/SharedAttributes.h" #include "mc/world/effect/EffectDuration.h" @@ -56,6 +57,7 @@ #include #include +#include using lse::api::AttributeHelper; using magic_enum::enum_integer; @@ -137,6 +139,7 @@ ClassDefine EntityClassBuilder = .instanceFunction("getContainer", &EntityClass::getContainer) .instanceFunction("refreshItems", &EntityClass::refreshItems) .instanceFunction("setScale", &EntityClass::setScale) + .instanceFunction("setCustomName", &EntityClass::setCustomName) .instanceFunction("setNbt", &EntityClass::setNbt) .instanceFunction("getNbt", &EntityClass::getNbt) .instanceFunction("addTag", &EntityClass::addTag) @@ -1292,6 +1295,20 @@ Local EntityClass::setScale(Arguments const& args) const { CATCH_AND_THROW } +Local EntityClass::setCustomName(Arguments const& args) const { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + + try { + Actor* entity = get(); + if (!entity) return {}; + + entity->setNameTag(args[0].asString().toString()); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + Local EntityClass::getNbt(Arguments const&) const { try { Actor const* entity = get(); @@ -1765,6 +1782,103 @@ Local McClass::spawnMob(Arguments const& args) { CATCH_AND_THROW } +Local McClass::summonMob(Arguments const& args) { + CHECK_ARGS_COUNT(args, 2); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + using namespace ll::memory_literals; + + try { + size_t paramIndex{0}; + + ActorDefinitionIdentifier name{args[paramIndex++].asString().toString()}; + if (EntityTypeFromString(name.mFullName) == ActorType::Player) return {}; + + FloatVec4 pos; + if (IsInstanceOf(args[paramIndex])) { + pos = *IntPos::extractPos(args[paramIndex++]); + } else if (IsInstanceOf(args[paramIndex])) { + pos = *FloatPos::extractPos(args[paramIndex++]); + } else if (args.size() >= 5 && std::ranges::all_of(std::views::iota(0ull, 4ull), [&](auto index) { + return args[paramIndex + index].getKind() == ValueKind::kNumber; + })) { + pos = { + args[paramIndex++].asNumber().toFloat(), + args[paramIndex++].asNumber().toFloat(), + args[paramIndex++].asNumber().toFloat(), + args[paramIndex++].asNumber().toInt32() + }; + } else { + throw WrongArgTypeException(__FUNCTION__); + } + + if (args.size() - paramIndex >= 1) { + name.mInitEvent = args[paramIndex++].asString().toString(); + name._initialize(); + } + + static auto func = reinterpret_cast< + Actor* (*)(BlockSource&, Vec3 const&, ActorDefinitionIdentifier const&, ActorUniqueID&, Actor*)>( + "`anonymous namespace'::CommandUtilsAnon::_spawnEntityAt"_sym.resolve() + ); + + auto dimension = ll::service::getLevel()->getDimension(pos.dim); + if (dimension.expired()) return {}; + ActorUniqueID uniqueId{}; + auto* entity = + func(dimension.lock()->getBlockSourceFromMainChunkSource(), pos.getVec3(), name, uniqueId, nullptr); + + if (!entity || entity->mRemoved) return {}; + + if (auto type = std::to_underlying(entity->getEntityTypeId()); + (type & std::to_underlying(ActorType::Mob)) != 0 || type - 10 <= 0x35) { + entity->setPersistent(); + } + + return EntityClass::newEntity(entity); + } + CATCH_AND_THROW +} + +Local McClass::loadMob(Arguments const& args) { + CHECK_ARGS_COUNT(args, 2); + if (!IsInstanceOf(args[0])) { + throw WrongArgTypeException(__FUNCTION__); + } + + try { + FloatVec4 pos; + if (IsInstanceOf(args[1])) { + pos = *IntPos::extractPos(args[1]); + } else if (IsInstanceOf(args[1])) { + pos = *FloatPos::extractPos(args[1]); + } else if (args.size() >= 5 && std::ranges::all_of(std::views::iota(1ull, 5ull), [&](auto index) { + return args[index].getKind() == ValueKind::kNumber; + })) { + pos = { + args[1].asNumber().toFloat(), + args[2].asNumber().toFloat(), + args[3].asNumber().toFloat(), + args[4].asNumber().toInt32() + }; + } else { + throw WrongArgTypeException(__FUNCTION__); + } + + auto dimension = ll::service::getLevel()->getDimension(pos.dim); + if (dimension.expired()) return {}; + + if (auto* nbt = NbtCompoundClass::extract(args[0]); nbt) { + auto backup = *nbt; + backup["Pos"] = ListTag{pos.x, pos.y, pos.z}; + if (auto entity = dimension.lock()->getBlockSourceFromMainChunkSource().spawnActor(backup); entity) { + return EntityClass::newEntity(entity.as_ptr()); + } + } + return {}; + } + CATCH_AND_THROW +} + Local McClass::explode(Arguments const& args) { CHECK_ARGS_COUNT(args, 5); diff --git a/src/legacy/api/EntityAPI.h b/src/legacy/api/EntityAPI.h index 5e3bbbf2..fdaf93fb 100644 --- a/src/legacy/api/EntityAPI.h +++ b/src/legacy/api/EntityAPI.h @@ -83,6 +83,7 @@ class EntityClass : public ScriptClass { Local setFire(Arguments const& args) const; Local stopFire(Arguments const& args) const; Local setScale(Arguments const& args) const; + Local setCustomName(Arguments const& args) const; Local distanceTo(Arguments const& args) const; Local distanceToSqr(Arguments const& args) const; diff --git a/src/legacy/api/FileSystemAPI.cpp b/src/legacy/api/FileSystemAPI.cpp index c79d9ec2..bc758a4f 100644 --- a/src/legacy/api/FileSystemAPI.cpp +++ b/src/legacy/api/FileSystemAPI.cpp @@ -624,11 +624,15 @@ Local GetFilesList(Arguments const& args) { Local FileReadFrom(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); + if (args.size() >= 2) { + CHECK_ARG_TYPE(args[1], ValueKind::kBoolean); + } try { - auto content = ll::file_utils::readFile(args[0].asString().toU8string()); + auto isBinary = args.size() >= 2 && args[1].asBoolean().value(); + auto content = ll::file_utils::readFile(args[0].asString().toU8string(), isBinary); if (!content) return {}; // Null - return String::newString(content.value()); + return isBinary ? ByteBuffer::newByteBuffer(content->data(), content->size()).asValue() : String::newString(content.value()); } CATCH_AND_THROW } @@ -636,7 +640,7 @@ Local FileReadFrom(Arguments const& args) { Local FileWriteTo(Arguments const& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); - CHECK_ARG_TYPE(args[1], ValueKind::kString); + CHECK_ARG_TYPE(args[1], ValueKind::kString && args[1].getKind() != ValueKind::kByteBuffer); try { std::filesystem::path path(args[0].asString().toU8string()); @@ -655,7 +659,15 @@ Local FileWriteTo(Arguments const& args) { "Fail to create directory of " + args[0].asString().toString() + "!" ); } - return Boolean::newBoolean(ll::file_utils::writeFile(path, args[1].asString().toString(), false)); + std::string content; + if (args[1].isString()) { + content = args[1].asString().toString(); + } else { + auto data = args[1].asByteBuffer(); + content = std::string_view{reinterpret_cast(data.getRawBytes()), data.byteLength()}; + } + + return Boolean::newBoolean(ll::file_utils::writeFile(path, content, false)); } CATCH_AND_THROW } diff --git a/src/legacy/api/ItemAPI.cpp b/src/legacy/api/ItemAPI.cpp index e99ea312..05abdebe 100644 --- a/src/legacy/api/ItemAPI.cpp +++ b/src/legacy/api/ItemAPI.cpp @@ -66,6 +66,9 @@ ClassDefine ItemClassBuilder = defineClass("LLSE_Item") .instanceFunction("getNbt", &ItemClass::getNbt) .instanceFunction("match", &ItemClass::match) + .instanceFunction("addCount", &ItemClass::addCount) + .instanceFunction("removeCount", &ItemClass::removeCount) + .instanceFunction("setCount", &ItemClass::setCount) // For Compatibility .instanceFunction("setTag", &ItemClass::setNbt) @@ -394,9 +397,8 @@ Local ItemClass::setLore(Arguments const& args) const { auto value = arr.get(i); if (value.getKind() == ValueKind::kString) lores.push_back(value.asString().toString()); } - if (lores.empty()) return Boolean::newBoolean(false); - get()->setCustomLore(lores); + lores.empty() ? get()->clearCustomLore() : get()->setCustomLore(lores); return Boolean::newBoolean(true); } CATCH_AND_THROW @@ -545,3 +547,42 @@ Local ItemClass::match(Arguments const& args) const { } CATCH_AND_THROW } + +Local ItemClass::addCount(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + + try { + get()->add(args[0].asNumber().toInt32()); + // update Pre Data + preloadData(); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + +Local ItemClass::removeCount(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + + try { + get()->remove(args[0].asNumber().toInt32()); + // update Pre Data + preloadData(); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + +Local ItemClass::setCount(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + + try { + get()->set(args[0].asNumber().toInt32()); + // update Pre Data + preloadData(); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} \ No newline at end of file diff --git a/src/legacy/api/ItemAPI.h b/src/legacy/api/ItemAPI.h index 105275bf..0fd7af99 100644 --- a/src/legacy/api/ItemAPI.h +++ b/src/legacy/api/ItemAPI.h @@ -73,6 +73,9 @@ class ItemClass : public ScriptClass { Local setNbt(Arguments const& args); Local match(Arguments const& args) const; + Local addCount(Arguments const& args); + Local removeCount(Arguments const& args); + Local setCount(Arguments const& args); }; extern ClassDefine ItemClassBuilder; diff --git a/src/legacy/api/McAPI.cpp b/src/legacy/api/McAPI.cpp index 7f276b80..8818cc84 100644 --- a/src/legacy/api/McAPI.cpp +++ b/src/legacy/api/McAPI.cpp @@ -16,6 +16,8 @@ ClassDefine McClassBuilder = defineClass("mc") .function("getEntity", McClass::getEntity) .function("newItem", &McClass::newItem) .function("spawnMob", &McClass::spawnMob) + .function("summonMob", &McClass::summonMob) + .function("loadMob", &McClass::loadMob) .function("cloneMob", &McClass::cloneMob) .function("spawnItem", &McClass::spawnItem) .function("spawnSimulatedPlayer", &McClass::spawnSimulatedPlayer) diff --git a/src/legacy/api/McAPI.h b/src/legacy/api/McAPI.h index b506325d..61a497fb 100644 --- a/src/legacy/api/McAPI.h +++ b/src/legacy/api/McAPI.h @@ -22,6 +22,8 @@ class McClass { static Local newItem(Arguments const& args); static Local spawnMob(Arguments const& args); + static Local summonMob(Arguments const& args); + static Local loadMob(Arguments const& args); static Local spawnItem(Arguments const& args); static Local spawnSimulatedPlayer(Arguments const& args); static Local explode(Arguments const& args); diff --git a/src/legacy/api/PacketAPI.cpp b/src/legacy/api/PacketAPI.cpp index 7fa76dea..5fa4bbd2 100644 --- a/src/legacy/api/PacketAPI.cpp +++ b/src/legacy/api/PacketAPI.cpp @@ -13,6 +13,9 @@ #include "mc/network/Packet.h" #include "mc/world/item/NetworkItemStackDescriptor.h" +#include +#include + //////////////////// Class Definition //////////////////// ClassDefine PacketClassBuilder = defineClass("LLSE_Packet") @@ -39,7 +42,6 @@ ClassDefine BinaryStreamClassBuilder = .instanceFunction("writeBytes", &BinaryStreamClass::writeBytes) .instanceFunction("writeDouble", &BinaryStreamClass::writeDouble) .instanceFunction("writeFloat", &BinaryStreamClass::writeFloat) - .instanceFunction("writeNormalizedFloat", &BinaryStreamClass::writeNormalizedFloat) .instanceFunction("writeSignedBigEndianInt", &BinaryStreamClass::writeSignedBigEndianInt) .instanceFunction("writeSignedInt", &BinaryStreamClass::writeSignedInt) .instanceFunction("writeSignedInt64", &BinaryStreamClass::writeSignedInt64) @@ -58,6 +60,24 @@ ClassDefine BinaryStreamClassBuilder = .instanceFunction("writeCompoundTag", &BinaryStreamClass::writeCompoundTag) .instanceFunction("writeItem", &BinaryStreamClass::writeItem) .instanceFunction("writeUuid", &BinaryStreamClass::writeUuid) + .instanceFunction("readBool", &BinaryStreamClass::readBool) + .instanceFunction("readByte", &BinaryStreamClass::readByte) + .instanceFunction("readBytes", &BinaryStreamClass::readBytes) + .instanceFunction("readUnsignedChar", &BinaryStreamClass::readByte) + .instanceFunction("readDouble", &BinaryStreamClass::readDouble) + .instanceFunction("readFloat", &BinaryStreamClass::readFloat) + .instanceFunction("readSignedBigEndianInt", &BinaryStreamClass::readSignedBigEndianInt) + .instanceFunction("readSignedInt", &BinaryStreamClass::readSignedInt) + .instanceFunction("readSignedInt64", &BinaryStreamClass::readSignedInt64) + .instanceFunction("readSignedShort", &BinaryStreamClass::readSignedShort) + .instanceFunction("readString", &BinaryStreamClass::readString) + .instanceFunction("readUnsignedInt", &BinaryStreamClass::readUnsignedInt) + .instanceFunction("readUnsignedInt64", &BinaryStreamClass::readUnsignedInt64) + .instanceFunction("readUnsignedShort", &BinaryStreamClass::readUnsignedShort) + .instanceFunction("readUnsignedVarInt", &BinaryStreamClass::readUnsignedVarInt) + .instanceFunction("readUnsignedVarInt64", &BinaryStreamClass::readUnsignedVarInt64) + .instanceFunction("readVarInt", &BinaryStreamClass::readVarInt) + .instanceFunction("readVarInt64", &BinaryStreamClass::readVarInt64) .instanceFunction("createPacket", &BinaryStreamClass::createPacket) .build(); @@ -168,6 +188,71 @@ Local PacketClass::sendToServer(Arguments const& args) { //////////////////// BinaryStream Classes //////////////////// +template +auto parseScalarArg(Arguments const& args, std::string_view func) { + CHECK_ARGS_COUNT(args, 1); + + auto const& value = args[0]; + if constexpr (ll::traits::is_string_v) { + if (value.isString()) { + return value.asString().toString(); + } + } else { + if (value.isBoolean()) { + return static_cast(value.asBoolean().value()); + } + if (value.isNumber()) { + if (auto num = value.asNumber(); num.isInteger()) { + return static_cast(value.asNumber().toInt64()); + } else { + return static_cast(value.asNumber().toDouble()); + } + } + if (value.isString()) { + if constexpr (std::is_same_v) { + if (auto res = ll::string_utils::svtobool(value.asString().toString()); res) { + return res.value(); + } + } else if constexpr (std::is_floating_point_v) { + if (auto res = + ll::string_utils::svtonum(value.asString().toString(), nullptr, std::chars_format::general); + res) { + return res.value(); + } + } else { + if (auto res = ll::string_utils::svtonum(value.asString().toString(), nullptr, 10); res) { + return res.value(); + } + } + } + } + throw WrongArgTypeException(std::string{func}); +} + +template +Local makeScalarResult(Bedrock::Result&& res, char const* func, bool asString) { + if (!res) { + throw Exception(fmt::format("{}\nfunction: {}", res.error().code().message(), func)); + } + auto& value = res.value(); + if (asString) return String::newString(fmt::to_string(value)); + + if constexpr (std::is_same_v) { + return Boolean::newBoolean(res.value()); + } else if constexpr (std::is_same_v) { + return String::newString(res.value()); + } else if constexpr (std::is_floating_point_v) { + return Number::newNumber(static_cast(value)); + } else if constexpr (std::is_unsigned_v) { + if (value <= static_cast(std::numeric_limits::max())) { + return Number::newNumber(static_cast(value)); + } + return Number::newNumber(static_cast(value)); + } else { + return Number::newNumber(static_cast(value)); + } +} + BinaryStreamClass::BinaryStreamClass(std::shared_ptr const& bs) : ScriptClass(ConstructFromCpp{}) { set(bs); @@ -280,34 +365,6 @@ Local BinaryStreamClass::reserve(Arguments const& args) { CATCH_AND_THROW } -Local BinaryStreamClass::writeBool(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kBoolean); - try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeBool(args[0].asBoolean().value(), nullptr, nullptr); - return Boolean::newBoolean(true); - } - CATCH_AND_THROW -} - -Local BinaryStreamClass::writeByte(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeByte(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); - return Boolean::newBoolean(true); - } - CATCH_AND_THROW -} - Local BinaryStreamClass::writeBytes(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kByteBuffer); @@ -324,68 +381,77 @@ Local BinaryStreamClass::writeBytes(Arguments const& args) { CATCH_AND_THROW } -Local BinaryStreamClass::writeString(Arguments const& args) { +Local BinaryStreamClass::readBytes(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kString); + CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + + auto length = args[0].asNumber().toInt32(); + if (length <= 0) throw WrongArgTypeException(__FUNCTION__); try { auto stream = get(); if (!stream) { return {}; } - stream->writeString(args[0].asString().toString(), nullptr, nullptr); - return Boolean::newBoolean(true); + + std::string buffer(length, '\0'); + if (auto res = stream->read(buffer.data(), length); !res) { + throw Exception(fmt::format("{}\nfunction: {}", res.error().code().message(), __func__)); + } + return ByteBuffer::newByteBuffer(buffer.data(), length); } CATCH_AND_THROW } -#define WRITE_MACRO(FUNC_NAME, INDEX) \ - Local BinaryStreamClass::FUNC_NAME(Arguments const& args) { \ - CHECK_ARGS_COUNT(args, 1); \ - CHECK_ARG_TYPE(args[0], ValueKind::kNumber && args[0].getKind() != ValueKind::kString); \ +#define SCALAR_STREAM_MACRO(NAME, ...) \ + Local BinaryStreamClass::write##NAME(Arguments const& args) { \ try { \ auto stream = get(); \ if (!stream) { \ return {}; \ } \ - using T = ll::traits::function_traits::arg<0>; \ - T value{}; \ - if (args[0].isNumber()) { \ - value = static_cast(args[0].asNumber().toInt64()); \ - } else if (args[0].isString()) { \ - if (auto res = ll::string_utils::svtonum(args[0].asString().toString(), nullptr, INDEX); res) { \ - value = res.value(); \ - } else { \ - throw WrongArgTypeException(__FUNCTION__); \ - } \ - } \ - stream->FUNC_NAME(value, nullptr, nullptr); \ + auto value = parseScalarArg::arg<0>>( \ + args, \ + __FUNCTION__ \ + ); \ + stream->write##NAME(value, nullptr, nullptr); \ return Boolean::newBoolean(true); \ } \ CATCH_AND_THROW \ + } \ + Local BinaryStreamClass::read##NAME(Arguments const& args) { \ + if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kBoolean); \ + try { \ + auto stream = get(); \ + if (!stream) { \ + return {}; \ + } \ + return makeScalarResult( \ + stream->get##NAME(__VA_ARGS__), \ + __FUNCTION__, \ + args.size() >= 1 ? args[0].asBoolean().value() : false \ + ); \ + } \ + CATCH_AND_THROW \ } -#define WRITE_INTEGER_MACRO(FUNC_NAME) WRITE_MACRO(FUNC_NAME, 10) -#define WRITE_FLOAT_MACRO(FUNC_NAME) WRITE_MACRO(FUNC_NAME, std::chars_format::general) - -WRITE_INTEGER_MACRO(writeSignedBigEndianInt); -WRITE_INTEGER_MACRO(writeSignedInt); -WRITE_INTEGER_MACRO(writeSignedInt64); -WRITE_INTEGER_MACRO(writeSignedShort); -WRITE_INTEGER_MACRO(writeUnsignedInt); -WRITE_INTEGER_MACRO(writeUnsignedInt64); -WRITE_INTEGER_MACRO(writeUnsignedShort); -WRITE_INTEGER_MACRO(writeUnsignedVarInt); -WRITE_INTEGER_MACRO(writeUnsignedVarInt64); -WRITE_INTEGER_MACRO(writeVarInt); -WRITE_INTEGER_MACRO(writeVarInt64); - -WRITE_FLOAT_MACRO(writeFloat) -WRITE_FLOAT_MACRO(writeDouble) -WRITE_FLOAT_MACRO(writeNormalizedFloat) - -#undef WRITE_MACRO -#undef WRITE_INTEGER_MACRO -#undef WRITE_FLOAT_MACRO +SCALAR_STREAM_MACRO(Bool); +SCALAR_STREAM_MACRO(Byte); +SCALAR_STREAM_MACRO(Double); +SCALAR_STREAM_MACRO(Float); +SCALAR_STREAM_MACRO(SignedBigEndianInt); +SCALAR_STREAM_MACRO(SignedInt); +SCALAR_STREAM_MACRO(SignedInt64); +SCALAR_STREAM_MACRO(SignedShort); +SCALAR_STREAM_MACRO(String, std::numeric_limits::max()); +SCALAR_STREAM_MACRO(UnsignedInt); +SCALAR_STREAM_MACRO(UnsignedInt64); +SCALAR_STREAM_MACRO(UnsignedShort); +SCALAR_STREAM_MACRO(UnsignedVarInt); +SCALAR_STREAM_MACRO(UnsignedVarInt64); +SCALAR_STREAM_MACRO(VarInt); +SCALAR_STREAM_MACRO(VarInt64); + +#undef SCALAR_STREAM_MACRO Local BinaryStreamClass::writeVec3(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); @@ -500,7 +566,7 @@ Local BinaryStreamClass::createPacket(Arguments const& args) { ); if (auto res = pkt->read(*stream); !res) { - throw Exception(fmt ::format("{}\nfunction: {}", res.error().code().message(), __func__)); + throw Exception(fmt::format("{}\nfunction: {}", res.error().code().message(), __func__)); } return PacketClass::newPacket(pkt); } diff --git a/src/legacy/api/PacketAPI.h b/src/legacy/api/PacketAPI.h index a814aa51..aa4e8fbd 100644 --- a/src/legacy/api/PacketAPI.h +++ b/src/legacy/api/PacketAPI.h @@ -50,15 +50,14 @@ class BinaryStreamClass : public ScriptClass { Local setReadPointer(Arguments const& args); Local getData(Arguments const& args); Local setData(Arguments const& args); + Local reserve(Arguments const& args); Local reset(); - Local reserve(Arguments const& args); Local writeBool(Arguments const& args); Local writeByte(Arguments const& args); Local writeBytes(Arguments const& args); Local writeDouble(Arguments const& args); Local writeFloat(Arguments const& args); - Local writeNormalizedFloat(Arguments const& args); Local writeSignedBigEndianInt(Arguments const& args); Local writeSignedInt(Arguments const& args); Local writeSignedInt64(Arguments const& args); @@ -77,6 +76,24 @@ class BinaryStreamClass : public ScriptClass { Local writeItem(Arguments const& args); Local writeUuid(Arguments const& args); + Local readBool(Arguments const& args); + Local readByte(Arguments const& args); + Local readBytes(Arguments const& args); + Local readDouble(Arguments const& args); + Local readFloat(Arguments const& args); + Local readSignedBigEndianInt(Arguments const& args); + Local readSignedInt(Arguments const& args); + Local readSignedInt64(Arguments const& args); + Local readSignedShort(Arguments const& args); + Local readString(Arguments const& args); + Local readUnsignedInt(Arguments const& args); + Local readUnsignedInt64(Arguments const& args); + Local readUnsignedShort(Arguments const& args); + Local readUnsignedVarInt(Arguments const& args); + Local readUnsignedVarInt64(Arguments const& args); + Local readVarInt(Arguments const& args); + Local readVarInt64(Arguments const& args); + Local createPacket(Arguments const& args); }; extern ClassDefine BinaryStreamClassBuilder; diff --git a/src/legacy/api/SystemAPI.cpp b/src/legacy/api/SystemAPI.cpp index 8e5190ce..ebf6373b 100644 --- a/src/legacy/api/SystemAPI.cpp +++ b/src/legacy/api/SystemAPI.cpp @@ -19,6 +19,7 @@ ClassDefine SystemClassBuilder = defineClass("system") .function("getTimeStr", &SystemClass::getTimeStr) .function("getTimeObj", &SystemClass::getTimeObj) .function("randomGuid", &SystemClass::randomGuid) + .function("randomUuid", &SystemClass::randomUuid) .function("cmd", &SystemClass::cmd) .function("newProcess", &SystemClass::newProcess) .build(); @@ -192,3 +193,5 @@ Local SystemClass::getTimeObj(Arguments const&) { } Local SystemClass::randomGuid(Arguments const&) { return String::newString(Raw_RandomGuid()); } + +Local SystemClass::randomUuid(Arguments const&) { return String::newString(mce::UUID::random().asString()); } diff --git a/src/legacy/api/SystemAPI.h b/src/legacy/api/SystemAPI.h index 360d73e7..85a9fb64 100644 --- a/src/legacy/api/SystemAPI.h +++ b/src/legacy/api/SystemAPI.h @@ -8,6 +8,7 @@ class SystemClass { static Local getTimeStr(Arguments const& args); static Local getTimeObj(Arguments const& args); static Local randomGuid(Arguments const& args); + static Local randomUuid(Arguments const& args); static Local cmd(Arguments const& args); static Local newProcess(Arguments const& args); diff --git a/src/legacy/utils/IniHelper.cpp b/src/legacy/utils/IniHelper.cpp index 3c56c20f..e9b58a40 100644 --- a/src/legacy/utils/IniHelper.cpp +++ b/src/legacy/utils/IniHelper.cpp @@ -1,5 +1,6 @@ #include "legacy/utils/IniHelper.h" +#include "ll/api/base/Macro.h" #include "ll/api/io/Logger.h" #include "lse/Entry.h" diff --git a/xmake.lua b/xmake.lua index faf9edc0..7134b8fe 100644 --- a/xmake.lua +++ b/xmake.lua @@ -87,7 +87,8 @@ target("LegacyScriptEngine") add_defines( "NOMINMAX", "UNICODE", - "_AMD64_" + "_AMD64_", + "_HAS_CXX23=1" ) add_packages( "cpp-httplib", From d74dd0f0e22daa5ea3b3b82d3826d3b8e2b74ea7 Mon Sep 17 00:00:00 2001 From: xianyubb <2149656630@qq.com> Date: Tue, 30 Jun 2026 14:29:46 +0800 Subject: [PATCH 05/17] feat(legacy api): add VaillanI18n internationalization api --- .trae/rules/git-commit-message.md | 7 ++ src/legacy/api/VaillanI18n.cpp | 106 ++++++++++++++++++++++++++++++ src/legacy/api/VaillanI18n.h | 21 ++++++ 3 files changed, 134 insertions(+) create mode 100644 .trae/rules/git-commit-message.md create mode 100644 src/legacy/api/VaillanI18n.cpp create mode 100644 src/legacy/api/VaillanI18n.h diff --git a/.trae/rules/git-commit-message.md b/.trae/rules/git-commit-message.md new file mode 100644 index 00000000..78d1a197 --- /dev/null +++ b/.trae/rules/git-commit-message.md @@ -0,0 +1,7 @@ +--- +alwaysApply: true +scene: git_message +language: English +--- + +在此处编写规则,自定义 AI 生成提交信息的风格。 diff --git a/src/legacy/api/VaillanI18n.cpp b/src/legacy/api/VaillanI18n.cpp new file mode 100644 index 00000000..5b82b6b6 --- /dev/null +++ b/src/legacy/api/VaillanI18n.cpp @@ -0,0 +1,106 @@ +#include "VaillanI18n.h" + +#include +#include + +ClassDefine VaillanI18nClassBuilder = + defineClass("VaillanI18n") + .function("setCurrentLanguage", &VaillanI18nClass::setCurrentLanguage) + .function("getCurrentLanguage", &VaillanI18nClass::getCurrentLanguage) + .function("getSupportedLanguages", &VaillanI18nClass::getSupportedLanguages) + .function("translate", &VaillanI18nClass::translate) + .function("loadLanguage", &VaillanI18nClass::loadLanguage) + .function("loadLanguageFromFile", &VaillanI18nClass::loadLanguageFromFile) + .function("loadLanguagesFromDirectory", &VaillanI18nClass::loadLanguagesFromDirectory) + .build(); + +Local VaillanI18nClass::setCurrentLanguage(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + std::string lang = args[0].asString().toString(); + try { + getI18n().chooseLanguage(lang); + } + CATCH_AND_THROW + return Boolean::newBoolean(true); +} + +Local VaillanI18nClass::getCurrentLanguage(Arguments const& args) { + CHECK_ARGS_COUNT(args, 0); + try { + return String::newString(*getI18n().getCurrentLanguage()->mCode); + } + CATCH_AND_THROW +} + +Local VaillanI18nClass::getSupportedLanguages(Arguments const& args) { + CHECK_ARGS_COUNT(args, 0); + try { + auto arr = Array::newArray(); + for (auto& lang : getI18n().getSupportedLanguageCodes()) { + arr.add(String::newString(lang)); + } + return arr; + } + CATCH_AND_THROW +} + +Local VaillanI18nClass::translate(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + + std::string key = args[0].asString().toString(); + std::string lang; + + std::vector params; + if (args.size() >= 2) { + CHECK_ARG_TYPE(args[1], ValueKind::kArray); + auto arr = args[1].asArray(); + for (size_t i = 0; i < arr.size(); i++) { + CHECK_ARG_TYPE(arr.get(i), ValueKind::kString); + params.push_back(arr.get(i).asString().toString()); + } + } + + if (args.size() >= 3) { + CHECK_ARG_TYPE(args[2], ValueKind::kString); + lang = args[2].asString().toString(); + } + + try { + return String::newString(getI18n().get(key, params, getI18n().getLocaleFor(lang))); + } + CATCH_AND_THROW +} + +Local VaillanI18nClass::loadLanguage(Arguments const& args) { + CHECK_ARGS_COUNT(args, 2); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + CHECK_ARG_TYPE(args[1], ValueKind::kObject); + std::string lang = args[0].asString().toString(); + auto obj = args[1].asObject(); + + try { + std::unordered_map map; + for (auto& prop : obj.getKeys()) { + CHECK_ARG_TYPE(obj.get(prop), ValueKind::kString); + map[prop.toString()] = obj.get(prop).asString().toString(); + } + auto loc = std::const_pointer_cast(getI18n().getLocaleFor(lang)); + if (!loc) getI18n().appendAdditionalTranslations(map, lang); + else + for (auto const& [key, value] : map) loc->mStrings->insert_or_assign(key, value); + } + CATCH_AND_THROW + return Boolean::newBoolean(true); +} + +Local VaillanI18nClass::loadLanguageFromFile(Arguments const& args) { + // TODO: Load language from file + return Boolean::newBoolean(true); +} + +Local VaillanI18nClass::loadLanguagesFromDirectory(Arguments const& args) { + // TODO: Load languages from directory + return Boolean::newBoolean(true); +} \ No newline at end of file diff --git a/src/legacy/api/VaillanI18n.h b/src/legacy/api/VaillanI18n.h new file mode 100644 index 00000000..1022e78c --- /dev/null +++ b/src/legacy/api/VaillanI18n.h @@ -0,0 +1,21 @@ +#pragma once +#include "legacy/api/APIHelp.h" + +class VaillanI18nClass { +public: + static Local setCurrentLanguage(Arguments const& args); + + static Local getCurrentLanguage(Arguments const& args); + + static Local getSupportedLanguages(Arguments const& args); + + static Local translate(Arguments const& args); + + static Local loadLanguage(Arguments const& args); + + static Local loadLanguageFromFile(Arguments const& args); + + static Local loadLanguagesFromDirectory(Arguments const& args); +}; + +extern ClassDefine<> VaillanI18nClassBuilder; From a6b5050e30fbb6b33c46c2ee9b60f9c382f984a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Tue, 30 Jun 2026 21:07:24 +0800 Subject: [PATCH 06/17] feat: add some apis --- .trae/rules/git-commit-message.md | 7 --- docs/apis/GameAPI/Packet.md | 10 +-- docs/apis/GameAPI/Packet.zh.md | 8 +-- docs/apis/ScriptAPI/VanillaI18n.md | 78 +++++++++++++++++++++++ docs/apis/ScriptAPI/VanillaI18n.zh.md | 78 +++++++++++++++++++++++ src/legacy/api/PacketAPI.cpp | 80 +++++++++++++++++++++++- src/legacy/api/PacketAPI.h | 15 +++-- src/legacy/api/VaillanI18n.cpp | 89 ++++++++++++++++++++++----- src/legacy/api/VaillanI18n.h | 12 ++-- src/legacy/main/BindAPIs.cpp | 2 + 10 files changed, 332 insertions(+), 47 deletions(-) delete mode 100644 .trae/rules/git-commit-message.md create mode 100644 docs/apis/ScriptAPI/VanillaI18n.md create mode 100644 docs/apis/ScriptAPI/VanillaI18n.zh.md diff --git a/.trae/rules/git-commit-message.md b/.trae/rules/git-commit-message.md deleted file mode 100644 index 78d1a197..00000000 --- a/.trae/rules/git-commit-message.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -alwaysApply: true -scene: git_message -language: English ---- - -在此处编写规则,自定义 AI 生成提交信息的风格。 diff --git a/docs/apis/GameAPI/Packet.md b/docs/apis/GameAPI/Packet.md index 91852718..5e44bff0 100644 --- a/docs/apis/GameAPI/Packet.md +++ b/docs/apis/GameAPI/Packet.md @@ -134,12 +134,12 @@ This function is only available in 0.19.0 and later. !!! warning The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.0 and later. -`bs.getData([clear])` +`bs.getData([release])` - Parameters: - - clear : `Boolean` (Optional parameter) - Whether to clear stream data after reading. The default value is `true` + - release : `Boolean` (Optional parameter) + Whether to clear stream data after reading. The default value is `false` - Return value: binary stream data - Return value type: `ByteBuffer` @@ -223,7 +223,7 @@ This function group is only available in 0.19.0 and later. | Available functions | Parameter Type | Return Type | | ---------------------- | -------------- | ------------------- | -| readBool | None | `Boolean` | +| readBool | `Boolean` | `Boolean` / `String` | | readByte | `Boolean` | `Number` / `String` | | readBytes | `Integer` | `ByteBuffer` | | readUnsignedChar | `Boolean` | `Number` / `String` | @@ -242,7 +242,7 @@ This function group is only available in 0.19.0 and later. | readVarInt | `Boolean` | `Number` / `String` | | readVarInt64 | `Boolean` | `Number` / `String` | -When `asString` is `true`, numeric `read*` functions return `String`; otherwise they return `Number`. +When `asString` is `true`, scalar `read*` functions return `String`. #### Building packet from binary stream diff --git a/docs/apis/GameAPI/Packet.zh.md b/docs/apis/GameAPI/Packet.zh.md index 250d91ff..4b9cb357 100644 --- a/docs/apis/GameAPI/Packet.zh.md +++ b/docs/apis/GameAPI/Packet.zh.md @@ -134,12 +134,12 @@ !!! warning 此函数的可选参数和 `ByteBuffer` 返回值仅在 0.19.0 及以后版本可用 -`bs.getData([clear])` +`bs.getData([release])` - 参数: - - clear : `Boolean`(可选参数) - 获取后是否清空流数据。默认值为 `true` + - release : `Boolean`(可选参数) + 获取后是否清空流数据。默认值为 `false` - 返回值:二进制流数据 - 返回值类型: `ByteBuffer` @@ -223,7 +223,7 @@ | 可用函数 | 参数类型 | 返回值类型 | | ---------------------- | --------- | ------------------- | -| readBool | 无 | `Boolean`/ `String` | +| readBool | `Boolean` | `Boolean`/ `String` | | readByte | `Boolean` | `Number` / `String` | | readBytes | `Integer` | `ByteBuffer` | | readUnsignedChar | `Boolean` | `Number` / `String` | diff --git a/docs/apis/ScriptAPI/VanillaI18n.md b/docs/apis/ScriptAPI/VanillaI18n.md new file mode 100644 index 00000000..a68f9892 --- /dev/null +++ b/docs/apis/ScriptAPI/VanillaI18n.md @@ -0,0 +1,78 @@ +# VanillaI18n API + +This API is used to modify Minecraft vanilla language data. + +## Get current language + +`VaillanI18n.getCurrentLanguage()` + +- Return value: current language code +- Return value type: `String` + +## Set current language + +`VaillanI18n.setCurrentLanguage(language)` + +- Parameters: + - language: `String` + Language code, such as `en_US` or `zh_CN` +- Return value: success or not +- Return value type: `Boolean` + +## Get supported languages + +`VaillanI18n.getSupportedLanguages()` + +- Return value: supported language code list +- Return value type: `Array` + +## Translate vanilla text + +`VaillanI18n.translate(key[, params][, language])` + +- Parameters: + - key: `String` + Translation key + - params: `Array` (Optional) + Translation parameters + - language: `String` (Optional) + Target language. If not provided, the current language is used +- Return value: translated text +- Return value type: `String` + +## Load language data from object + +`VaillanI18n.loadLanguage(language, data)` + +- Parameters: + - language: `String` + Language code + - data: `Object` + Translation data. Each key and value must be `String` +- Return value: success or not +- Return value type: `Boolean` + +## Load language data from file + +`VaillanI18n.loadLanguageFromFile(language, path)` + +- Parameters: + - language: `String` + Language code + - path: `String` + Language file path +- Return value: success or not +- Return value type: `Boolean` + +## Load language data from directory + +`VaillanI18n.loadLanguagesFromDirectory(path)` + +- Parameters: + - path: `String` + Directory path +- Return value: success or not +- Return value type: `Boolean` + +This function loads all `.lang` files in the directory. +The file name without the `.lang` extension is used as the language code. diff --git a/docs/apis/ScriptAPI/VanillaI18n.zh.md b/docs/apis/ScriptAPI/VanillaI18n.zh.md new file mode 100644 index 00000000..6a3dfde2 --- /dev/null +++ b/docs/apis/ScriptAPI/VanillaI18n.zh.md @@ -0,0 +1,78 @@ +# VanillaI18n API + +此 API 用于修改 Minecraft 原版语言数据。 + +## 获取当前语言 + +`VaillanI18n.getCurrentLanguage()` + +- 返回值:当前语言代码 +- 返回值类型:`String` + +## 设置当前语言 + +`VaillanI18n.setCurrentLanguage(language)` + +- 参数: + - language: `String` + 语言代码,如 `en_US`、`zh_CN` +- 返回值:是否成功 +- 返回值类型:`Boolean` + +## 获取支持的语言列表 + +`VaillanI18n.getSupportedLanguages()` + +- 返回值:支持的语言代码列表 +- 返回值类型:`Array` + +## 翻译原版文本 + +`VaillanI18n.translate(key[, params][, language])` + +- 参数: + - key: `String` + 翻译键名 + - params: `Array`(可选参数) + 翻译参数 + - language: `String`(可选参数) + 目标语言。不传时使用当前语言 +- 返回值:翻译后的文本 +- 返回值类型:`String` + +## 从对象加载语言数据 + +`VaillanI18n.loadLanguage(language, data)` + +- 参数: + - language: `String` + 语言代码 + - data: `Object` + 语言数据,其中每个键和值都必须为 `String` +- 返回值:是否成功 +- 返回值类型:`Boolean` + +## 从文件加载语言数据 + +`VaillanI18n.loadLanguageFromFile(language, path)` + +- 参数: + - language: `String` + 语言代码 + - path: `String` + 语言文件路径 +- 返回值:是否成功 +- 返回值类型:`Boolean` + +## 从目录加载语言数据 + +`VaillanI18n.loadLanguagesFromDirectory(path)` + +- 参数: + - path: `String` + 目录路径 +- 返回值:是否成功 +- 返回值类型:`Boolean` + +此函数会加载目录下所有 `.lang` 文件。 +文件名去掉 `.lang` 后缀后会作为语言代码使用。 diff --git a/src/legacy/api/PacketAPI.cpp b/src/legacy/api/PacketAPI.cpp index 5fa4bbd2..9489c7cd 100644 --- a/src/legacy/api/PacketAPI.cpp +++ b/src/legacy/api/PacketAPI.cpp @@ -1,5 +1,6 @@ #include "legacy/api/PacketAPI.h" +#include "PacketAPI.h" #include "legacy/api/APIHelp.h" #include "legacy/api/BaseAPI.h" #include "legacy/api/EntityAPI.h" @@ -22,9 +23,13 @@ ClassDefine PacketClassBuilder = defineClass("LLSE_Pac .constructor(nullptr) .instanceFunction("getName", &PacketClass::getName) .instanceFunction("getId", &PacketClass::getId) + .instanceFunction("read", &PacketClass::read) + .instanceFunction("write", &PacketClass::write) .instanceFunction("sendTo", &PacketClass::sendTo) .instanceFunction("sendToClients", &PacketClass::sendToClients) .instanceFunction("sendToServer", &PacketClass::sendToServer) + + .function("createPacket", &PacketClass::createPacket) .build(); @@ -121,6 +126,73 @@ Local PacketClass::getId() { CATCH_AND_THROW } +Local PacketClass::read(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + if (!IsInstanceOf(args[0])) { + throw WrongArgTypeException(__FUNCTION__); + } + + try { + auto pkt = get(); + if (!pkt) { + return Boolean::newBoolean(false); + } + + auto stream = BinaryStreamClass::extract(args[0]); + if (!stream) { + return Boolean::newBoolean(false); + } + + if (auto res = pkt->read(*stream); !res) { + throw Exception(fmt::format("{}\nfunction: {}", res.error().code().message(), __FUNCTION__)); + } + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + +Local PacketClass::write(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + if (!IsInstanceOf(args[0])) { + throw WrongArgTypeException(__FUNCTION__); + } + + try { + auto pkt = get(); + if (!pkt) { + return Boolean::newBoolean(false); + } + + auto stream = BinaryStreamClass::extract(args[0]); + if (!stream) { + return Boolean::newBoolean(false); + } + + pkt->write(*stream); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + +Local PacketClass::createPacket(Arguments const& args) { + CHECK_ARGS_COUNT(args, 1); + if (args.size() >= 2) { + CHECK_ARG_TYPE(args[1], ValueKind::kBoolean); + } + try { + auto out = new PacketClass( + args.size() < 2 || !args[1].asBoolean().value() + ? MinecraftPackets::createPacket(static_cast(args[0].asNumber().toInt32())) + : std::make_shared( + static_cast(args[0].asNumber().toInt32()), + "" + ) + ); + return out->getScriptObject(); + } + CATCH_AND_THROW +} + Local PacketClass::sendTo(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); try { @@ -264,6 +336,12 @@ Local BinaryStreamClass::newBinaryStream() { return out->getScriptObject(); } +std::shared_ptr BinaryStreamClass::extract(Local const& v) { + if (EngineScope::currentEngine()->isInstanceOf(v)) + return EngineScope::currentEngine()->getNativeInstance(v)->get(); + return nullptr; +} + // member function Local BinaryStreamClass::getData(Arguments const& args) { @@ -276,7 +354,7 @@ Local BinaryStreamClass::getData(Arguments const& args) { } auto result = ByteBuffer::newByteBuffer(stream->mBuffer.data(), stream->mBuffer.size()); - if (args.size() < 1 || args[0].asBoolean().value()) { + if (args.size() >= 1 && args[0].asBoolean().value()) { stream->mBuffer.clear(); stream->mView = stream->mBuffer; stream->mReadPointer = 0; diff --git a/src/legacy/api/PacketAPI.h b/src/legacy/api/PacketAPI.h index aa4e8fbd..2757359e 100644 --- a/src/legacy/api/PacketAPI.h +++ b/src/legacy/api/PacketAPI.h @@ -12,16 +12,18 @@ class PacketClass : public ScriptClass { public: explicit PacketClass(std::shared_ptr const& p); + static std::shared_ptr extract(Local const& v); + static Local newPacket(std::shared_ptr const& pkt); + static Local createPacket(Arguments const& args); std::shared_ptr get() { return packet; } - - void set(std::shared_ptr const& pkt) { packet = pkt; }; - - static Local newPacket(std::shared_ptr const& pkt); + void set(std::shared_ptr const& pkt) { packet = pkt; }; Local getId(); Local getName(); + Local read(Arguments const& args); + Local write(Arguments const& args); Local sendTo(Arguments const& args); Local sendToClients(Arguments const& args); @@ -43,8 +45,9 @@ class BinaryStreamClass : public ScriptClass { std::shared_ptr get() { return binaryStream; } void set(std::shared_ptr const& bs) { binaryStream = bs; }; - static Local newBinaryStream(); - static BinaryStreamClass* constructor(Arguments const& args); + static Local newBinaryStream(); + static BinaryStreamClass* constructor(Arguments const& args); + static std::shared_ptr extract(Local const& v); Local getReadPointer(Arguments const& args); Local setReadPointer(Arguments const& args); diff --git a/src/legacy/api/VaillanI18n.cpp b/src/legacy/api/VaillanI18n.cpp index 5b82b6b6..1ebb7420 100644 --- a/src/legacy/api/VaillanI18n.cpp +++ b/src/legacy/api/VaillanI18n.cpp @@ -1,10 +1,10 @@ -#include "VaillanI18n.h" +#include "legacy/api/VaillanI18n.h" #include #include -ClassDefine VaillanI18nClassBuilder = - defineClass("VaillanI18n") +ClassDefine VaillanI18nClassBuilder = + defineClass("VaillanI18n") .function("setCurrentLanguage", &VaillanI18nClass::setCurrentLanguage) .function("getCurrentLanguage", &VaillanI18nClass::getCurrentLanguage) .function("getSupportedLanguages", &VaillanI18nClass::getSupportedLanguages) @@ -20,9 +20,9 @@ Local VaillanI18nClass::setCurrentLanguage(Arguments const& args) { std::string lang = args[0].asString().toString(); try { getI18n().chooseLanguage(lang); + return Boolean::newBoolean(true); } CATCH_AND_THROW - return Boolean::newBoolean(true); } Local VaillanI18nClass::getCurrentLanguage(Arguments const& args) { @@ -49,8 +49,8 @@ Local VaillanI18nClass::translate(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); - std::string key = args[0].asString().toString(); - std::string lang; + auto key = args[0].asString().toString(); + auto language = getI18n().getCurrentLanguage().get(); std::vector params; if (args.size() >= 2) { @@ -64,11 +64,13 @@ Local VaillanI18nClass::translate(Arguments const& args) { if (args.size() >= 3) { CHECK_ARG_TYPE(args[2], ValueKind::kString); - lang = args[2].asString().toString(); + if (auto res = getI18n().getLocaleFor(args[2].asString().toString()); res) { + language = res; + } } try { - return String::newString(getI18n().get(key, params, getI18n().getLocaleFor(lang))); + return String::newString(getI18n().get(key, params, language)); } CATCH_AND_THROW } @@ -87,20 +89,75 @@ Local VaillanI18nClass::loadLanguage(Arguments const& args) { map[prop.toString()] = obj.get(prop).asString().toString(); } auto loc = std::const_pointer_cast(getI18n().getLocaleFor(lang)); - if (!loc) getI18n().appendAdditionalTranslations(map, lang); - else - for (auto const& [key, value] : map) loc->mStrings->insert_or_assign(key, value); + if (!loc) { + getI18n().appendAdditionalTranslations(map, lang); + } else { + for (auto& [key, value] : map) { + loc->mStrings->insert_or_assign(key, std::move(value)); + } + } + return Boolean::newBoolean(true); } CATCH_AND_THROW - return Boolean::newBoolean(true); } Local VaillanI18nClass::loadLanguageFromFile(Arguments const& args) { - // TODO: Load language from file - return Boolean::newBoolean(true); + CHECK_ARGS_COUNT(args, 2); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + CHECK_ARG_TYPE(args[1], ValueKind::kString); + + try { + auto path = std::filesystem::path{args[1].asString().toU8string()}; + if (!std::filesystem::is_regular_file(path)) return Boolean::newBoolean(false); + loadLanguageFile(args[0].asString().toString(), path); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW } Local VaillanI18nClass::loadLanguagesFromDirectory(Arguments const& args) { - // TODO: Load languages from directory - return Boolean::newBoolean(true); + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + + try { + for (auto& entry : std::filesystem::directory_iterator(args[0].asString().toU8string())) { + if (!entry.is_regular_file()) continue; + auto file = ll::string_utils::u8str2str(entry.path().filename().u8string()); + if (!file.ends_with(".lang")) continue; + loadLanguageFile(file.substr(0, file.size() - 5), entry.path()); + } + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + +void VaillanI18nClass::loadLanguageFile(std::string const& language, std::filesystem::path const& path) { + std::ifstream file(path); + if (!file.is_open()) return; + std::unordered_map map; + + for (std::string line; std::getline(file, line); ) { + if (line.empty()) continue; + ll::string_utils::replaceAll(line, "\t", ""); + ll::string_utils::replaceAll(line, "\\n", "\n"); + if (auto equalPos = line.find('='); equalPos != std::string::npos) { + auto key = line.substr(0, equalPos); + ll::string_utils::replaceAll(key, " ", ""); + + auto value = line.substr(equalPos + 1, line.find('#', equalPos + 1)); + while (value.ends_with(' ')) value.erase(value.size() - 1); + + if (!key.empty() && !value.empty()) map.insert_or_assign(key, value); + } + } + + file.close(); + auto loc = std::const_pointer_cast(getI18n().getLocaleFor(language)); + if (!loc) { + getI18n().appendAdditionalTranslations(map, language); + } else { + for (auto& [key, value] : map) { + loc->mStrings->insert_or_assign(key, std::move(value)); + } + } } \ No newline at end of file diff --git a/src/legacy/api/VaillanI18n.h b/src/legacy/api/VaillanI18n.h index 1022e78c..f3bdf6d1 100644 --- a/src/legacy/api/VaillanI18n.h +++ b/src/legacy/api/VaillanI18n.h @@ -1,21 +1,17 @@ #pragma once #include "legacy/api/APIHelp.h" -class VaillanI18nClass { +class VaillanI18nClass : public ScriptClass { public: static Local setCurrentLanguage(Arguments const& args); - static Local getCurrentLanguage(Arguments const& args); - static Local getSupportedLanguages(Arguments const& args); - static Local translate(Arguments const& args); - static Local loadLanguage(Arguments const& args); - static Local loadLanguageFromFile(Arguments const& args); - static Local loadLanguagesFromDirectory(Arguments const& args); + + static void loadLanguageFile(std::string const& language, std::filesystem::path const& path); }; -extern ClassDefine<> VaillanI18nClassBuilder; +extern ClassDefine VaillanI18nClassBuilder; diff --git a/src/legacy/main/BindAPIs.cpp b/src/legacy/main/BindAPIs.cpp index cd9cca12..e7d3415f 100644 --- a/src/legacy/main/BindAPIs.cpp +++ b/src/legacy/main/BindAPIs.cpp @@ -26,6 +26,7 @@ #include "legacy/api/ScoreboardAPI.h" #include "legacy/api/ScriptAPI.h" #include "legacy/api/SystemAPI.h" +#include "legacy/api/VaillanI18n.h" // #include "legacy/api/PermissionAPI.h" #include "legacy/api/InternationalAPI.h" @@ -110,4 +111,5 @@ void BindAPIs(std::shared_ptr const& engine) { engine->registerNativeClass(HttpResponseClassBuilder); engine->registerNativeClass(BinaryStreamClassBuilder); engine->registerNativeClass(ParticleSpawnerBuilder); + engine->registerNativeClass(VaillanI18nClassBuilder); } From ed16f001a74eff036f0759696a84d34521d38d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Tue, 30 Jun 2026 22:20:38 +0800 Subject: [PATCH 07/17] feat: add some apis --- CHANGELOG.md | 15 ++- docs/apis/GameAPI/BlockEntity.md | 23 ++++ docs/apis/GameAPI/BlockEntity.zh.md | 23 ++++ docs/apis/GameAPI/Entity.md | 10 ++ docs/apis/GameAPI/Entity.zh.md | 10 ++ docs/apis/GameAPI/Item.md | 10 ++ docs/apis/GameAPI/Item.zh.md | 10 ++ docs/apis/GameAPI/Packet.md | 35 ++++++ docs/apis/GameAPI/Packet.zh.md | 35 ++++++ docs/apis/ScriptAPI/VanillaI18n.md | 5 + docs/apis/ScriptAPI/VanillaI18n.zh.md | 5 + output.patch | 155 ++++++++++++++++++++++++++ src/baselib/BaseLib.js | 2 + src/baselib/BaseLib.lua | 2 + src/legacy/api/BlockEntityAPI.cpp | 20 ++++ src/legacy/api/BlockEntityAPI.h | 2 + src/legacy/api/EntityAPI.cpp | 11 ++ src/legacy/api/EntityAPI.h | 1 + src/legacy/api/ItemAPI.cpp | 8 ++ src/legacy/api/ItemAPI.h | 1 + 20 files changed, 379 insertions(+), 4 deletions(-) create mode 100644 output.patch diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c4fe9c..d61f8bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added packet APIs: - `Packet::sendTo`, `Packet::sendToClients`, `Packet::sendToServer` @zimuya4153 + `Packet::sendTo`, `Packet::sendToClients`, `Packet::sendToServer`, + `Packet` / `LXL_Packet` global aliases @zimuya4153 - Added `BinaryStream` APIs: `getReadPointer`, `setReadPointer`, `setData`, `writeBytes`, `readBytes`, `writeUuid` @zimuya4153 @@ -20,13 +21,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added gameplay helpers: `en.setCustomName`, `mc.summonMob`, `mc.loadMob`, `it.addCount`, `it.removeCount`, `it.setCount`, `system.randomUuid` @zimuya4153 +- Added name access APIs: + `be.setCustomName`, `be.getCustomName`, `en.getCustomName`, + `it.getDisplayName` @zimuya4153 +- Added `VaillanI18n` API for modifying Minecraft vanilla language data + @zimuya4153 ### Changed - Improved `BinaryStream`: - `getData([clear])` now supports the optional `clear` parameter and returns - `ByteBuffer`; `createPacket(pktid[,raw])` now supports raw packets; several - numeric `write*` APIs now accept `String` for BigInt-friendly input + `getData([release])` now returns `ByteBuffer`, does not clear by default, and + optionally releases data after reading; `createPacket(pktid[,raw])` now + supports raw packets; scalar `read*` APIs were added; several numeric + `write*` APIs now accept `String` for BigInt-friendly input @zimuya4153 - Improved file and item behavior: `File.readFrom(path[,isBinary])` now supports binary reads; diff --git a/docs/apis/GameAPI/BlockEntity.md b/docs/apis/GameAPI/BlockEntity.md index a7058f12..d5a030ff 100644 --- a/docs/apis/GameAPI/BlockEntity.md +++ b/docs/apis/GameAPI/BlockEntity.md @@ -70,4 +70,27 @@ For more usage of NBT objects, please refer to [NBT Interface Documentation](../ - Return type: The block entity's block object. - Return value type: `Block` +#### Set Block Entity Custom Name + +!!! warning +This function is only available in 0.19.0 and later. + +`be.setCustomName(name)` + +- Parameters: + - name : `String` + New block entity custom name. +- Return type: Whether setting was successful. +- Return value type: `Boolean` + +#### Get Block Entity Custom Name + +!!! warning +This function is only available in 0.19.0 and later. + +`be.getCustomName()` + +- Return type: The block entity's custom name. +- Return value type: `String` + diff --git a/docs/apis/GameAPI/BlockEntity.zh.md b/docs/apis/GameAPI/BlockEntity.zh.md index e6fa07c1..61917609 100644 --- a/docs/apis/GameAPI/BlockEntity.zh.md +++ b/docs/apis/GameAPI/BlockEntity.zh.md @@ -70,4 +70,27 @@ - 返回值:方块实体对应的方块对象 - 返回值类型:`Block` +#### 设置方块实体自定义名称 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`be.setCustomName(name)` + +- 参数: + - name : `String` + 新的方块实体自定义名称 +- 返回值:是否设置成功 +- 返回值类型:`Boolean` + +#### 获取方块实体自定义名称 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`be.getCustomName()` + +- 返回值:方块实体自定义名称 +- 返回值类型:`String` + diff --git a/docs/apis/GameAPI/Entity.md b/docs/apis/GameAPI/Entity.md index 487d8a85..de3f8267 100644 --- a/docs/apis/GameAPI/Entity.md +++ b/docs/apis/GameAPI/Entity.md @@ -452,6 +452,16 @@ This function is only available in 0.19.0 and later. - Return value: Whether setting was successful - Return value type: `Boolean` +#### Get Entity Custom Name + +!!! warning +This function is only available in 0.19.0 and later. + +`en.getCustomName()` + +- Return value: The entity's custom name +- Return value type: `String` + #### Get Entity Distance To Pos `en.distanceTo(pos)` diff --git a/docs/apis/GameAPI/Entity.zh.md b/docs/apis/GameAPI/Entity.zh.md index 8b81ee54..e93fdb0a 100644 --- a/docs/apis/GameAPI/Entity.zh.md +++ b/docs/apis/GameAPI/Entity.zh.md @@ -439,6 +439,16 @@ - 返回值: 是否设置成功 - 返回值类型: `Boolean` +#### 获取实体自定义名称 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`en.getCustomName()` + +- 返回值: 实体自定义名称 +- 返回值类型: `String` + #### 获取实体到坐标的距离 `en.distanceTo(pos)` diff --git a/docs/apis/GameAPI/Item.md b/docs/apis/GameAPI/Item.md index 5e970dad..acb4d84b 100644 --- a/docs/apis/GameAPI/Item.md +++ b/docs/apis/GameAPI/Item.md @@ -220,6 +220,16 @@ In 0.19.0 and later, passing an empty array will clear the item's lore. - Return value: Whether setting the name was successful. - Return value type: `Boolean` +#### Get Custom Item Name + +!!! warning +This function is only available in 0.19.0 and later. + +`it.getDisplayName()` + +- Return value: Custom item name +- Return value type: `String` + #### Determine if it is the same kind of item diff --git a/docs/apis/GameAPI/Item.zh.md b/docs/apis/GameAPI/Item.zh.md index adb59da0..28217dbd 100644 --- a/docs/apis/GameAPI/Item.zh.md +++ b/docs/apis/GameAPI/Item.zh.md @@ -221,6 +221,16 @@ - 返回值: 设置物品名称是否成功 - 返回值类型: `Boolean` +#### 获取自定义物品名称 + +!!! warning + 此函数仅在0.19.0及以后版本可用 + +`it.getDisplayName()` + +- 返回值: 自定义物品名称 +- 返回值类型: `String` + #### 判断是否为同类物品 diff --git a/docs/apis/GameAPI/Packet.md b/docs/apis/GameAPI/Packet.md index 5e44bff0..ee71dea9 100644 --- a/docs/apis/GameAPI/Packet.md +++ b/docs/apis/GameAPI/Packet.md @@ -22,6 +22,21 @@ In LLSE, 「Packet Object」 is used to get information about packets. Call some **return packet object** function to get to the packet object given by BDS See [Binary Stream Objects](#-binary-stream-object-api) for details +#### Create from packet ID + +!!! warning +The global aliases of this class are only available in 0.19.0 and later. + +`Packet.createPacket(pktid[,raw])` + +- Parameters: + - pktid : `Integer` + Packet ID + - raw : `Boolean` (Optional parameter) + Create raw network packet. The default value is `false` +- Return value:Packet object +- Return value type: `Packet` + ### Packet Objects - Functions Every packet object contains some member functions (member methods) that can be executed. For a particular entity object `pkt`, some operations can be performed on this packet by these functions @@ -40,6 +55,26 @@ Every packet object contains some member functions (member methods) that can be - Return value:packet id - Return value type: `Integer` +#### Read binary stream data into packet + +`pkt.read(bs)` + +- Parameters: + - bs : `BinaryStream` + Source binary stream +- Return value:success or not +- Return value type: `Boolean` + +#### Write packet data into binary stream + +`pkt.write(bs)` + +- Parameters: + - bs : `BinaryStream` + Target binary stream +- Return value:success or not +- Return value type: `Boolean` + #### Send packet to specified target !!! warning diff --git a/docs/apis/GameAPI/Packet.zh.md b/docs/apis/GameAPI/Packet.zh.md index 4b9cb357..6631e111 100644 --- a/docs/apis/GameAPI/Packet.zh.md +++ b/docs/apis/GameAPI/Packet.zh.md @@ -22,6 +22,21 @@ 调用某些**返回数据包对象**的函数,来获取到 BDS 给出的数据包对象 详见 [二进制流对象](#-二进制流对象-api) +#### 通过数据包 ID 创建 + +!!! warning +此类的全局别名仅在 0.19.0 及以后版本可用 + +`Packet.createPacket(pktid[,raw])` + +- 参数: + - pktid : `Integer` + 数据包 ID + - raw : `Boolean`(可选参数) + 是否创建原始网络数据包。默认值为 `false` +- 返回值:数据包对象 +- 返回值类型: `Packet` + ### 数据包对象 - 函数 每一个数据包对象都包含一些可以执行的成员函数(成员方法)。对于某个特定的实体对象`pkt`,可以通过以下这些函数对这个数据包进行一些操作 @@ -40,6 +55,26 @@ - 返回值:数据包 ID - 返回值类型: `Integer` +#### 将二进制流数据读取到数据包 + +`pkt.read(bs)` + +- 参数: + - bs : `BinaryStream` + 源二进制流 +- 返回值:是否成功 +- 返回值类型: `Boolean` + +#### 将数据包数据写入二进制流 + +`pkt.write(bs)` + +- 参数: + - bs : `BinaryStream` + 目标二进制流 +- 返回值:是否成功 +- 返回值类型: `Boolean` + #### 发送数据包到指定目标 !!! warning diff --git a/docs/apis/ScriptAPI/VanillaI18n.md b/docs/apis/ScriptAPI/VanillaI18n.md index a68f9892..edb3d255 100644 --- a/docs/apis/ScriptAPI/VanillaI18n.md +++ b/docs/apis/ScriptAPI/VanillaI18n.md @@ -2,6 +2,11 @@ This API is used to modify Minecraft vanilla language data. +!!! warning +This API is only available in 0.19.0 and later. + +The actual registered class name in the script engine is `VaillanI18n`. + ## Get current language `VaillanI18n.getCurrentLanguage()` diff --git a/docs/apis/ScriptAPI/VanillaI18n.zh.md b/docs/apis/ScriptAPI/VanillaI18n.zh.md index 6a3dfde2..1c0e0daf 100644 --- a/docs/apis/ScriptAPI/VanillaI18n.zh.md +++ b/docs/apis/ScriptAPI/VanillaI18n.zh.md @@ -2,6 +2,11 @@ 此 API 用于修改 Minecraft 原版语言数据。 +!!! warning + 此 API 仅在0.19.0及以后版本可用 + +此对象在脚本引擎内实际注册的类名为 `VaillanI18n`。 + ## 获取当前语言 `VaillanI18n.getCurrentLanguage()` diff --git a/output.patch b/output.patch new file mode 100644 index 00000000..8332e7c8 --- /dev/null +++ b/output.patch @@ -0,0 +1,155 @@ +diff --git a/src/baselib/BaseLib.js b/src/baselib/BaseLib.js +index 4387716..cd122ef 100644 +--- a/src/baselib/BaseLib.js ++++ b/src/baselib/BaseLib.js +@@ -414,5 +414,7 @@ globalThis.LXL_CustomForm = LLSE_CustomForm; + globalThis.LXL_Item = LLSE_Item; + globalThis.LXL_Player = LLSE_Player; + globalThis.LXL_Objective = LLSE_Objective; ++globalThis.LXL_Packet = LLSE_Packet; ++globalThis.Packet = LLSE_Packet; + ll.export = ll.exports; + ll.import = ll.imports; +diff --git a/src/baselib/BaseLib.lua b/src/baselib/BaseLib.lua +index b70720f..5f1ceac 100644 +--- a/src/baselib/BaseLib.lua ++++ b/src/baselib/BaseLib.lua +@@ -20,5 +20,7 @@ LXL_CustomForm = LLSE_CustomForm + LXL_Item = LLSE_Item + LXL_Player = LLSE_Player + LXL_Objective = LLSE_Objective ++LXL_Packet = LLSE_Packet ++Packet = LLSE_Packet + ll.export = ll.exports + ll.import = ll.imports +diff --git a/src/legacy/api/BlockEntityAPI.cpp b/src/legacy/api/BlockEntityAPI.cpp +index c61dbcf..961c208 100644 +--- a/src/legacy/api/BlockEntityAPI.cpp ++++ b/src/legacy/api/BlockEntityAPI.cpp +@@ -24,6 +24,8 @@ ClassDefine BlockEntityClassBuilder = defineClass BlockEntityClass::getBlock(Arguments const&) const { + } + CATCH_AND_THROW + } ++ ++Local BlockEntityClass::setCustomName(Arguments const& args) const { ++ CHECK_ARGS_COUNT(args, 1); ++ CHECK_ARG_TYPE(args[0], ValueKind::kString); ++ ++ try { ++ blockEntity->setCustomName({args[0].asString().toString(), std::nullopt}); ++ return Boolean::newBoolean(true); ++ } ++ CATCH_AND_THROW ++} ++ ++Local BlockEntityClass::getCustomName() const { ++ try { ++ return String::newString(blockEntity->getCustomName().mUnredactedString); ++ } ++ CATCH_AND_THROW ++} +\ No newline at end of file +diff --git a/src/legacy/api/BlockEntityAPI.h b/src/legacy/api/BlockEntityAPI.h +index 4eee391..8d51af9 100644 +--- a/src/legacy/api/BlockEntityAPI.h ++++ b/src/legacy/api/BlockEntityAPI.h +@@ -20,9 +20,11 @@ class BlockEntityClass : public ScriptClass { + Local getName() const; + Local getPos() const; + Local getType() const; ++ Local getCustomName() const; + + Local getNbt(Arguments const& args) const; + Local setNbt(Arguments const& args) const; + Local getBlock(Arguments const& args) const; ++ Local setCustomName(Arguments const& args) const; + }; + extern ClassDefine BlockEntityClassBuilder; +diff --git a/src/legacy/api/EntityAPI.cpp b/src/legacy/api/EntityAPI.cpp +index cb34c4d..7267640 100644 +--- a/src/legacy/api/EntityAPI.cpp ++++ b/src/legacy/api/EntityAPI.cpp +@@ -140,6 +140,7 @@ ClassDefine EntityClassBuilder = + .instanceFunction("refreshItems", &EntityClass::refreshItems) + .instanceFunction("setScale", &EntityClass::setScale) + .instanceFunction("setCustomName", &EntityClass::setCustomName) ++ .instanceFunction("getCustomName", &EntityClass::getCustomName) + .instanceFunction("setNbt", &EntityClass::setNbt) + .instanceFunction("getNbt", &EntityClass::getNbt) + .instanceFunction("addTag", &EntityClass::addTag) +@@ -1309,6 +1310,16 @@ Local EntityClass::setCustomName(Arguments const& args) const { + CATCH_AND_THROW + } + ++Local EntityClass::getCustomName() const { ++ try { ++ Actor* entity = get(); ++ if (!entity) return {}; ++ ++ return String::newString(entity->getNameTag()); ++ } ++ CATCH_AND_THROW ++} ++ + Local EntityClass::getNbt(Arguments const&) const { + try { + Actor const* entity = get(); +diff --git a/src/legacy/api/EntityAPI.h b/src/legacy/api/EntityAPI.h +index fdaf93f..5bfee5e 100644 +--- a/src/legacy/api/EntityAPI.h ++++ b/src/legacy/api/EntityAPI.h +@@ -31,6 +31,7 @@ class EntityClass : public ScriptClass { + Local getCanFreeze() const; + Local getCanSeeDaylight() const; + Local getCanPickupItems() const; ++ Local getCustomName() const; + Local getInAir() const; + Local getInWater() const; + Local getInClouds() const; +diff --git a/src/legacy/api/ItemAPI.cpp b/src/legacy/api/ItemAPI.cpp +index 05abdeb..f8e9981 100644 +--- a/src/legacy/api/ItemAPI.cpp ++++ b/src/legacy/api/ItemAPI.cpp +@@ -61,6 +61,7 @@ ClassDefine ItemClassBuilder = defineClass("LLSE_Item") + .instanceFunction("setAux", &ItemClass::setAux) + .instanceFunction("setLore", &ItemClass::setLore) + .instanceFunction("setDisplayName", &ItemClass::setDisplayName) ++ .instanceFunction("getDisplayName", &ItemClass::getDisplayName) + .instanceFunction("setDamage", &ItemClass::setDamage) + .instanceFunction("setNbt", &ItemClass::setNbt) + .instanceFunction("getNbt", &ItemClass::getNbt) +@@ -419,6 +420,13 @@ Local ItemClass::setDisplayName(Arguments const& args) const { + CATCH_AND_THROW + } + ++Local ItemClass::getDisplayName() const { ++ try { ++ return String::newString(get()->getCustomName()); ++ } ++ CATCH_AND_THROW ++} ++ + Local ItemClass::setDamage(Arguments const& args) const { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kNumber); +diff --git a/src/legacy/api/ItemAPI.h b/src/legacy/api/ItemAPI.h +index 0fd7af9..dbbfcbe 100644 +--- a/src/legacy/api/ItemAPI.h ++++ b/src/legacy/api/ItemAPI.h +@@ -43,6 +43,7 @@ class ItemClass : public ScriptClass { + Local getMaxDamage() const; + Local getMaxStackSize() const; + Local getLore() const; ++ Local getDisplayName() const; + + Local isArmorItem() const; + Local isBlock() const; diff --git a/src/baselib/BaseLib.js b/src/baselib/BaseLib.js index 4387716d..cd122efc 100644 --- a/src/baselib/BaseLib.js +++ b/src/baselib/BaseLib.js @@ -414,5 +414,7 @@ globalThis.LXL_CustomForm = LLSE_CustomForm; globalThis.LXL_Item = LLSE_Item; globalThis.LXL_Player = LLSE_Player; globalThis.LXL_Objective = LLSE_Objective; +globalThis.LXL_Packet = LLSE_Packet; +globalThis.Packet = LLSE_Packet; ll.export = ll.exports; ll.import = ll.imports; diff --git a/src/baselib/BaseLib.lua b/src/baselib/BaseLib.lua index b70720f0..5f1ceac7 100644 --- a/src/baselib/BaseLib.lua +++ b/src/baselib/BaseLib.lua @@ -20,5 +20,7 @@ LXL_CustomForm = LLSE_CustomForm LXL_Item = LLSE_Item LXL_Player = LLSE_Player LXL_Objective = LLSE_Objective +LXL_Packet = LLSE_Packet +Packet = LLSE_Packet ll.export = ll.exports ll.import = ll.imports diff --git a/src/legacy/api/BlockEntityAPI.cpp b/src/legacy/api/BlockEntityAPI.cpp index c61dbcfe..961c2087 100644 --- a/src/legacy/api/BlockEntityAPI.cpp +++ b/src/legacy/api/BlockEntityAPI.cpp @@ -24,6 +24,8 @@ ClassDefine BlockEntityClassBuilder = defineClass BlockEntityClass::getBlock(Arguments const&) const { } CATCH_AND_THROW } + +Local BlockEntityClass::setCustomName(Arguments const& args) const { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + + try { + blockEntity->setCustomName({args[0].asString().toString(), std::nullopt}); + return Boolean::newBoolean(true); + } + CATCH_AND_THROW +} + +Local BlockEntityClass::getCustomName() const { + try { + return String::newString(blockEntity->getCustomName().mUnredactedString); + } + CATCH_AND_THROW +} \ No newline at end of file diff --git a/src/legacy/api/BlockEntityAPI.h b/src/legacy/api/BlockEntityAPI.h index 4eee3912..8d51af97 100644 --- a/src/legacy/api/BlockEntityAPI.h +++ b/src/legacy/api/BlockEntityAPI.h @@ -20,9 +20,11 @@ class BlockEntityClass : public ScriptClass { Local getName() const; Local getPos() const; Local getType() const; + Local getCustomName() const; Local getNbt(Arguments const& args) const; Local setNbt(Arguments const& args) const; Local getBlock(Arguments const& args) const; + Local setCustomName(Arguments const& args) const; }; extern ClassDefine BlockEntityClassBuilder; diff --git a/src/legacy/api/EntityAPI.cpp b/src/legacy/api/EntityAPI.cpp index cb34c4d5..72676403 100644 --- a/src/legacy/api/EntityAPI.cpp +++ b/src/legacy/api/EntityAPI.cpp @@ -140,6 +140,7 @@ ClassDefine EntityClassBuilder = .instanceFunction("refreshItems", &EntityClass::refreshItems) .instanceFunction("setScale", &EntityClass::setScale) .instanceFunction("setCustomName", &EntityClass::setCustomName) + .instanceFunction("getCustomName", &EntityClass::getCustomName) .instanceFunction("setNbt", &EntityClass::setNbt) .instanceFunction("getNbt", &EntityClass::getNbt) .instanceFunction("addTag", &EntityClass::addTag) @@ -1309,6 +1310,16 @@ Local EntityClass::setCustomName(Arguments const& args) const { CATCH_AND_THROW } +Local EntityClass::getCustomName() const { + try { + Actor* entity = get(); + if (!entity) return {}; + + return String::newString(entity->getNameTag()); + } + CATCH_AND_THROW +} + Local EntityClass::getNbt(Arguments const&) const { try { Actor const* entity = get(); diff --git a/src/legacy/api/EntityAPI.h b/src/legacy/api/EntityAPI.h index fdaf93fb..5bfee5e7 100644 --- a/src/legacy/api/EntityAPI.h +++ b/src/legacy/api/EntityAPI.h @@ -31,6 +31,7 @@ class EntityClass : public ScriptClass { Local getCanFreeze() const; Local getCanSeeDaylight() const; Local getCanPickupItems() const; + Local getCustomName() const; Local getInAir() const; Local getInWater() const; Local getInClouds() const; diff --git a/src/legacy/api/ItemAPI.cpp b/src/legacy/api/ItemAPI.cpp index 05abdebe..f8e99815 100644 --- a/src/legacy/api/ItemAPI.cpp +++ b/src/legacy/api/ItemAPI.cpp @@ -61,6 +61,7 @@ ClassDefine ItemClassBuilder = defineClass("LLSE_Item") .instanceFunction("setAux", &ItemClass::setAux) .instanceFunction("setLore", &ItemClass::setLore) .instanceFunction("setDisplayName", &ItemClass::setDisplayName) + .instanceFunction("getDisplayName", &ItemClass::getDisplayName) .instanceFunction("setDamage", &ItemClass::setDamage) .instanceFunction("setNbt", &ItemClass::setNbt) .instanceFunction("getNbt", &ItemClass::getNbt) @@ -419,6 +420,13 @@ Local ItemClass::setDisplayName(Arguments const& args) const { CATCH_AND_THROW } +Local ItemClass::getDisplayName() const { + try { + return String::newString(get()->getCustomName()); + } + CATCH_AND_THROW +} + Local ItemClass::setDamage(Arguments const& args) const { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kNumber); diff --git a/src/legacy/api/ItemAPI.h b/src/legacy/api/ItemAPI.h index 0fd7af99..dbbfcbee 100644 --- a/src/legacy/api/ItemAPI.h +++ b/src/legacy/api/ItemAPI.h @@ -43,6 +43,7 @@ class ItemClass : public ScriptClass { Local getMaxDamage() const; Local getMaxStackSize() const; Local getLore() const; + Local getDisplayName() const; Local isArmorItem() const; Local isBlock() const; From 565371268205d09377a2cfff2d8b6b2e67a7a29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Tue, 30 Jun 2026 22:22:09 +0800 Subject: [PATCH 08/17] chore: update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d61f8bad..4bfd13ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `be.setCustomName`, `be.getCustomName`, `en.getCustomName`, `it.getDisplayName` @zimuya4153 - Added `VaillanI18n` API for modifying Minecraft vanilla language data - @zimuya4153 + @zimuya4153 @xianyubb ### Changed From f9afdba5a1a722c7610170bcaa7c8b3414fa52b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:38:25 +0800 Subject: [PATCH 09/17] feat: add some apis --- docs/apis/GameAPI/Player.md | 27 ++-- docs/apis/GameAPI/Player.zh.md | 27 ++-- src/legacy/api/McAPI.cpp | 1 + src/legacy/api/McAPI.h | 1 + src/legacy/api/PlayerAPI.cpp | 262 +++++++++++++++++++++++---------- 5 files changed, 222 insertions(+), 96 deletions(-) diff --git a/docs/apis/GameAPI/Player.md b/docs/apis/GameAPI/Player.md index 168ca9d6..c0a5420d 100644 --- a/docs/apis/GameAPI/Player.md +++ b/docs/apis/GameAPI/Player.md @@ -1109,23 +1109,24 @@ For more usage of NBT objects, please refer to [NBT Interface Documentation](../ Player`s UUID - Return value: Player's NBT object. - Return value type: `NbtCompound` - -Using this API, you can operate offline player`s nbt. + - If the player does not exist or no saved data is found, the return value is `Null`. #### Write to an Player's NBT Object -`mc.setPlayerNbt(uuid,nbt)` +`mc.setPlayerNbt(uuid,nbt[,forceCreate,isOnlineMode])` - Parameters: - uuid : `String` Player`s UUID - nbt : `NbtCompound` NBT objects + - forceCreate : `Boolean` (Optional parameter) + Whether to create offline player data when the UUID has no saved record. The default value is `false`. + - isOnlineMode : `Boolean` (Optional parameter) + Only used when `forceCreate` is `true`. Whether the created player data uses online mode. The default value is `true`. - Return value: Whether the write was successful or not. - Return value type: `Boolean` -Using this API, you can operate offline player`s nbt. - #### Write Data to Some Special Tags of an Player's NBT Object `mc.setPlayerNbtTags(uuid,nbt,tags)` @@ -1135,13 +1136,11 @@ Using this API, you can operate offline player`s nbt. Player`s UUID - nbt : `NbtCompound` NBT objects - - tags : `Array` + - tags : `Array` Tags need to write - Return value: Whether the write was successful or not. - Return value type: `Boolean` -Using this API, you can operate offline player`s nbt. - #### Delete an Player's NBT Object `mc.deletePlayerNbt(uuid)` @@ -1152,7 +1151,17 @@ Using this API, you can operate offline player`s nbt. - Return value: Whether the delete was successful or not. - Return value type: `Boolean` -Using this API, you can operate offline player`s nbt. +Using this API, you can delete saved offline player nbt data by UUID. + +#### Get All Saved Player UUIDs + +`mc.getAllPlayerUuids([isOnlineMode])` + +- Parameters: + - isOnlineMode : `Boolean` (Optional parameter) + `true` returns UUIDs of players who logged in with a Microsoft account; `false` returns UUIDs of offline mode players, default is `false`. +- Return value: List of saved player UUID strings. +- Return value type: `Array` #### Add a Tag for the Player diff --git a/docs/apis/GameAPI/Player.zh.md b/docs/apis/GameAPI/Player.zh.md index 3fb52113..6c02a624 100644 --- a/docs/apis/GameAPI/Player.zh.md +++ b/docs/apis/GameAPI/Player.zh.md @@ -1464,23 +1464,24 @@ 玩家的UUID - 返回值:玩家的NBT对象 - 返回值类型:`NbtCompound` - -此API的好处是可以获取到离线玩家NBT,无需玩家在线,无需玩家对象。 + - 如果玩家不存在或没有存档数据,则返回值为 `Null` #### 写入玩家对应的NBT对象 -`mc.setPlayerNbt(uuid,nbt)` +`mc.setPlayerNbt(uuid,nbt[,forceCreate,isOnlineMode])` - 参数: - uuid : `String` 玩家的UUID - nbt : `NbtCompound` NBT对象 + - forceCreate : `Boolean` 可选参数 + 当该 UUID 没有对应存档记录时,是否创建离线玩家数据。默认值为 `false` + - isOnlineMode : `Boolean` 可选参数 + 仅在 `forceCreate` 为 `true` 时生效。决定新建玩家数据是否使用在线模式。默认值为 `true` - 返回值:是否成功写入 - 返回值类型:`Boolean` -此API的好处是可以操作离线玩家NBT,无需玩家在线,无需玩家对象。 - #### 覆盖玩家对应的NBT对象的特定NbtTag `mc.setPlayerNbtTags(uuid,nbt,tags)` @@ -1490,13 +1491,11 @@ 玩家的UUID - nbt : `NbtCompound` NBT对象 - - tags : `Array` - 需要覆盖的NbtTag (String) + - tags : `Array` + 需要覆盖的NbtTag - 返回值:是否成功覆盖对应的Tag - 返回值类型:`Boolean` -此API的好处是可以操作离线玩家NBT,无需玩家在线,无需玩家对象。 - #### 从存档中删除玩家对应的NBT对象的全部内容 `mc.deletePlayerNbt(uuid)` @@ -1507,7 +1506,15 @@ - 返回值:是否删除成功 - 返回值类型:`Boolean` -此API的好处是可以操作离线玩家NBT,无需玩家在线,无需玩家对象。 +#### 获取全部已保存玩家 UUID + +`mc.getAllPlayerUuids([isOnlineMode])` + +- 参数: + - isOnlineMode : `Boolean` 可选参数 + 为 `true` 时返回登录了微软账号玩家的 UUID;为 `false` 时返回离线模式玩家的 UUID,默认为 `false` +- 返回值:已保存玩家 UUID 字符串列表 +- 返回值类型:`Array` #### 为玩家增加一个Tag diff --git a/src/legacy/api/McAPI.cpp b/src/legacy/api/McAPI.cpp index 8818cc84..dee9f43a 100644 --- a/src/legacy/api/McAPI.cpp +++ b/src/legacy/api/McAPI.cpp @@ -46,6 +46,7 @@ ClassDefine McClassBuilder = defineClass("mc") .function("setPlayerNbt", &McClass::setPlayerNbt) .function("setPlayerNbtTags", &McClass::setPlayerNbtTags) .function("deletePlayerNbt", &McClass::deletePlayerNbt) + .function("getAllPlayerUuids", &McClass::getAllPlayerUuids) .function("getPlayerScore", &McClass::getPlayerScore) .function("setPlayerScore", &McClass::setPlayerScore) .function("addPlayerScore", &McClass::addPlayerScore) diff --git a/src/legacy/api/McAPI.h b/src/legacy/api/McAPI.h index 61a497fb..f7ca46a2 100644 --- a/src/legacy/api/McAPI.h +++ b/src/legacy/api/McAPI.h @@ -64,6 +64,7 @@ class McClass { static Local setPlayerNbt(Arguments const& args); static Local setPlayerNbtTags(Arguments const& args); static Local deletePlayerNbt(Arguments const& args); + static Local getAllPlayerUuids(Arguments const& args); static Local getPlayerScore(Arguments const& args); static Local setPlayerScore(Arguments const& args); static Local addPlayerScore(Arguments const& args); diff --git a/src/legacy/api/PlayerAPI.cpp b/src/legacy/api/PlayerAPI.cpp index d9af6297..cf5f59b8 100644 --- a/src/legacy/api/PlayerAPI.cpp +++ b/src/legacy/api/PlayerAPI.cpp @@ -1,5 +1,6 @@ #include "legacy/api/PlayerAPI.h" +#include "NbtAPI.h" #include "legacy/api/APIHelp.h" #include "legacy/api/BaseAPI.h" #include "legacy/api/BlockAPI.h" @@ -33,6 +34,7 @@ #include "lse/api/helper/ScoreboardHelper.h" #include "mc/deps/core/math/Vec2.h" #include "mc/deps/core/utility/MCRESULT.h" +#include "mc/deps/core/utility/optional_ref.h" #include "mc/deps/nbt/CompoundTag.h" #include "mc/deps/nbt/ListTag.h" #include "mc/deps/nbt/StringTag.h" @@ -360,19 +362,39 @@ using namespace lse::api; Local McClass::getPlayerNbt(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); + if (!mce::UUID::canParse(args[0].asString().toString())) { + throw CreateExceptionWithInfo( + __FUNCTION__, + fmt::format("{0} is not a valid UUID", args[0].asString().toString()) + ); + } try { auto uuid = mce::UUID::fromString(args[0].asString().toString()); - auto db = ll::service::getDBStorage(); - if (db && db->hasKey("player_" + uuid.asString(), DBHelpers::Category::Player)) { - std::unique_ptr playerTag = - db->getCompoundTag("player_" + uuid.asString(), DBHelpers::Category::Player); - if (playerTag) { - std::string serverId = playerTag->at("ServerId"); - if (!serverId.empty() && db->hasKey(serverId, DBHelpers::Category::Player)) { - return NbtCompoundClass::pack(db->getCompoundTag(serverId, DBHelpers::Category::Player)); + if (!uuid) return Boolean::newBoolean(false); + + // online + if (auto* player = ll::service::getLevel()->getPlayer(uuid); player) { + auto tag = std::make_unique(); + player->save(*tag); + return NbtCompoundClass::pack(std::move(tag)); + } + + auto storage = ll::service::getDBStorage(); + + // offline + if (auto playerKey = "player_" + uuid.asString(); storage->hasKey(playerKey, DBHelpers::Category::All)) { + if (auto data = storage->getCompoundTag(playerKey, DBHelpers::Category::All); + data && data->contains("ServerId", Tag::String)) { + if (auto serverId = (*data)["ServerId"].get(); !serverId.empty()) { + if (storage->hasKey(serverId, DBHelpers::Category::Player)) { + if (auto nbt = storage->getCompoundTag(serverId, DBHelpers::Category::Player); nbt) { + return NbtCompoundClass::pack(std::move(nbt)); + } + } } } } + return {}; } CATCH_AND_THROW @@ -381,26 +403,73 @@ Local McClass::getPlayerNbt(Arguments const& args) { Local McClass::setPlayerNbt(Arguments const& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); + if (!mce::UUID::canParse(args[0].asString().toString())) { + throw CreateExceptionWithInfo( + __FUNCTION__, + fmt::format("{0} is not a valid UUID", args[0].asString().toString()) + ); + } + if (!IsInstanceOf(args[1])) { + throw WrongArgTypeException(__FUNCTION__); + } + if (args.size() >= 3) CHECK_ARG_TYPE(args[2], ValueKind::kBoolean); // forceCreate (default: false) + if (args.size() >= 4) CHECK_ARG_TYPE(args[3], ValueKind::kBoolean); // isOnlineMode (default: true) try { - mce::UUID uuid = mce::UUID::fromString(args[0].asString().toString()); - auto tag = NbtCompoundClass::extract(args[1]); - Player* player = ll::service::getLevel()->getPlayer(uuid); - if (player && tag) { + auto uuid = mce::UUID::fromString(args[0].asString().toString()); + if (!uuid) return Boolean::newBoolean(false); + + auto* tag = NbtCompoundClass::extract(args[1]); + if (!tag) return Boolean::newBoolean(false); + + // online + if (auto* player = ll::service::getLevel()->getPlayer(uuid); player) { player->load(*tag, MoreGlobal::defaultDataLoadHelper()); - } else if (tag) { - auto db = ll::service::getDBStorage(); - if (db && db->hasKey("player_" + uuid.asString(), DBHelpers::Category::Player)) { - std::unique_ptr playerTag = - db->getCompoundTag("player_" + uuid.asString(), DBHelpers::Category::Player); - if (playerTag) { - std::string serverId = playerTag->at("ServerId"); - if (!serverId.empty()) { - db->saveData(serverId, tag->toBinaryNbt(), DBHelpers::Category::Player); - return Boolean::newBoolean(true); + return Boolean::newBoolean(true); + } + + auto storage = ll::service::getDBStorage(); + + // offline + if (auto playerKey = "player_" + uuid.asString(); storage->hasKey(playerKey, DBHelpers::Category::All)) { + if (auto data = storage->getCompoundTag(playerKey, DBHelpers::Category::All); + data && data->contains("ServerId", Tag::String)) { + if (auto serverId = (*data)["ServerId"].get(); !serverId.empty()) { + storage->saveData(serverId, tag->toBinaryNbt(), DBHelpers::Category::All); + return Boolean::newBoolean(true); + } + } + } + + // not found + if (args.size() >= 3 && args[2].asBoolean().value()) { + auto playerKey = "player_" + uuid.asString(); + auto serverId = fmt::format("player_server_{0}", mce::UUID::random().asString()); + { + // 这里要么 playerKey 不存在,要么 ServerId 不存在/为空,不然 offline 就能正常处理了 + auto playerTag = storage->hasKey(playerKey, DBHelpers::Category::All) + ? storage->getCompoundTag(playerKey, DBHelpers::Category::All) + : nullptr; + if (!playerTag) { + playerTag = std::make_unique(std::initializer_list{ + {"ServerId", serverId} + }); + + if (args.size() < 4 || args[3].asBoolean().value()) { // isOnlineMode + (*playerTag)["MsaId"] = uuid.asString(); + (*playerTag)["SelfSignedId"] = mce::UUID::random().asString(); + } else { + (*playerTag)["MsaId"] = ""; + (*playerTag)["SelfSignedId"] = uuid.asString(); } + } else { + (*playerTag)["ServerId"] = serverId; } + storage->saveData(playerKey, playerTag->toBinaryNbt(), DBHelpers::Category::All); } + storage->saveData(serverId, tag->toBinaryNbt(), DBHelpers::Category::All); + return Boolean::newBoolean(true); } + return Boolean::newBoolean(false); } CATCH_AND_THROW @@ -409,51 +478,62 @@ Local McClass::setPlayerNbt(Arguments const& args) { Local McClass::setPlayerNbtTags(Arguments const& args) { CHECK_ARGS_COUNT(args, 3); CHECK_ARG_TYPE(args[0], ValueKind::kString); + if (!mce::UUID::canParse(args[0].asString().toString())) { + throw CreateExceptionWithInfo( + __FUNCTION__, + fmt::format("{0} is not a valid UUID", args[0].asString().toString()) + ); + } + if (!IsInstanceOf(args[1])) { + throw WrongArgTypeException(__FUNCTION__); + } CHECK_ARG_TYPE(args[2], ValueKind::kArray); try { - mce::UUID uuid = mce::UUID::fromString(args[0].asString().toString()); - auto tag = NbtCompoundClass::extract(args[1]); - Local arr = args[2].asArray(); - Player* player = ll::service::getLevel()->getPlayer(uuid); - if (player && tag) { - CompoundTag loadedTag; - player->save(loadedTag); + auto uuid = mce::UUID::fromString(args[0].asString().toString()); + if (!uuid) return Boolean::newBoolean(false); + + auto* tag = NbtCompoundClass::extract(args[1]); + if (!tag) return Boolean::newBoolean(false); + + Local arr = args[2].asArray(); + + auto copyTags = [&](CompoundTag& target) { for (size_t i = 0; i < arr.size(); ++i) { auto value = arr.get(i); - if (value.getKind() == ValueKind::kString) { - std::string tagName = value.asString().toString(); - if (!tag->at(tagName).is_null()) { - loadedTag.at(tagName) = tag->at(tagName); - } + if (value.getKind() != ValueKind::kString) continue; + std::string tagName = value.asString().toString(); + if (tag->contains(tagName)) { + target[tagName] = tag->at(tagName); } } + }; + + // online + if (auto* player = ll::service::getLevel()->getPlayer(uuid)) { + CompoundTag loadedTag; + player->save(loadedTag); + copyTags(loadedTag); player->load(loadedTag, MoreGlobal::defaultDataLoadHelper()); - } else if (tag) { - auto db = ll::service::getDBStorage(); - if (db && db->hasKey("player_" + uuid.asString(), DBHelpers::Category::Player)) { - std::unique_ptr playerTag = - db->getCompoundTag("player_" + uuid.asString(), DBHelpers::Category::Player); - if (playerTag) { - std::string serverId = playerTag->at("ServerId"); - if (!serverId.empty() && db->hasKey(serverId, DBHelpers::Category::Player)) { - if (auto loadedTag = db->getCompoundTag(serverId, DBHelpers::Category::Player)) { - for (size_t i = 0; i < arr.size(); ++i) { - auto value = arr.get(i); - if (value.getKind() == ValueKind::kString) { - std::string tagName = value.asString().toString(); - if (!tag->at(tagName).is_null()) { - loadedTag->at(tagName) = tag->at(tagName); - } - } - } - db->saveData(serverId, loadedTag->toBinaryNbt(), DBHelpers::Category::Player); - return Boolean::newBoolean(true); - } + return Boolean::newBoolean(true); + } + + // offline + auto storage = ll::service::getDBStorage(); + + if (auto playerKey = "player_" + uuid.asString(); storage->hasKey(playerKey, DBHelpers::Category::All)) { + auto data = storage->getCompoundTag(playerKey, DBHelpers::Category::All); + if (data && data->contains("ServerId", Tag::String)) { + if (auto serverId = (*data)["ServerId"].get(); + !serverId.empty() && storage->hasKey(serverId, DBHelpers::Category::All)) { + if (auto loadedTag = storage->getCompoundTag(serverId, DBHelpers::Category::All); loadedTag) { + copyTags(*loadedTag); + storage->saveData(serverId, loadedTag->toBinaryNbt(), DBHelpers::Category::All); return Boolean::newBoolean(true); } } } } + return Boolean::newBoolean(false); } CATCH_AND_THROW @@ -462,31 +542,59 @@ Local McClass::setPlayerNbtTags(Arguments const& args) { Local McClass::deletePlayerNbt(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); + if (!mce::UUID::canParse(args[0].asString().toString())) { + throw CreateExceptionWithInfo( + __FUNCTION__, + fmt::format("{0} is not a valid UUID", args[0].asString().toString()) + ); + } try { - mce::UUID uuid = mce::UUID::fromString(args[0].asString().toString()); - if (uuid == mce::UUID::EMPTY()) { - throw std::invalid_argument(args[0].asString().toString() + " is not a valid UUID"); - } - auto storage = ll::service::getLevel().transform([](auto& level) { return &level.getLevelStorage(); }); - if (!storage) { - return Boolean::newBoolean(false); - } - auto playerIds = storage->getCompoundTag("player_" + uuid.asString(), DBHelpers::Category::Player); - if (!playerIds) { - return Boolean::newBoolean(false); - } - for (auto& [type, id] : *playerIds) { - if (!id.is_string()) { - continue; - } - std::string& key = id.get(); - if (type == "ServerId") { - storage->deleteData(key, ::DBHelpers::Category::Player); - } else { - storage->deleteData("player_" + key, ::DBHelpers::Category::Player); + auto storage = ll::service::getDBStorage(); + + if (auto playerKey = "player_" + args[0].asString().toString(); storage->hasKey(playerKey, DBHelpers::Category::All)) { + if (auto data = storage->getCompoundTag(playerKey, DBHelpers::Category::All); data) { + if (data->contains("ServerId", Tag::String)) { + if (auto serverId = (*data)["ServerId"].get(); !serverId.empty()) { + storage->deleteData(serverId, DBHelpers::Category::All); + } + } + return Boolean::newBoolean(true); } } - return Boolean::newBoolean(true); + return Boolean::newBoolean(false); + } + CATCH_AND_THROW +} + +Local McClass::getAllPlayerUuids(Arguments const& args) { + if (args.size() >= 1) CHECK_ARG_TYPE(args[1], ValueKind::kBoolean); // isOnlineMode (default: true) + + try { + auto isOnlineMode = args.size() >= 2 && args[1].asBoolean().value(); + + auto arr = Array::newArray(); + ll::service::getDBStorage()->forEachKeyWithPrefix( + "player_", + DBHelpers::Category::All, + [&](std::string_view key, std::string_view content) { + if (key.size() != 36) return; + + auto data = CompoundTag::fromBinaryNbt(content); + if (!data) return; + if (!data->contains("ServerId", Tag::String) || (*data)["ServerId"].get().empty()) return; + + auto msaId = data->contains("MsaId", Tag::String) ? (*data)["MsaId"].get() : ""; + auto selfSignedId = + data->contains("SelfSignedId", Tag::String) ? (*data)["SelfSignedId"].get() : ""; + + if (!msaId.empty() && selfSignedId != key && isOnlineMode) { + arr.add(String::newString(msaId)); + } else if (!isOnlineMode && !selfSignedId.empty() && msaId != key) { + arr.add(String::newString(selfSignedId)); + } + } + ); + return arr; } CATCH_AND_THROW } From 786909760b7d3e94d988533f6af5c0d895f37784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:11:00 +0800 Subject: [PATCH 10/17] feat: add some apis --- CHANGELOG.md | 9 +++++ docs/apis/DataAPI/DataBase.md | 18 ++++++++- docs/apis/DataAPI/DataBase.zh.md | 14 +++++++ src/legacy/api/DatabaseAPI.cpp | 9 +++++ src/legacy/api/DatabaseAPI.h | 1 + src/legacy/db/Session.cpp | 2 + src/legacy/db/Session.h | 7 ++++ src/legacy/db/impl/sqlite/Session.cpp | 57 +++++++++++++++++++++++++++ src/legacy/db/impl/sqlite/Session.h | 1 + 9 files changed, 117 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bfd13ea..5c75215b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added name access APIs: `be.setCustomName`, `be.getCustomName`, `en.getCustomName`, `it.getDisplayName` @zimuya4153 +- Added player data API: + `mc.getAllPlayerUuids([isOnlineMode])` for enumerating saved player UUIDs +- Added database API: + `DBSession::backup(path)` for backing up SQL sessions - Added `VaillanI18n` API for modifying Minecraft vanilla language data @zimuya4153 @xianyubb @@ -39,6 +43,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `File.readFrom(path[,isBinary])` now supports binary reads; `File.writeTo(path, text)` now accepts `ByteBuffer`; `it.setLore([])` now clears custom lore instead of failing @zimuya4153 +- Improved player NBT APIs: + `mc.getPlayerNbt(uuid)` now returns `Null` when no saved data exists and can + read online players directly; `mc.setPlayerNbt(uuid, nbt[, forceCreate, + isOnlineMode])` now supports optional offline record creation; player NBT + APIs now validate UUID input more strictly @zimuya4153 ## [0.18.2] - 2026-04-17 diff --git a/docs/apis/DataAPI/DataBase.md b/docs/apis/DataAPI/DataBase.md index 7be76a8c..699613a9 100644 --- a/docs/apis/DataAPI/DataBase.md +++ b/docs/apis/DataAPI/DataBase.md @@ -209,6 +209,22 @@ The `query` method returns: --- +### Backup Database + +`session.backup(path)` + +* **Parameters:** +* `path`: `String` + Destination path of the backup database file. + + +* **Returns:** Whether the backup operation succeeded. +* **Return Type:** `Boolean` + +> Currently this is implemented for SQLite sessions. + +--- + ### Close Database Session `session.close()` @@ -432,4 +448,4 @@ mc.regPlayerCmd("getcoin", "Get a coin!", (pl, args)=>{ dat[pl.realName]++; modified[pl.realName]++; }); -``` \ No newline at end of file +``` diff --git a/docs/apis/DataAPI/DataBase.zh.md b/docs/apis/DataAPI/DataBase.zh.md index 1ea512b8..f488d1f2 100644 --- a/docs/apis/DataAPI/DataBase.zh.md +++ b/docs/apis/DataAPI/DataBase.zh.md @@ -203,6 +203,20 @@ SQL数据库适用于使用SQL语句处理大量的关系型数据。接口底 +### 备份数据库 + +`session.backup(path)` + +- 参数: + - path : `String` + 备份数据库文件的目标路径 +- 返回值:备份操作是否成功 +- 返回值类型:`Boolean` + +> 目前该接口只为 SQLite 会话实现。 + + + ### 关闭数据库会话 `session.close()` diff --git a/src/legacy/api/DatabaseAPI.cpp b/src/legacy/api/DatabaseAPI.cpp index 5533ffc7..448d76c3 100644 --- a/src/legacy/api/DatabaseAPI.cpp +++ b/src/legacy/api/DatabaseAPI.cpp @@ -21,6 +21,7 @@ ClassDefine DBSessionClassBuilder = defineClass( .instanceFunction("prepare", &DBSessionClass::prepare) .instanceFunction("close", &DBSessionClass::close) .instanceFunction("isOpen", &DBSessionClass::isOpen) + .instanceFunction("backup", &DBSessionClass::backup) .build(); ClassDefine DBStmtClassBuilder = defineClass("DBStmt") @@ -347,6 +348,14 @@ Local DBSessionClass::isOpen(Arguments const& args) const { } CATCH_AND_THROW } +Local DBSessionClass::backup(Arguments const& args) const { + CHECK_ARGS_COUNT(args, 1); + CHECK_ARG_TYPE(args[0], ValueKind::kString); + try { + return Boolean::newBoolean(session->backup(args[0].asString().toU8string())); + } + CATCH_AND_THROW +} //////////////////// Classes DBStmt //////////////////// diff --git a/src/legacy/api/DatabaseAPI.h b/src/legacy/api/DatabaseAPI.h index 16ea9dfb..9be141eb 100644 --- a/src/legacy/api/DatabaseAPI.h +++ b/src/legacy/api/DatabaseAPI.h @@ -46,6 +46,7 @@ class DBSessionClass : public ScriptClass { Local prepare(Arguments const& args) const; Local close(Arguments const& args) const; Local isOpen(Arguments const& args) const; + Local backup(Arguments const& args) const; }; extern ClassDefine DBSessionClassBuilder; diff --git a/src/legacy/db/Session.cpp b/src/legacy/db/Session.cpp index 0fc5858d..53a9a367 100644 --- a/src/legacy/db/Session.cpp +++ b/src/legacy/db/Session.cpp @@ -40,6 +40,8 @@ ResultSet Session::query(std::string const& query) { std::string Session::getLastError() const { throw std::runtime_error("Session::getLastError: Not implemented"); } +bool Session::backup(std::filesystem::path const& path) { return false; } + std::weak_ptr Session::getOrSetSelf() { if (self.expired()) { IF_ENDBG lse::LegacyScriptEngine::getLogger().debug("Session::getOrSetSelf: `self` expired, trying fetching"); diff --git a/src/legacy/db/Session.h b/src/legacy/db/Session.h index db0d306a..e7da6a40 100644 --- a/src/legacy/db/Session.h +++ b/src/legacy/db/Session.h @@ -122,6 +122,13 @@ class Session { * @return DBType The database type */ virtual DBType getType() = 0; + /** + * @brief Backup the database to a file. + * + * @param backupPath Path to the backup file + * @return bool Success or not + */ + virtual bool backup(std::filesystem::path const& backupPath); /** * @brief Get or set the self pointer * diff --git a/src/legacy/db/impl/sqlite/Session.cpp b/src/legacy/db/impl/sqlite/Session.cpp index bd7e5871..29a25107 100644 --- a/src/legacy/db/impl/sqlite/Session.cpp +++ b/src/legacy/db/impl/sqlite/Session.cpp @@ -127,6 +127,63 @@ bool SQLiteSession::isOpen() { return conn != nullptr; } DBType SQLiteSession::getType() { return DBType::SQLite; } +bool SQLiteSession::backup(std::filesystem::path const& backupPath) { + if (!conn) { + IF_ENDBG lse::LegacyScriptEngine::getLogger().error("SQLiteSession::backup: No open database connection."); + return false; + } + + struct DestDb { + sqlite3* mDb{nullptr}; + explicit DestDb(std::filesystem::path const& path) { + if (sqlite3_open_v2( + ll::string_utils::u8str2str(path.u8string()).c_str(), + &mDb, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + nullptr + ) + != SQLITE_OK) { + if (mDb) sqlite3_close(mDb); + mDb = nullptr; + return; + } + } + ~DestDb() { + if (mDb) sqlite3_close(mDb); + } + } destDb{backupPath}; + if (!destDb.mDb) { + IF_ENDBG lse::LegacyScriptEngine::getLogger().error( + "SQLiteSession::backup: Failed to open destination database '{}'", + backupPath.string() + ); + return false; + } + + struct BackupObj { + sqlite3_backup* mBackup{nullptr}; + BackupObj(sqlite3* srcDb, sqlite3* destDb) : mBackup(sqlite3_backup_init(destDb, "main", srcDb, "main")) {} + ~BackupObj() { + if (mBackup) sqlite3_backup_finish(mBackup); + } + } backup{conn, destDb.mDb}; + if (!backup.mBackup) { + IF_ENDBG lse::LegacyScriptEngine::getLogger().error("SQLiteSession::backup: Failed to initialize backup."); + return false; + } + + if (sqlite3_backup_step(backup.mBackup, -1) != SQLITE_DONE) { + IF_ENDBG lse::LegacyScriptEngine::getLogger().error("SQLiteSession::backup: Backup step failed."); + return false; + } + + IF_ENDBG lse::LegacyScriptEngine::getLogger().info( + "SQLiteSession::backup: Database successfully backed up to '{}'", + backupPath.string() + ); + return true; +} + SharedPointer SQLiteSession::operator<<(std::string const& query) { return prepare(query, true); } } // namespace DB diff --git a/src/legacy/db/impl/sqlite/Session.h b/src/legacy/db/impl/sqlite/Session.h index 7d57994d..326e0264 100644 --- a/src/legacy/db/impl/sqlite/Session.h +++ b/src/legacy/db/impl/sqlite/Session.h @@ -22,6 +22,7 @@ class SQLiteSession : public Session { void close() override; bool isOpen() override; DBType getType() override; + bool backup(std::filesystem::path const& backupPath) override; SharedPointer operator<<(std::string const& query) override; From 7a9870fcfa2ec3c5c74c96b58e6d5e3fb35791c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sat, 4 Jul 2026 17:57:52 +0800 Subject: [PATCH 11/17] feat: add error output for SQLiteSession::backup --- src/legacy/db/impl/sqlite/Session.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/legacy/db/impl/sqlite/Session.cpp b/src/legacy/db/impl/sqlite/Session.cpp index 29a25107..96554ffe 100644 --- a/src/legacy/db/impl/sqlite/Session.cpp +++ b/src/legacy/db/impl/sqlite/Session.cpp @@ -136,6 +136,10 @@ bool SQLiteSession::backup(std::filesystem::path const& backupPath) { struct DestDb { sqlite3* mDb{nullptr}; explicit DestDb(std::filesystem::path const& path) { + { + std::error_code ec; + std::filesystem::create_directories(path.parent_path(), ec); + } if (sqlite3_open_v2( ll::string_utils::u8str2str(path.u8string()).c_str(), &mDb, @@ -160,10 +164,11 @@ bool SQLiteSession::backup(std::filesystem::path const& backupPath) { return false; } - struct BackupObj { + sqlite3_exec(conn, "ROLLBACK", nullptr, nullptr, nullptr); + struct Backup { sqlite3_backup* mBackup{nullptr}; - BackupObj(sqlite3* srcDb, sqlite3* destDb) : mBackup(sqlite3_backup_init(destDb, "main", srcDb, "main")) {} - ~BackupObj() { + Backup(sqlite3* srcDb, sqlite3* destDb) : mBackup(sqlite3_backup_init(destDb, "main", srcDb, "main")) {} + ~Backup() { if (mBackup) sqlite3_backup_finish(mBackup); } } backup{conn, destDb.mDb}; @@ -172,8 +177,9 @@ bool SQLiteSession::backup(std::filesystem::path const& backupPath) { return false; } - if (sqlite3_backup_step(backup.mBackup, -1) != SQLITE_DONE) { - IF_ENDBG lse::LegacyScriptEngine::getLogger().error("SQLiteSession::backup: Backup step failed."); + if (auto rc = sqlite3_backup_step(backup.mBackup, -1); rc != SQLITE_DONE) { + IF_ENDBG lse::LegacyScriptEngine::getLogger() + .error("SQLiteSession::backup: Backup step failed with code {} ({})", rc, sqlite3_errstr(rc)); return false; } From 281c50cb5e92ee7d09bc197dc7e848a2eb8d6928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sun, 5 Jul 2026 11:06:30 +0800 Subject: [PATCH 12/17] fix: fix the NbtCompound::setTag --- src/legacy/api/NbtAPI.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/legacy/api/NbtAPI.cpp b/src/legacy/api/NbtAPI.cpp index 8bf180e7..0f3c736a 100644 --- a/src/legacy/api/NbtAPI.cpp +++ b/src/legacy/api/NbtAPI.cpp @@ -1678,25 +1678,25 @@ Local NbtCompoundClass::setTag(Arguments const& args) const { if (IsInstanceOf( args[1] )) { // Assignment refers to the rvalue, so the Tag is copied before assignment - getPtr()->at(key) = NbtByteClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtByteClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtShortClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtShortClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtIntClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtIntClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtLongClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtLongClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtFloatClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtFloatClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtDoubleClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtDoubleClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtStringClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtStringClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = NbtByteArrayClass::extract(args[1])->copy()->as(); + (*getPtr())[key] = NbtByteArrayClass::extract(args[1])->copy()->as(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = *NbtListClass::extract(args[1])->copyList(); + (*getPtr())[key] = *NbtListClass::extract(args[1])->copyList(); } else if (IsInstanceOf(args[1])) { - getPtr()->at(key) = *NbtCompoundClass::extract(args[1])->clone(); + (*getPtr())[key] = *NbtCompoundClass::extract(args[1])->clone(); } else { throw CreateExceptionWithInfo(__FUNCTION__, "Unknown type! Cannot set Tag into Compound"); } From 75bb5c43eeb68d4f2bcb7c7ea763bfad04bb00ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:19:33 +0800 Subject: [PATCH 13/17] feat: add some apis --- CHANGELOG.md | 11 +++ docs/apis/GameAPI/Player.md | 2 + docs/apis/GameAPI/Player.zh.md | 2 + docs/apis/NbtAPI/NBT.md | 43 ++++++++++ docs/apis/NbtAPI/NBT.zh.md | 43 ++++++++++ docs/apis/NbtAPI/NBTCompound.md | 9 +- docs/apis/NbtAPI/NBTCompound.zh.md | 11 ++- docs/apis/README.md | 1 + docs/apis/README.zh.md | 1 + src/legacy/api/NbtAPI.cpp | 133 ++++++++++++++++++++++++++--- src/legacy/api/NbtAPI.h | 14 ++- src/legacy/api/PlayerAPI.cpp | 26 ++++++ src/legacy/api/PlayerAPI.h | 2 + src/legacy/main/BindAPIs.cpp | 1 + 14 files changed, 278 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c75215b..940145d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added gameplay helpers: `en.setCustomName`, `mc.summonMob`, `mc.loadMob`, `it.addCount`, `it.removeCount`, `it.setCount`, `system.randomUuid` @zimuya4153 +- Added player state APIs: + `pl.isSwimming`, `pl.isCrawling` @zimuya4153 - Added name access APIs: `be.setCustomName`, `be.getCustomName`, `en.getCustomName`, `it.getDisplayName` @zimuya4153 @@ -30,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `DBSession::backup(path)` for backing up SQL sessions - Added `VaillanI18n` API for modifying Minecraft vanilla language data @zimuya4153 @xianyubb +- Added `toSNBT([space[,format]])` to all NBT object types @zimuya4153 ### Changed @@ -48,6 +51,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 read online players directly; `mc.setPlayerNbt(uuid, nbt[, forceCreate, isOnlineMode])` now supports optional offline record creation; player NBT APIs now validate UUID input more strictly @zimuya4153 +- Improved NBT serialization: + `NbtCompound::toSNBT([space[,format]])` now supports explicitly selecting + `SnbtFormat`; one-argument usage still defaults to `SnbtFormat::ForceQuote` + @zimuya4153 + +### Fixed + +- Fixed `NbtCompound::setTag` @zimuya4153 ## [0.18.2] - 2026-04-17 diff --git a/docs/apis/GameAPI/Player.md b/docs/apis/GameAPI/Player.md index c0a5420d..89575629 100644 --- a/docs/apis/GameAPI/Player.md +++ b/docs/apis/GameAPI/Player.md @@ -103,6 +103,8 @@ properties. | pl.isSleeping | Player is sleeping | `Boolean` | | pl.isMoving | Player is moving | `Boolean` | | pl.isSneaking | Player is sneaking | `Boolean` | +| pl.isSwimming | Whether the player is swimming (Added in 0.19.0) | `Boolean` | +| pl.isCrawling | Whether the player is crawling (Added in 0.19.0) | `Boolean` | These object properties are read-only and cannot be modified. in: diff --git a/docs/apis/GameAPI/Player.zh.md b/docs/apis/GameAPI/Player.zh.md index 6c02a624..3459feba 100644 --- a/docs/apis/GameAPI/Player.zh.md +++ b/docs/apis/GameAPI/Player.zh.md @@ -101,6 +101,8 @@ | pl.isSleeping | 玩家是否正在睡觉 | `Boolean` | | pl.isMoving | 玩家是否正在移动 | `Boolean` | | pl.isSneaking | 玩家是否正在潜行 | `Boolean` | +| pl.isSwimming | 玩家是否正在游泳(在 0.19.0 时被加入) | `Boolean` | +| pl.isCrawling | 玩家是否正在爬行(在 0.19.0 时被加入) | `Boolean` | 这些对象属性都是只读的,无法被修改。其中: diff --git a/docs/apis/NbtAPI/NBT.md b/docs/apis/NbtAPI/NBT.md index 4ef88c8f..8a0fc75d 100644 --- a/docs/apis/NbtAPI/NBT.md +++ b/docs/apis/NbtAPI/NBT.md @@ -66,3 +66,46 @@ If this NBT object stores the `ByteArray` Type, the output byte string will be b > If there is a need for deserialization, please use the **SNBT** interface provided by the NBT tag class. +#### Convert NBT Object to SNBT String + +`nbt.toSNBT([space[,format]])` + +- Parameter: + - space : `Integer` + (Optional parameter) Indent width. Pass `-1` or omit it to disable pretty formatting. + - format : `Enum` + (Optional parameter) SNBT output format, using a value from the `SnbtFormat` enum. +- Return value: The SNBT form of the NBT object. +- Return value type: `String` + +Calling `nbt.toSNBT()` or `nbt.toSNBT(space)` uses `SnbtFormat.ForceQuote` by default. +If you need to specify another SNBT format explicitly, use `nbt.toSNBT(space, format)`. + + +## 🎨 SnbtFormat Enum + +`SnbtFormat` is the enum used by `NbtCompound::toSNBT([space[,format]])`. + +The following values are available: + +| Enum Value | Numeric Value | Description | +|----------------------------|---------------|-------------| +| `SnbtFormat.Minimize` | `0` | Minimal output. | +| `SnbtFormat.CompoundLineFeed` | `1` | Insert line feeds for compound members. | +| `SnbtFormat.ArrayLineFeed` | `2` | Insert line feeds for arrays / lists. | +| `SnbtFormat.Colored` | `4` | Add color formatting to output. | +| `SnbtFormat.Console` | `8` | Use console-oriented formatting. | +| `SnbtFormat.ForceAscii` | `16` | Force ASCII output. | +| `SnbtFormat.ForceQuote` | `32` | Force quoted string output. | +| `SnbtFormat.CommentMarks` | `64` | Add comment-style marks. | +| `SnbtFormat.Jsonify` | `96` | Preset: `ForceQuote | CommentMarks`. | +| `SnbtFormat.PartialLineFeed` | `1` | Alias of `CompoundLineFeed`. | +| `SnbtFormat.AlwaysLineFeed` | `3` | Preset: `CompoundLineFeed | ArrayLineFeed`. | +| `SnbtFormat.PrettyFilePrint` | `1` | Alias of `PartialLineFeed`. | +| `SnbtFormat.PrettyChatPrint` | `5` | Preset: `PrettyFilePrint | Colored`. | +| `SnbtFormat.PrettyConsolePrint` | `13` | Preset: `PrettyFilePrint | Colored | Console`. | + +`CompoundLineFeed`, `ArrayLineFeed`, `Colored`, `Console`, `ForceAscii`, `ForceQuote`, and `CommentMarks` are bit flags. +The other entries above are predefined combinations or aliases. + + diff --git a/docs/apis/NbtAPI/NBT.zh.md b/docs/apis/NbtAPI/NBT.zh.md index 138608e2..0d058b6d 100644 --- a/docs/apis/NbtAPI/NBT.zh.md +++ b/docs/apis/NbtAPI/NBT.zh.md @@ -66,3 +66,46 @@ > 如果有反序列化的需求,请使用 NBT标签类 提供的的 **SNBT** 接口 +#### 将 NBT 对象转换为 SNBT 字符串 + +`nbt.toSNBT([space[,format]])` + +- 参数 + - space : `Integer` + (可选参数)缩进空格数。传入 `-1` 或省略时不进行格式化输出。 + - format : `Enum` + (可选参数)SNBT 输出格式,使用 `SnbtFormat` 枚举中的值。 +- 返回值:对应的 SNBT 字符串 +- 返回值类型:`String` + +调用 `nbt.toSNBT()` 或 `nbt.toSNBT(space)` 时,默认使用 `SnbtFormat.ForceQuote`。 +如果需要显式指定其他 SNBT 格式,请使用 `nbt.toSNBT(space, format)`。 + + +## 🎨 SnbtFormat 枚举 + +`SnbtFormat` 是 `NbtCompound::toSNBT([space[,format]])` 使用的枚举。 + +可用的枚举值如下: + +| 枚举值 | 数值 | 说明 | +| ------ | ---- | ---- | +| `SnbtFormat.Minimize` | `0` | 最小化输出。 | +| `SnbtFormat.CompoundLineFeed` | `1` | 对 Compound 成员进行换行。 | +| `SnbtFormat.ArrayLineFeed` | `2` | 对数组 / 列表内容进行换行。 | +| `SnbtFormat.Colored` | `4` | 为输出添加颜色格式。 | +| `SnbtFormat.Console` | `8` | 使用面向控制台的格式。 | +| `SnbtFormat.ForceAscii` | `16` | 强制使用 ASCII 输出。 | +| `SnbtFormat.ForceQuote` | `32` | 强制字符串使用引号输出。 | +| `SnbtFormat.CommentMarks` | `64` | 添加注释风格标记。 | +| `SnbtFormat.Jsonify` | `96` | 预设组合:`ForceQuote | CommentMarks`。 | +| `SnbtFormat.PartialLineFeed` | `1` | `CompoundLineFeed` 的别名。 | +| `SnbtFormat.AlwaysLineFeed` | `3` | 预设组合:`CompoundLineFeed | ArrayLineFeed`。 | +| `SnbtFormat.PrettyFilePrint` | `1` | `PartialLineFeed` 的别名。 | +| `SnbtFormat.PrettyChatPrint` | `5` | 预设组合:`PrettyFilePrint | Colored`。 | +| `SnbtFormat.PrettyConsolePrint` | `13` | 预设组合:`PrettyFilePrint | Colored | Console`。 | + +其中 `CompoundLineFeed`、`ArrayLineFeed`、`Colored`、`Console`、`ForceAscii`、`ForceQuote`、`CommentMarks` 是位标记。 +其余项是预设组合或别名。 + + diff --git a/docs/apis/NbtAPI/NBTCompound.md b/docs/apis/NbtAPI/NBTCompound.md index 2867c938..6e292981 100644 --- a/docs/apis/NbtAPI/NBTCompound.md +++ b/docs/apis/NbtAPI/NBTCompound.md @@ -115,7 +115,7 @@ Possible return values ​​are: `NBT.End` `NBT.Byte` `NBT.Short` `NBT.Int` `NB The name of the key to operate on. - tag: `NBT Object` NBT object to be written (it carries specific NBT data). - The write data type must be the same as the data type stored in the value corresponding to the key, and the key name may not exist. + If the key already exists, the original tag will be replaced. If the key does not exist, a new key-value pair will be created. - Return value: CompoundTag itself. - Return value type: `NBTCompound` @@ -201,16 +201,21 @@ If an item of Compound stores a `List` or `Compound` type NBT, it will recursive #### Serialize NBT Tag Object to SNBT -`nbt.toSNBT([space])` +`comp.toSNBT([space[,format]])` - Parameter: - space : `Integer` (Optional parameter) If you want to format the output string, pass this parameter. Represents the number of spaces per indent, so the resulting string is more human-readable. This parameter defaults to `-1`, i.e. the output string is not formatted. + - format : `Enum` + (Optional parameter) SNBT output format, using a value from the `SnbtFormat` enum. - Return value: The converted SNBT string. - Return value type: `String` +Calling `comp.toSNBT()` or `comp.toSNBT(space)` uses `SnbtFormat.ForceQuote` by default. +If you need to specify another SNBT format explicitly, use `comp.toSNBT(space, format)`. + > In addition to the normal binary NBT, another type of NBT that players are more familiar with is plain text, usually used in [commands](https://minecraft.wiki/w/Commands). This format is often referred to as **SNBT** (**Binary Named Tag,**, **S**tringified **NBT**) > > --- Minecraft Wiki diff --git a/docs/apis/NbtAPI/NBTCompound.zh.md b/docs/apis/NbtAPI/NBTCompound.zh.md index c095bfc2..7d76f736 100644 --- a/docs/apis/NbtAPI/NBTCompound.zh.md +++ b/docs/apis/NbtAPI/NBTCompound.zh.md @@ -114,8 +114,8 @@ local nbt = NbtCompound({ - key:`String` 要操作的键名 - tag: `NBT Object` - 要写入的 NBT 对象(它承载着具体的NBT数据) - 写入数据类型必须和键对应的值储存的数据类型一致,键名可以不存在 + 要写入的 NBT 对象(它承载着具体的 NBT 数据) + 如果键已存在,会直接替换原有标签;如果键不存在,则会新建这个键值对 - 返回值:CompoundTag 自身 - 返回值类型:`NBTCompound` @@ -201,16 +201,21 @@ local nbt = NbtCompound({ #### 将 NBT 标签对象序列化为 SNBT -`nbt.toSNBT([space])` +`comp.toSNBT([space[,format]])` - 参数 - space : `Integer` (可选参数)如果要格式化输出的字符串,则传入此参数 代表每个缩进的空格数量,这样生成的字符串更适合人阅读 此参数默认为 -1,即不对输出字符串进行格式化 + - format : `Enum` + (可选参数)SNBT 输出格式,使用 `SnbtFormat` 枚举中的值 - 返回值:对应的 SNBT 字符串 - 返回值类型:`String` +调用 `comp.toSNBT()` 或 `comp.toSNBT(space)` 时,默认使用 `SnbtFormat.ForceQuote`。 +如果需要显式指定其他 SNBT 格式,请使用 `comp.toSNBT(space, format)`。 + > 除了普通的二进制 NBT 之外,另一种玩家更熟悉的 NBT 是纯文本形式的,通常在[命令](https://zh.minecraft.wiki/w/命令)里使用。这种格式常被称为**SNBT**(**字符串化的二进制命名标签**,**S**tringified **NBT**) > > --- Minecraft Wiki diff --git a/docs/apis/README.md b/docs/apis/README.md index 68b0649d..f4b51ef5 100644 --- a/docs/apis/README.md +++ b/docs/apis/README.md @@ -47,6 +47,7 @@ In addition to the above standard types, there are also some engine-defined obje - `Device` - Player equipment information object (see player for details) - `Container` - container object (see container for details) - `Objective` - Scored item object (see Scoreboard for details) +- `Packet` - Packet object (see Packet for details) - `NBT` - NBT Tag object (see NBT for details) - `SimpleForm` - Normal form objects (see Form Builder for details) - `CustomForm` - Custom form objects (see Form Builder for details) diff --git a/docs/apis/README.zh.md b/docs/apis/README.zh.md index cc90f7ba..7b974f75 100644 --- a/docs/apis/README.zh.md +++ b/docs/apis/README.zh.md @@ -47,6 +47,7 @@ - `Device` - 玩家设备信息对象(详见 玩家)- 在脚本引擎内实际注册的类名:`LLSE_Device` - `Container` - 容器对象(详见 容器)- 在脚本引擎内实际注册的类名:`LLSE_Container` - `Objective` - 计分项对象(详见 计分板)- 在脚本引擎内实际注册的类名:`LLSE_Objective` +- `Packet` - 数据包对象(详见 数据包)- 在脚本引擎内实际注册的类名:`LLSE_Packet` - `NBT` - NBT Tag对象(详见 NBT) - `SimpleForm` - 普通表单对象(详见 表单构建器)- 在脚本引擎内实际注册的类名:`LLSE_SimpleForm` - `CustomForm` - 自定义表单对象(详见 表单构建器)- 在脚本引擎内实际注册的类名:`LLSE_CustomForm` diff --git a/src/legacy/api/NbtAPI.cpp b/src/legacy/api/NbtAPI.cpp index 0f3c736a..f583aaf7 100644 --- a/src/legacy/api/NbtAPI.cpp +++ b/src/legacy/api/NbtAPI.cpp @@ -26,26 +26,45 @@ using magic_enum::enum_cast; ClassDefine NbtStaticBuilder = defineClass("NBT") .function("parseSNBT", &NbtStatic::parseSNBT) .function("parseBinaryNBT", &NbtStatic::parseBinaryNBT) - .property("End", &NbtStatic::getType) - .property("Byte", &NbtStatic::getType) - .property("Short", &NbtStatic::getType) - .property("Int", &NbtStatic::getType) - .property("Long", &NbtStatic::getType) - .property("Float", &NbtStatic::getType) - .property("Double", &NbtStatic::getType) - .property("ByteArray", &NbtStatic::getType) - .property("String", &NbtStatic::getType) - .property("List", &NbtStatic::getType) - .property("Compound", &NbtStatic::getType) + .property("End", &NbtStatic::getValue) + .property("Byte", &NbtStatic::getValue) + .property("Short", &NbtStatic::getValue) + .property("Int", &NbtStatic::getValue) + .property("Long", &NbtStatic::getValue) + .property("Float", &NbtStatic::getValue) + .property("Double", &NbtStatic::getValue) + .property("ByteArray", &NbtStatic::getValue) + .property("String", &NbtStatic::getValue) + .property("List", &NbtStatic::getValue) + .property("Compound", &NbtStatic::getValue) // For Compatibility .function("createTag", &NbtStatic::newTag) .function("newTag", &NbtStatic::newTag) .build(); +ClassDefine SnbtFormatEnumBuilder = + defineClass("SnbtFormatEnumBuilder") + .property("Minimize", &NbtStatic::getValue) + .property("CompoundLineFeed", &NbtStatic::getValue) + .property("ArrayLineFeed", &NbtStatic::getValue) + .property("Colored", &NbtStatic::getValue) + .property("Console", &NbtStatic::getValue) + .property("ForceAscii", &NbtStatic::getValue) + .property("ForceQuote", &NbtStatic::getValue) + .property("CommentMarks", &NbtStatic::getValue) + .property("Jsonify", &NbtStatic::getValue) + .property("PartialLineFeed", &NbtStatic::getValue) + .property("AlwaysLineFeed", &NbtStatic::getValue) + .property("PrettyFilePrint", &NbtStatic::getValue) + .property("PrettyChatPrint", &NbtStatic::getValue) + .property("PrettyConsolePrint", &NbtStatic::getValue) + .build(); + ClassDefine NbtByteClassBuilder = defineClass("NbtByte") .constructor(&NbtByteClass::constructor) .instanceFunction("getType", &NbtByteClass::getType) + .instanceFunction("toSNBT", &NbtByteClass::toSNBT) .instanceFunction("toString", &NbtByteClass::toString) .instanceFunction("set", &NbtByteClass::set) .instanceFunction("get", &NbtByteClass::get) @@ -54,6 +73,7 @@ ClassDefine NbtByteClassBuilder = defineClass("NbtBy ClassDefine NbtShortClassBuilder = defineClass("NbtShort") .constructor(&NbtShortClass::constructor) .instanceFunction("getType", &NbtShortClass::getType) + .instanceFunction("toSNBT", &NbtShortClass::toSNBT) .instanceFunction("toString", &NbtShortClass::toString) .instanceFunction("set", &NbtShortClass::set) .instanceFunction("get", &NbtShortClass::get) @@ -62,6 +82,7 @@ ClassDefine NbtShortClassBuilder = defineClass("Nb ClassDefine NbtIntClassBuilder = defineClass("NbtInt") .constructor(&NbtIntClass::constructor) .instanceFunction("getType", &NbtIntClass::getType) + .instanceFunction("toSNBT", &NbtIntClass::toSNBT) .instanceFunction("toString", &NbtIntClass::toString) .instanceFunction("set", &NbtIntClass::set) .instanceFunction("get", &NbtIntClass::get) @@ -70,6 +91,7 @@ ClassDefine NbtIntClassBuilder = defineClass("NbtInt") ClassDefine NbtLongClassBuilder = defineClass("NbtLong") .constructor(&NbtLongClass::constructor) .instanceFunction("getType", &NbtLongClass::getType) + .instanceFunction("toSNBT", &NbtLongClass::toSNBT) .instanceFunction("toString", &NbtLongClass::toString) .instanceFunction("set", &NbtLongClass::set) .instanceFunction("get", &NbtLongClass::get) @@ -78,6 +100,7 @@ ClassDefine NbtLongClassBuilder = defineClass("NbtLo ClassDefine NbtFloatClassBuilder = defineClass("NbtFloat") .constructor(&NbtFloatClass::constructor) .instanceFunction("getType", &NbtFloatClass::getType) + .instanceFunction("toSNBT", &NbtFloatClass::toSNBT) .instanceFunction("toString", &NbtFloatClass::toString) .instanceFunction("set", &NbtFloatClass::set) .instanceFunction("get", &NbtFloatClass::get) @@ -86,6 +109,7 @@ ClassDefine NbtFloatClassBuilder = defineClass("Nb ClassDefine NbtDoubleClassBuilder = defineClass("NbtDouble") .constructor(&NbtDoubleClass::constructor) .instanceFunction("getType", &NbtDoubleClass::getType) + .instanceFunction("toSNBT", &NbtDoubleClass::toSNBT) .instanceFunction("toString", &NbtDoubleClass::toString) .instanceFunction("set", &NbtDoubleClass::set) .instanceFunction("get", &NbtDoubleClass::get) @@ -94,6 +118,7 @@ ClassDefine NbtDoubleClassBuilder = defineClass( ClassDefine NbtStringClassBuilder = defineClass("NbtString") .constructor(&NbtStringClass::constructor) .instanceFunction("getType", &NbtStringClass::getType) + .instanceFunction("toSNBT", &NbtStringClass::toSNBT) .instanceFunction("toString", &NbtStringClass::toString) .instanceFunction("set", &NbtStringClass::set) .instanceFunction("get", &NbtStringClass::get) @@ -103,6 +128,7 @@ ClassDefine NbtByteArrayClassBuilder = defineClass("NbtByteArray") .constructor(&NbtByteArrayClass::constructor) .instanceFunction("getType", &NbtByteArrayClass::getType) + .instanceFunction("toSNBT", &NbtByteArrayClass::toSNBT) .instanceFunction("toString", &NbtByteArrayClass::toString) .instanceFunction("set", &NbtByteArrayClass::set) .instanceFunction("get", &NbtByteArrayClass::get) @@ -111,6 +137,7 @@ ClassDefine NbtByteArrayClassBuilder = ClassDefine NbtListClassBuilder = defineClass("NbtList") .constructor(&NbtListClass::constructor) .instanceFunction("getType", &NbtListClass::getType) + .instanceFunction("toSNBT", &NbtListClass::toSNBT) .instanceFunction("toString", &NbtListClass::toString) .instanceFunction("getSize", &NbtListClass::getSize) .instanceFunction("getTypeOf", &NbtListClass::getTypeOf) @@ -159,6 +186,23 @@ ClassDefine NbtCompoundClassBuilder = void TagToJson_Compound_Helper(ordered_json& res, CompoundTag* nbt); +template +Local TagToSNBT(TagT* tag, Arguments const& args) { + if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); + if (args.size() >= 2) CHECK_ARG_TYPE(args[1], ValueKind::kNumber); + + if (args.size() <= 1) { + auto indent = args.size() >= 1 ? args[0].asNumber().toInt32() : -1; + return String::newString( + indent == -1 ? tag->toSnbt(SnbtFormat::ForceQuote, 0) : tag->toSnbt(SnbtFormat::ForceQuote, indent) + ); + } + + return String::newString( + tag->toSnbt(static_cast(args[1].asNumber().toInt32()), args[0].asNumber().toInt32()) + ); +} + void TagToJson_List_Helper(ordered_json& res, ListTag const* nbt) { for (auto& tag : *nbt) { switch (tag->getId()) { @@ -378,6 +422,13 @@ Local NbtByteClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtByteClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtByteClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -448,6 +499,13 @@ Local NbtIntClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtIntClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtIntClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -518,6 +576,13 @@ Local NbtShortClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtShortClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtShortClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -588,6 +653,13 @@ Local NbtLongClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtLongClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtLongClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -659,6 +731,13 @@ Local NbtFloatClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtFloatClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtFloatClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -731,6 +810,13 @@ Local NbtDoubleClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtDoubleClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtDoubleClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -803,6 +889,13 @@ Local NbtStringClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtStringClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtStringClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -886,6 +979,13 @@ Local NbtByteArrayClass::get(Arguments const&) const { CATCH_AND_THROW } +Local NbtByteArrayClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtByteArrayClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -1398,6 +1498,13 @@ Local NbtListClass::toArray(Arguments const&) const { CATCH_AND_THROW } +Local NbtListClass::toSNBT(Arguments const& args) const { + try { + return TagToSNBT(getPtr(), args); + } + CATCH_AND_THROW +} + Local NbtListClass::toString(Arguments const& args) const { if (args.size() >= 1) CHECK_ARG_TYPE(args[0], ValueKind::kNumber); @@ -1808,9 +1915,7 @@ Local NbtCompoundClass::toObject(Arguments const&) const { Local NbtCompoundClass::toSNBT(Arguments const& args) const { try { - int indent = args.size() >= 1 ? args[0].asNumber().toInt32() : -1; - if (indent == -1) return String::newString(getPtr()->toSnbt(SnbtFormat::ForceQuote, 0)); - return String::newString(getPtr()->toSnbt(SnbtFormat::PartialLineFeed, indent)); + return TagToSNBT(getPtr(), args); } CATCH_AND_THROW } diff --git a/src/legacy/api/NbtAPI.h b/src/legacy/api/NbtAPI.h index 09a10e3d..da3b6a1e 100644 --- a/src/legacy/api/NbtAPI.h +++ b/src/legacy/api/NbtAPI.h @@ -20,12 +20,13 @@ class NbtStatic : public ScriptClass { static Local parseSNBT(Arguments const& args); static Local parseBinaryNBT(Arguments const& args); - template - static Local getType() { + template + static Local getValue() { return Number::newNumber(static_cast(T)); } }; extern ClassDefine<> NbtStaticBuilder; +extern ClassDefine<> SnbtFormatEnumBuilder; // NBT Byte class NbtByteClass : public ScriptClass { @@ -52,6 +53,7 @@ class NbtByteClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtByteClassBuilder; @@ -82,6 +84,7 @@ class NbtShortClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtShortClassBuilder; @@ -111,6 +114,7 @@ class NbtIntClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtIntClassBuilder; @@ -141,6 +145,7 @@ class NbtLongClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtLongClassBuilder; @@ -171,6 +176,7 @@ class NbtFloatClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtFloatClassBuilder; @@ -201,6 +207,7 @@ class NbtDoubleClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtDoubleClassBuilder; @@ -231,6 +238,7 @@ class NbtStringClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtStringClassBuilder; @@ -261,6 +269,7 @@ class NbtByteArrayClass : public ScriptClass { Local getType(Arguments const& args); Local set(Arguments const& args) const; Local get(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtByteArrayClassBuilder; @@ -308,6 +317,7 @@ class NbtListClass : public ScriptClass { Local getTag(Arguments const& args) const; Local toArray(Arguments const& args) const; + Local toSNBT(Arguments const& args) const; Local toString(Arguments const& args) const; }; extern ClassDefine NbtListClassBuilder; diff --git a/src/legacy/api/PlayerAPI.cpp b/src/legacy/api/PlayerAPI.cpp index cf5f59b8..8c41b680 100644 --- a/src/legacy/api/PlayerAPI.cpp +++ b/src/legacy/api/PlayerAPI.cpp @@ -183,6 +183,8 @@ ClassDefine PlayerClassBuilder = .instanceProperty("isSleeping", &PlayerClass::isSleeping) .instanceProperty("isMoving", &PlayerClass::isMoving) .instanceProperty("isSneaking", &PlayerClass::isSneaking) + .instanceProperty("isSwimming", &PlayerClass::isSwimming) + .instanceProperty("isCrawling", &PlayerClass::isCrawling) .instanceFunction("isOP", &PlayerClass::isOP) .instanceFunction("setPermLevel", &PlayerClass::setPermLevel) @@ -1135,6 +1137,30 @@ Local PlayerClass::isSneaking() const { CATCH_AND_THROW } +Local PlayerClass::isSwimming() const { + try { + Player* player = get(); + if (!player) return {}; + + return Boolean::newBoolean(player->isSwimming()); + } + CATCH_AND_THROW +} + +Local PlayerClass::isCrawling() const { + try { + Player* player = get(); + if (!player) { + return {}; + } + + return Boolean::newBoolean( + SynchedActorDataAccess::getActorFlag(player->getEntityContext(), ActorFlags::Crawling) + ); + } + CATCH_AND_THROW +} + Local PlayerClass::getSpeed() const { try { Player* player = get(); diff --git a/src/legacy/api/PlayerAPI.h b/src/legacy/api/PlayerAPI.h index 6d1bfc6a..0f39da14 100644 --- a/src/legacy/api/PlayerAPI.h +++ b/src/legacy/api/PlayerAPI.h @@ -77,6 +77,8 @@ class PlayerClass : public ScriptClass { Local isSleeping() const; Local isMoving() const; Local isSneaking() const; + Local isSwimming() const; + Local isCrawling() const; Local isOP(Arguments const& args) const; Local setPermLevel(Arguments const& args) const; diff --git a/src/legacy/main/BindAPIs.cpp b/src/legacy/main/BindAPIs.cpp index e7d3415f..0189a178 100644 --- a/src/legacy/main/BindAPIs.cpp +++ b/src/legacy/main/BindAPIs.cpp @@ -57,6 +57,7 @@ void BindAPIs(std::shared_ptr const& engine) { engine->registerNativeClass(LlClassBuilder); engine->registerNativeClass(VersionClassBuilder); engine->registerNativeClass(NbtStaticBuilder); + engine->registerNativeClass(SnbtFormatEnumBuilder); engine->registerNativeClass(TextClassBuilder); engine->registerNativeClass(ParticleColorBuilder); engine->registerNativeClass(DirectionBuilder); From 944409a2bb25be9a9d582c74141224b63badb47c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:39:43 +0800 Subject: [PATCH 14/17] merge --- CHANGELOG.md | 28 +++++++++++++++++++++++++ src/legacy/api/CommandAPI.cpp | 2 +- xmake.lua | 39 +++++++++++++++++------------------ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 940145d4..2bd319f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed `NbtCompound::setTag` @zimuya4153 +## [0.19.0] - 2026-07-04 + +### Changed + +- Improved `BinaryStream`: + `getData([release])` now returns `ByteBuffer`, does not clear by default, and + optionally releases data after reading; `createPacket(pktid[,raw])` now + supports raw packets; scalar `read*` APIs were added; several numeric + `write*` APIs now accept `String` for BigInt-friendly input + @zimuya4153 +- Improved file and item behavior: + `File.readFrom(path[,isBinary])` now supports binary reads; + `File.writeTo(path, text)` now accepts `ByteBuffer`; + `it.setLore([])` now clears custom lore instead of failing @zimuya4153 +- Improved player NBT APIs: + `mc.getPlayerNbt(uuid)` now returns `Null` when no saved data exists and can + read online players directly; `mc.setPlayerNbt(uuid, nbt[, forceCreate, + isOnlineMode])` now supports optional offline record creation; player NBT + APIs now validate UUID input more strictly @zimuya4153 +- Improved NBT serialization: + `NbtCompound::toSNBT([space[,format]])` now supports explicitly selecting + `SnbtFormat`; one-argument usage still defaults to `SnbtFormat::ForceQuote` + @zimuya4153 + +### Fixed + +- Fixed `NbtCompound::setTag` @zimuya4153 + ## [0.18.2] - 2026-04-17 ### Fixed diff --git a/src/legacy/api/CommandAPI.cpp b/src/legacy/api/CommandAPI.cpp index 05cd1453..a4c2b0ce 100644 --- a/src/legacy/api/CommandAPI.cpp +++ b/src/legacy/api/CommandAPI.cpp @@ -171,7 +171,7 @@ Local convertResult(ParamStorageType const& result, CommandOrigin const& return String::newString(std::get(result.value())); } if (result.hold(ParamKind::Kind::Dimension)) { - return Number::newNumber(std::get(result.value()).id); + return Number::newNumber(std::get(result.value()).mValue); } return {}; } diff --git a/xmake.lua b/xmake.lua index 7134b8fe..7006f37b 100644 --- a/xmake.lua +++ b/xmake.lua @@ -70,26 +70,25 @@ option("backend") target("LegacyScriptEngine") add_rules("@levibuildscript/linkrule") - add_cxflags( - "/EHa", - "/utf-8", - "/W4", - "/w44265", - "/w44289", - "/w44296", - "/w45263", - "/w44738", - "/w45204", - "/Zm2000", - "/wd4100", - {force = true} - ) - add_defines( - "NOMINMAX", - "UNICODE", - "_AMD64_", - "_HAS_CXX23=1" - ) + if is_plat("windows") then + add_defines("NOMINMAX", "UNICODE", "_AMC64_", "_HAS_CXX23=1") + set_exceptions("none") -- To avoid conflicts with /EHa. + add_cxflags( "/EHa", "/utf-8", "/W4", "/w44265", "/w44289", "/w44296", "/w45263", "/w44738", "/w45204") + add_cxflags( + "/EHs", + "-Wno-microsoft-cast", + "-Wno-invalid-offsetof", + "-Wno-c++2b-extensions", + "-Wno-microsoft-include", + "-Wno-overloaded-virtual", + "-Wno-ignored-qualifiers", + "-Wno-missing-field-initializers", + "-Wno-potentially-evaluated-expression", + "-Wno-pragma-system-header-outside-header", + {tools = {"clang_cl"}} + ) + set_toolchains("clang-cl") + end add_packages( "cpp-httplib", "legacymoney", From b68f96980df7621ed5bd6259931d82bfefdb571e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:43:23 +0800 Subject: [PATCH 15/17] chore: fix --- CHANGELOG.md | 24 +------- output.patch | 155 --------------------------------------------------- 2 files changed, 1 insertion(+), 178 deletions(-) delete mode 100644 output.patch diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bd319f5..f487eab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,29 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Improved `BinaryStream`: - `getData([release])` now returns `ByteBuffer`, does not clear by default, and - optionally releases data after reading; `createPacket(pktid[,raw])` now - supports raw packets; scalar `read*` APIs were added; several numeric - `write*` APIs now accept `String` for BigInt-friendly input - @zimuya4153 -- Improved file and item behavior: - `File.readFrom(path[,isBinary])` now supports binary reads; - `File.writeTo(path, text)` now accepts `ByteBuffer`; - `it.setLore([])` now clears custom lore instead of failing @zimuya4153 -- Improved player NBT APIs: - `mc.getPlayerNbt(uuid)` now returns `Null` when no saved data exists and can - read online players directly; `mc.setPlayerNbt(uuid, nbt[, forceCreate, - isOnlineMode])` now supports optional offline record creation; player NBT - APIs now validate UUID input more strictly @zimuya4153 -- Improved NBT serialization: - `NbtCompound::toSNBT([space[,format]])` now supports explicitly selecting - `SnbtFormat`; one-argument usage still defaults to `SnbtFormat::ForceQuote` - @zimuya4153 - -### Fixed - -- Fixed `NbtCompound::setTag` @zimuya4153 +- Adapted to LeviLamina 26.20.* ## [0.18.2] - 2026-04-17 diff --git a/output.patch b/output.patch deleted file mode 100644 index 8332e7c8..00000000 --- a/output.patch +++ /dev/null @@ -1,155 +0,0 @@ -diff --git a/src/baselib/BaseLib.js b/src/baselib/BaseLib.js -index 4387716..cd122ef 100644 ---- a/src/baselib/BaseLib.js -+++ b/src/baselib/BaseLib.js -@@ -414,5 +414,7 @@ globalThis.LXL_CustomForm = LLSE_CustomForm; - globalThis.LXL_Item = LLSE_Item; - globalThis.LXL_Player = LLSE_Player; - globalThis.LXL_Objective = LLSE_Objective; -+globalThis.LXL_Packet = LLSE_Packet; -+globalThis.Packet = LLSE_Packet; - ll.export = ll.exports; - ll.import = ll.imports; -diff --git a/src/baselib/BaseLib.lua b/src/baselib/BaseLib.lua -index b70720f..5f1ceac 100644 ---- a/src/baselib/BaseLib.lua -+++ b/src/baselib/BaseLib.lua -@@ -20,5 +20,7 @@ LXL_CustomForm = LLSE_CustomForm - LXL_Item = LLSE_Item - LXL_Player = LLSE_Player - LXL_Objective = LLSE_Objective -+LXL_Packet = LLSE_Packet -+Packet = LLSE_Packet - ll.export = ll.exports - ll.import = ll.imports -diff --git a/src/legacy/api/BlockEntityAPI.cpp b/src/legacy/api/BlockEntityAPI.cpp -index c61dbcf..961c208 100644 ---- a/src/legacy/api/BlockEntityAPI.cpp -+++ b/src/legacy/api/BlockEntityAPI.cpp -@@ -24,6 +24,8 @@ ClassDefine BlockEntityClassBuilder = defineClass BlockEntityClass::getBlock(Arguments const&) const { - } - CATCH_AND_THROW - } -+ -+Local BlockEntityClass::setCustomName(Arguments const& args) const { -+ CHECK_ARGS_COUNT(args, 1); -+ CHECK_ARG_TYPE(args[0], ValueKind::kString); -+ -+ try { -+ blockEntity->setCustomName({args[0].asString().toString(), std::nullopt}); -+ return Boolean::newBoolean(true); -+ } -+ CATCH_AND_THROW -+} -+ -+Local BlockEntityClass::getCustomName() const { -+ try { -+ return String::newString(blockEntity->getCustomName().mUnredactedString); -+ } -+ CATCH_AND_THROW -+} -\ No newline at end of file -diff --git a/src/legacy/api/BlockEntityAPI.h b/src/legacy/api/BlockEntityAPI.h -index 4eee391..8d51af9 100644 ---- a/src/legacy/api/BlockEntityAPI.h -+++ b/src/legacy/api/BlockEntityAPI.h -@@ -20,9 +20,11 @@ class BlockEntityClass : public ScriptClass { - Local getName() const; - Local getPos() const; - Local getType() const; -+ Local getCustomName() const; - - Local getNbt(Arguments const& args) const; - Local setNbt(Arguments const& args) const; - Local getBlock(Arguments const& args) const; -+ Local setCustomName(Arguments const& args) const; - }; - extern ClassDefine BlockEntityClassBuilder; -diff --git a/src/legacy/api/EntityAPI.cpp b/src/legacy/api/EntityAPI.cpp -index cb34c4d..7267640 100644 ---- a/src/legacy/api/EntityAPI.cpp -+++ b/src/legacy/api/EntityAPI.cpp -@@ -140,6 +140,7 @@ ClassDefine EntityClassBuilder = - .instanceFunction("refreshItems", &EntityClass::refreshItems) - .instanceFunction("setScale", &EntityClass::setScale) - .instanceFunction("setCustomName", &EntityClass::setCustomName) -+ .instanceFunction("getCustomName", &EntityClass::getCustomName) - .instanceFunction("setNbt", &EntityClass::setNbt) - .instanceFunction("getNbt", &EntityClass::getNbt) - .instanceFunction("addTag", &EntityClass::addTag) -@@ -1309,6 +1310,16 @@ Local EntityClass::setCustomName(Arguments const& args) const { - CATCH_AND_THROW - } - -+Local EntityClass::getCustomName() const { -+ try { -+ Actor* entity = get(); -+ if (!entity) return {}; -+ -+ return String::newString(entity->getNameTag()); -+ } -+ CATCH_AND_THROW -+} -+ - Local EntityClass::getNbt(Arguments const&) const { - try { - Actor const* entity = get(); -diff --git a/src/legacy/api/EntityAPI.h b/src/legacy/api/EntityAPI.h -index fdaf93f..5bfee5e 100644 ---- a/src/legacy/api/EntityAPI.h -+++ b/src/legacy/api/EntityAPI.h -@@ -31,6 +31,7 @@ class EntityClass : public ScriptClass { - Local getCanFreeze() const; - Local getCanSeeDaylight() const; - Local getCanPickupItems() const; -+ Local getCustomName() const; - Local getInAir() const; - Local getInWater() const; - Local getInClouds() const; -diff --git a/src/legacy/api/ItemAPI.cpp b/src/legacy/api/ItemAPI.cpp -index 05abdeb..f8e9981 100644 ---- a/src/legacy/api/ItemAPI.cpp -+++ b/src/legacy/api/ItemAPI.cpp -@@ -61,6 +61,7 @@ ClassDefine ItemClassBuilder = defineClass("LLSE_Item") - .instanceFunction("setAux", &ItemClass::setAux) - .instanceFunction("setLore", &ItemClass::setLore) - .instanceFunction("setDisplayName", &ItemClass::setDisplayName) -+ .instanceFunction("getDisplayName", &ItemClass::getDisplayName) - .instanceFunction("setDamage", &ItemClass::setDamage) - .instanceFunction("setNbt", &ItemClass::setNbt) - .instanceFunction("getNbt", &ItemClass::getNbt) -@@ -419,6 +420,13 @@ Local ItemClass::setDisplayName(Arguments const& args) const { - CATCH_AND_THROW - } - -+Local ItemClass::getDisplayName() const { -+ try { -+ return String::newString(get()->getCustomName()); -+ } -+ CATCH_AND_THROW -+} -+ - Local ItemClass::setDamage(Arguments const& args) const { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); -diff --git a/src/legacy/api/ItemAPI.h b/src/legacy/api/ItemAPI.h -index 0fd7af9..dbbfcbe 100644 ---- a/src/legacy/api/ItemAPI.h -+++ b/src/legacy/api/ItemAPI.h -@@ -43,6 +43,7 @@ class ItemClass : public ScriptClass { - Local getMaxDamage() const; - Local getMaxStackSize() const; - Local getLore() const; -+ Local getDisplayName() const; - - Local isArmorItem() const; - Local isBlock() const; From 13dd954be8ab69df0acb43137de5853be13fe10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:49:32 +0800 Subject: [PATCH 16/17] fix: fix --- CHANGELOG.md | 6 +++++- docs/apis/GameAPI/BlockEntity.md | 4 ++-- docs/apis/GameAPI/BlockEntity.zh.md | 4 ++-- docs/apis/GameAPI/Entity.md | 8 ++++---- docs/apis/GameAPI/Entity.zh.md | 8 ++++---- docs/apis/GameAPI/Item.md | 10 +++++----- docs/apis/GameAPI/Item.zh.md | 10 +++++----- docs/apis/GameAPI/Packet.md | 28 +++++++++++++-------------- docs/apis/GameAPI/Packet.zh.md | 28 +++++++++++++-------------- docs/apis/GameAPI/Player.md | 4 ++-- docs/apis/GameAPI/Player.zh.md | 4 ++-- docs/apis/GameAPI/Server.md | 10 +++++----- docs/apis/GameAPI/Server.zh.md | 10 +++++----- docs/apis/ScriptAPI/VanillaI18n.md | 2 +- docs/apis/ScriptAPI/VanillaI18n.zh.md | 2 +- docs/apis/SystemAPI/File.md | 6 +++--- docs/apis/SystemAPI/File.zh.md | 6 +++--- docs/apis/SystemAPI/SystemInfo.md | 2 +- docs/apis/SystemAPI/SystemInfo.zh.md | 2 +- tooth.json | 2 +- xmake.lua | 9 ++++----- 21 files changed, 84 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f487eab3..ff0b1258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.19.1] - 2026-07-05 + ### Added - Added packet APIs: @@ -1316,7 +1318,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#353]: https://github.com/LiteLDev/LegacyScriptEngine/issues/353 [#358]: https://github.com/LiteLDev/LegacyScriptEngine/issues/358 -[Unreleased]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.18.2...HEAD +[Unreleased]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.19.1...HEAD +[0.19.1]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.19.0...v0.19.1 +[0.19.0]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.18.2...v0.19.0 [0.18.2]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.18.1...v0.18.2 [0.18.1]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.18.0...v0.18.1 [0.18.0]: https://github.com/LiteLDev/LegacyScriptEngine/compare/v0.17.13...v0.18.0 diff --git a/docs/apis/GameAPI/BlockEntity.md b/docs/apis/GameAPI/BlockEntity.md index d5a030ff..8f00cd14 100644 --- a/docs/apis/GameAPI/BlockEntity.md +++ b/docs/apis/GameAPI/BlockEntity.md @@ -73,7 +73,7 @@ For more usage of NBT objects, please refer to [NBT Interface Documentation](../ #### Set Block Entity Custom Name !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `be.setCustomName(name)` @@ -86,7 +86,7 @@ This function is only available in 0.19.0 and later. #### Get Block Entity Custom Name !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `be.getCustomName()` diff --git a/docs/apis/GameAPI/BlockEntity.zh.md b/docs/apis/GameAPI/BlockEntity.zh.md index 61917609..8bd8f453 100644 --- a/docs/apis/GameAPI/BlockEntity.zh.md +++ b/docs/apis/GameAPI/BlockEntity.zh.md @@ -73,7 +73,7 @@ #### 设置方块实体自定义名称 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `be.setCustomName(name)` @@ -86,7 +86,7 @@ #### 获取方块实体自定义名称 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `be.getCustomName()` diff --git a/docs/apis/GameAPI/Entity.md b/docs/apis/GameAPI/Entity.md index de3f8267..37ad52de 100644 --- a/docs/apis/GameAPI/Entity.md +++ b/docs/apis/GameAPI/Entity.md @@ -76,7 +76,7 @@ Through this function, generate a new creature at the specified location and get #### Summon New Creature and Get Its Entity Object !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. Through this function, summon a new creature at the specified location and get its corresponding entity object. @@ -102,7 +102,7 @@ Through this function, summon a new creature at the specified location and get i #### Load Creature From NBT and Get Its Entity Object !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. Through this function, spawn a creature with the specified NBT data at the specified location and get its corresponding entity object. @@ -442,7 +442,7 @@ Note that the damage dealt here is real damage and cannot be reduced by protecti #### Set Entity Custom Name !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `en.setCustomName(name)` @@ -455,7 +455,7 @@ This function is only available in 0.19.0 and later. #### Get Entity Custom Name !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `en.getCustomName()` diff --git a/docs/apis/GameAPI/Entity.zh.md b/docs/apis/GameAPI/Entity.zh.md index e93fdb0a..6c8facd6 100644 --- a/docs/apis/GameAPI/Entity.zh.md +++ b/docs/apis/GameAPI/Entity.zh.md @@ -72,7 +72,7 @@ #### 召唤新生物并获取 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 通过此函数,在指定的位置召唤一个新生物,并获取它对应的实体对象 @@ -96,7 +96,7 @@ #### 通过NBT加载生物并获取 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 通过此函数,使用指定的NBT数据在指定位置生成一个生物,并获取它对应的实体对象 @@ -429,7 +429,7 @@ #### 设置实体自定义名称 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `en.setCustomName(name)` @@ -442,7 +442,7 @@ #### 获取实体自定义名称 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `en.getCustomName()` diff --git a/docs/apis/GameAPI/Item.md b/docs/apis/GameAPI/Item.md index acb4d84b..4ea49807 100644 --- a/docs/apis/GameAPI/Item.md +++ b/docs/apis/GameAPI/Item.md @@ -206,7 +206,7 @@ For more usage of NBT objects, please refer to [NBT Interface Documentation](../ - Return value type: `Boolean` !!! warning -In 0.19.0 and later, passing an empty array will clear the item's lore. +In 0.19.1 and later, passing an empty array will clear the item's lore. @@ -223,7 +223,7 @@ In 0.19.0 and later, passing an empty array will clear the item's lore. #### Get Custom Item Name !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `it.getDisplayName()` @@ -245,7 +245,7 @@ This function is only available in 0.19.0 and later. #### Increase Item Count !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `it.addCount(count)` @@ -258,7 +258,7 @@ This function is only available in 0.19.0 and later. #### Decrease Item Count !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `it.removeCount(count)` @@ -271,7 +271,7 @@ This function is only available in 0.19.0 and later. #### Set Item Count !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `it.setCount(count)` diff --git a/docs/apis/GameAPI/Item.zh.md b/docs/apis/GameAPI/Item.zh.md index 28217dbd..10b676c1 100644 --- a/docs/apis/GameAPI/Item.zh.md +++ b/docs/apis/GameAPI/Item.zh.md @@ -207,7 +207,7 @@ - 返回值类型: `Boolean` !!! warning - 在0.19.0及以后版本中,传入空数组会清空物品Lore。 + 在0.19.1及以后版本中,传入空数组会清空物品Lore。 @@ -224,7 +224,7 @@ #### 获取自定义物品名称 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `it.getDisplayName()` @@ -246,7 +246,7 @@ #### 增加物品数量 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `it.addCount(count)` @@ -259,7 +259,7 @@ #### 减少物品数量 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `it.removeCount(count)` @@ -272,7 +272,7 @@ #### 设置物品数量 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `it.setCount(count)` diff --git a/docs/apis/GameAPI/Packet.md b/docs/apis/GameAPI/Packet.md index ee71dea9..46e04dc5 100644 --- a/docs/apis/GameAPI/Packet.md +++ b/docs/apis/GameAPI/Packet.md @@ -25,7 +25,7 @@ See [Binary Stream Objects](#-binary-stream-object-api) for details #### Create from packet ID !!! warning -The global aliases of this class are only available in 0.19.0 and later. +The global aliases of this class are only available in 0.19.1 and later. `Packet.createPacket(pktid[,raw])` @@ -78,7 +78,7 @@ Every packet object contains some member functions (member methods) that can be #### Send packet to specified target !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `pkt.sendTo(pos)` `pkt.sendTo(x,y,z,dimid)` @@ -100,7 +100,7 @@ If `target` is `Entity`, the packet will be sent to players around the specified #### Send packet to all clients !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `pkt.sendToClients()` @@ -110,7 +110,7 @@ This function is only available in 0.19.0 and later. #### Send packet to server !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `pkt.sendToServer()` @@ -142,7 +142,7 @@ Every binary stream object contains some member functions (member methods) that #### Get binary stream read pointer !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `bs.getReadPointer()` @@ -152,7 +152,7 @@ This function is only available in 0.19.0 and later. #### Set binary stream read pointer !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `bs.setReadPointer(pos)` @@ -167,7 +167,7 @@ This function is only available in 0.19.0 and later. #### Get binary stream data !!! warning -The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.0 and later. +The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.1 and later. `bs.getData([release])` @@ -179,7 +179,7 @@ The optional parameter and `ByteBuffer` return value of this function are only a - Return value: binary stream data - Return value type: `ByteBuffer` -Before 0.19.0, this function can only be used as `bs.getData()`. +Before 0.19.1, this function can only be used as `bs.getData()`. At that time, it always cleared the stream data after reading, and the return value type was `String`. Because the old return value was `String`, in JavaScript it may be forcibly encoded as UTF-8, causing binary data corruption and incorrect data content. @@ -187,7 +187,7 @@ Because the old return value was `String`, in JavaScript it may be forcibly enco #### Set binary stream data !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `bs.setData(data)` @@ -216,7 +216,7 @@ This function is only available in 0.19.0 and later. | ------------------------------ | -------------------- | | writeBool | `Boolean`/ `String` | | writeByte | `Integer` / `String` | -| writeBytes (Added in 0.19.0) | `ByteBuffer` | +| writeBytes (Added in 0.19.1) | `ByteBuffer` | | writeDouble | `Number` / `String` | | writeFloat | `Float` / `String` | | writeSignedBigEndianInt | `Number` / `String` | @@ -236,12 +236,12 @@ This function is only available in 0.19.0 and later. | writeBlockPos (Added in 0.9.5) | `BlockPos` | | writeCompoundTag | `NbtCompound` | | writeItem (Added in 0.9.5) | `Item` | -| writeUuid (Added in 0.19.0) | `String` | +| writeUuid (Added in 0.19.1) | `String` | #### Read from binary stream !!! warning -This function group is only available in 0.19.0 and later. +This function group is only available in 0.19.1 and later. `bs.readxxxx([asString])` `bs.readBytes(length)` @@ -282,7 +282,7 @@ When `asString` is `true`, scalar `read*` functions return `String`. #### Building packet from binary stream !!! warning -The optional parameter of this function is only available in 0.19.0 and later. +The optional parameter of this function is only available in 0.19.1 and later. `bs.createPacket(pktid[,raw])` @@ -296,7 +296,7 @@ The optional parameter of this function is only available in 0.19.0 and later. - Return value:Packet object - Return value type: `Packet` -Before 0.19.0, this function can only be used as `bs.createPacket(pktid)`. +Before 0.19.1, this function can only be used as `bs.createPacket(pktid)`. ### Dome Code diff --git a/docs/apis/GameAPI/Packet.zh.md b/docs/apis/GameAPI/Packet.zh.md index 6631e111..b521d9e0 100644 --- a/docs/apis/GameAPI/Packet.zh.md +++ b/docs/apis/GameAPI/Packet.zh.md @@ -25,7 +25,7 @@ #### 通过数据包 ID 创建 !!! warning -此类的全局别名仅在 0.19.0 及以后版本可用 +此类的全局别名仅在 0.19.1 及以后版本可用 `Packet.createPacket(pktid[,raw])` @@ -78,7 +78,7 @@ #### 发送数据包到指定目标 !!! warning -此函数仅在 0.19.0 及以后版本可用 +此函数仅在 0.19.1 及以后版本可用 `pkt.sendTo(pos)` `pkt.sendTo(x,y,z,dimid)` @@ -100,7 +100,7 @@ #### 发送数据包到所有客户端 !!! warning -此函数仅在 0.19.0 及以后版本可用 +此函数仅在 0.19.1 及以后版本可用 `pkt.sendToClients()` @@ -110,7 +110,7 @@ #### 发送数据包到服务端 !!! warning -此函数仅在 0.19.0 及以后版本可用 +此函数仅在 0.19.1 及以后版本可用 `pkt.sendToServer()` @@ -142,7 +142,7 @@ #### 获取二进制流读指针 !!! warning -此函数仅在 0.19.0 及以后版本可用 +此函数仅在 0.19.1 及以后版本可用 `bs.getReadPointer()` @@ -152,7 +152,7 @@ #### 设置二进制流读指针 !!! warning -此函数仅在 0.19.0 及以后版本可用 +此函数仅在 0.19.1 及以后版本可用 `bs.setReadPointer(pos)` @@ -167,7 +167,7 @@ #### 获取二进制流数据 !!! warning -此函数的可选参数和 `ByteBuffer` 返回值仅在 0.19.0 及以后版本可用 +此函数的可选参数和 `ByteBuffer` 返回值仅在 0.19.1 及以后版本可用 `bs.getData([release])` @@ -179,7 +179,7 @@ - 返回值:二进制流数据 - 返回值类型: `ByteBuffer` -在 0.19.0 之前,此函数只能使用 `bs.getData()` 形式调用。 +在 0.19.1 之前,此函数只能使用 `bs.getData()` 形式调用。 当时它在获取后总是会清空流数据,且返回值类型为 `String`。 由于旧版返回的是 `String`,在 JavaScript 中可能会被强制按 UTF-8 编码处理,导致二进制数据损坏,拿到的数据不正确。 @@ -187,7 +187,7 @@ #### 设置二进制流数据 !!! warning -此函数仅在 0.19.0 及以后版本可用 +此函数仅在 0.19.1 及以后版本可用 `bs.setData(data)` @@ -216,7 +216,7 @@ | ---------------------------- | -------------------- | | writeBool | `Boolean` | | writeByte | `Integer` / `String` | -| writeBytes (0.19.0 时加入) | `ByteBuffer` | +| writeBytes (0.19.1 时加入) | `ByteBuffer` | | writeDouble | `Number` / `String` | | writeFloat | `Float` / `String` | | writeSignedBigEndianInt | `Number` / `String` | @@ -236,12 +236,12 @@ | writeBlockPos (0.9.5 时加入) | `BlockPos` | | writeCompoundTag | `NbtCompound` | | writeItem (0.9.5 时加入) | `Item` | -| writeUuid (0.19.0 时加入) | `String` | +| writeUuid (0.19.1 时加入) | `String` | #### 从二进制流读取 !!! warning -此类函数仅在 0.19.0 及以后版本可用。 +此类函数仅在 0.19.1 及以后版本可用。 `bs.readxxxx([asString])` `bs.readBytes(length)` @@ -282,7 +282,7 @@ #### 通过二进制流构建数据包 !!! warning -此函数的可选参数仅在 0.19.0 及以后版本可用 +此函数的可选参数仅在 0.19.1 及以后版本可用 `bs.createPacket(pktid[,raw])` @@ -296,7 +296,7 @@ - 返回值:数据包对象 - 返回值类型: `Packet` -在 0.19.0 之前,此函数只能使用 `bs.createPacket(pktid)` 形式调用。 +在 0.19.1 之前,此函数只能使用 `bs.createPacket(pktid)` 形式调用。 ### 演示代码 diff --git a/docs/apis/GameAPI/Player.md b/docs/apis/GameAPI/Player.md index 89575629..9073e6d3 100644 --- a/docs/apis/GameAPI/Player.md +++ b/docs/apis/GameAPI/Player.md @@ -103,8 +103,8 @@ properties. | pl.isSleeping | Player is sleeping | `Boolean` | | pl.isMoving | Player is moving | `Boolean` | | pl.isSneaking | Player is sneaking | `Boolean` | -| pl.isSwimming | Whether the player is swimming (Added in 0.19.0) | `Boolean` | -| pl.isCrawling | Whether the player is crawling (Added in 0.19.0) | `Boolean` | +| pl.isSwimming | Whether the player is swimming (Added in 0.19.1) | `Boolean` | +| pl.isCrawling | Whether the player is crawling (Added in 0.19.1) | `Boolean` | These object properties are read-only and cannot be modified. in: diff --git a/docs/apis/GameAPI/Player.zh.md b/docs/apis/GameAPI/Player.zh.md index 3459feba..c9ff542b 100644 --- a/docs/apis/GameAPI/Player.zh.md +++ b/docs/apis/GameAPI/Player.zh.md @@ -101,8 +101,8 @@ | pl.isSleeping | 玩家是否正在睡觉 | `Boolean` | | pl.isMoving | 玩家是否正在移动 | `Boolean` | | pl.isSneaking | 玩家是否正在潜行 | `Boolean` | -| pl.isSwimming | 玩家是否正在游泳(在 0.19.0 时被加入) | `Boolean` | -| pl.isCrawling | 玩家是否正在爬行(在 0.19.0 时被加入) | `Boolean` | +| pl.isSwimming | 玩家是否正在游泳(在 0.19.1 时被加入) | `Boolean` | +| pl.isCrawling | 玩家是否正在爬行(在 0.19.1 时被加入) | `Boolean` | 这些对象属性都是只读的,无法被修改。其中: diff --git a/docs/apis/GameAPI/Server.md b/docs/apis/GameAPI/Server.md index 92395cc4..463906fd 100644 --- a/docs/apis/GameAPI/Server.md +++ b/docs/apis/GameAPI/Server.md @@ -23,7 +23,7 @@ The following APIs provide interfaces for customizing some server settings: ### Get Server Motd String !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `mc.getMotd()` @@ -59,7 +59,7 @@ This function is only available in 0.19.0 and later. ### Get Online Player Count !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `mc.getOnlinePlayerNum([ignoreSimulatedPlayer])` @@ -74,7 +74,7 @@ This function is only available in 0.19.0 and later. ### Get Server Maximum Player Count !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `mc.getMaxNumPlayers()` @@ -133,7 +133,7 @@ Among them, daytime is the number of game ticks since dawn, gametime is the age ### Get Dimension ID !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `mc.getDimensionId(name)` @@ -148,7 +148,7 @@ This function is only available in 0.19.0 and later. ### Get Dimension Name !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `mc.getDimensionName(dimid)` diff --git a/docs/apis/GameAPI/Server.zh.md b/docs/apis/GameAPI/Server.zh.md index 52908743..430d66f6 100644 --- a/docs/apis/GameAPI/Server.zh.md +++ b/docs/apis/GameAPI/Server.zh.md @@ -23,7 +23,7 @@ ### 获取服务器MOTD字符串 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `mc.getMotd()` @@ -59,7 +59,7 @@ ### 获取在线玩家数量 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `mc.getOnlinePlayerNum([ignoreSimulatedPlayer])` @@ -74,7 +74,7 @@ ### 获取服务器最大玩家数 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `mc.getMaxNumPlayers()` @@ -133,7 +133,7 @@ ### 获取维度ID !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `mc.getDimensionId(name)` @@ -148,7 +148,7 @@ ### 获取维度名称 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `mc.getDimensionName(dimid)` diff --git a/docs/apis/ScriptAPI/VanillaI18n.md b/docs/apis/ScriptAPI/VanillaI18n.md index edb3d255..f81c33e5 100644 --- a/docs/apis/ScriptAPI/VanillaI18n.md +++ b/docs/apis/ScriptAPI/VanillaI18n.md @@ -3,7 +3,7 @@ This API is used to modify Minecraft vanilla language data. !!! warning -This API is only available in 0.19.0 and later. +This API is only available in 0.19.1 and later. The actual registered class name in the script engine is `VaillanI18n`. diff --git a/docs/apis/ScriptAPI/VanillaI18n.zh.md b/docs/apis/ScriptAPI/VanillaI18n.zh.md index 1c0e0daf..8c8a963a 100644 --- a/docs/apis/ScriptAPI/VanillaI18n.zh.md +++ b/docs/apis/ScriptAPI/VanillaI18n.zh.md @@ -3,7 +3,7 @@ 此 API 用于修改 Minecraft 原版语言数据。 !!! warning - 此 API 仅在0.19.0及以后版本可用 + 此 API 仅在0.19.1及以后版本可用 此对象在脚本引擎内实际注册的类名为 `VaillanI18n`。 diff --git a/docs/apis/SystemAPI/File.md b/docs/apis/SystemAPI/File.md index 44b97513..eb624302 100644 --- a/docs/apis/SystemAPI/File.md +++ b/docs/apis/SystemAPI/File.md @@ -15,7 +15,7 @@ If you need to manipulate files frequently, use the file classes below to improv ### Read in All the Contents of the File !!! warning -The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.0 and later. +The optional parameter and `ByteBuffer` return value of this function are only available in 0.19.1 and later. `File.readFrom(path[,isBinary])` @@ -30,7 +30,7 @@ The optional parameter and `ByteBuffer` return value of this function are only a If `isBinary` is `true`, this function returns `ByteBuffer`; otherwise it returns `String`. -Before 0.19.0, this function could only be used as `File.readFrom(path)`, and the return value type was always `String`. +Before 0.19.1, this function could only be used as `File.readFrom(path)`, and the return value type was always `String`. @@ -52,7 +52,7 @@ Before 0.19.0, this function could only be used as `File.readFrom(path)`, and th > Note: If the file does not exist, it will be created automatically. If it exists, it will be **emptied** before writing. !!! warning -Passing `ByteBuffer` is only available in 0.19.0 and later. +Passing `ByteBuffer` is only available in 0.19.1 and later. diff --git a/docs/apis/SystemAPI/File.zh.md b/docs/apis/SystemAPI/File.zh.md index 97857170..f7c59701 100644 --- a/docs/apis/SystemAPI/File.zh.md +++ b/docs/apis/SystemAPI/File.zh.md @@ -15,7 +15,7 @@ ### 读入文件的所有内容 !!! warning - 此函数的可选参数和 `ByteBuffer` 返回值仅在0.19.0及以后版本可用 + 此函数的可选参数和 `ByteBuffer` 返回值仅在0.19.1及以后版本可用 `File.readFrom(path[,isBinary])` @@ -30,7 +30,7 @@ 如果 `isBinary` 为 `true`,则返回 `ByteBuffer`,否则返回 `String`。 -在0.19.0之前,此函数只能使用 `File.readFrom(path)` 形式调用,返回值类型固定为 `String`。 +在0.19.1之前,此函数只能使用 `File.readFrom(path)` 形式调用,返回值类型固定为 `String`。 @@ -52,7 +52,7 @@ > 注:若文件不存在会自动创建,若存在则会先将其**清空**再写入 !!! warning - 传入 `ByteBuffer` 仅在0.19.0及以后版本可用 + 传入 `ByteBuffer` 仅在0.19.1及以后版本可用 diff --git a/docs/apis/SystemAPI/SystemInfo.md b/docs/apis/SystemAPI/SystemInfo.md index 81c520b0..58f4d237 100644 --- a/docs/apis/SystemAPI/SystemInfo.md +++ b/docs/apis/SystemAPI/SystemInfo.md @@ -46,7 +46,7 @@ The following APIs provide interfaces to obtain necessary system information: ### Randomly Generate a UUID String !!! warning -This function is only available in 0.19.0 and later. +This function is only available in 0.19.1 and later. `system.randomUuid()` diff --git a/docs/apis/SystemAPI/SystemInfo.zh.md b/docs/apis/SystemAPI/SystemInfo.zh.md index 8692c5eb..55e5c99c 100644 --- a/docs/apis/SystemAPI/SystemInfo.zh.md +++ b/docs/apis/SystemAPI/SystemInfo.zh.md @@ -46,7 +46,7 @@ ### 随机生成一个 UUID 字符串 !!! warning - 此函数仅在0.19.0及以后版本可用 + 此函数仅在0.19.1及以后版本可用 `system.randomUuid()` diff --git a/tooth.json b/tooth.json index fd8acf6b..ca795796 100644 --- a/tooth.json +++ b/tooth.json @@ -2,7 +2,7 @@ "format_version": 3, "format_uuid": "289f771f-2c9a-4d73-9f3f-8492495a924d", "tooth": "github.com/LiteLDev/LegacyScriptEngine", - "version": "0.19.0", + "version": "0.19.1", "info": { "name": "LegacyScriptEngine", "description": "A plugin engine for running LLSE plugins on LeviLamina", diff --git a/xmake.lua b/xmake.lua index 7006f37b..8455ab6b 100644 --- a/xmake.lua +++ b/xmake.lua @@ -3,15 +3,15 @@ add_rules("mode.debug", "mode.release") add_repositories("levimc-repo " .. (get_config("levimc_repo") or "https://github.com/LiteLDev/xmake-repo.git")) if is_config("target_type", "server") then - add_requires("levilamina 26.10.*", {configs = {target_type = "server"}}) + add_requires("levilamina 26.20.0", {configs = {target_type = "server"}}) else - add_requires("levilamina 26.10.*", {configs = {target_type = "client"}}) + add_requires("levilamina 26.20.0", {configs = {target_type = "client"}}) end add_requires("levibuildscript") -add_requires("legacyremotecall 0.18.*", {configs = {target_type = get_config(target_type)}}) -add_requires("legacymoney 0.18.*", {configs = {target_type = get_config(target_type)}}) +add_requires("legacyremotecall 0.19.0", {configs = {target_type = get_config(target_type)}}) +add_requires("legacymoney 0.19.0", {configs = {target_type = get_config(target_type)}}) add_requires( "lightwebsocketclient 1.0.1", @@ -104,7 +104,6 @@ target("LegacyScriptEngine") "mariadb-connector-c", "ctre" ) - set_exceptions("none") set_kind("shared") set_languages("cxx20") set_symbols("debug") From 2c1792673a5d02ea43a8109ba2013a2943d2e94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=B2=90=E5=91=80?= <163636894+zimuya4153@users.noreply.github.com> Date: Sun, 5 Jul 2026 19:08:30 +0800 Subject: [PATCH 17/17] fix: try to fix the compilation. --- src/legacy/api/EntityAPI.cpp | 4 ++-- xmake.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/legacy/api/EntityAPI.cpp b/src/legacy/api/EntityAPI.cpp index bdf288ef..9de04066 100644 --- a/src/legacy/api/EntityAPI.cpp +++ b/src/legacy/api/EntityAPI.cpp @@ -1840,8 +1840,8 @@ Local McClass::summonMob(Arguments const& args) { if (!entity || entity->mRemoved) return {}; - if (auto type = std::to_underlying(entity->getEntityTypeId()); - (type & std::to_underlying(ActorType::Mob)) != 0 || type - 10 <= 0x35) { + if (auto type = static_cast(entity->getEntityTypeId()); + (type & static_cast(ActorType::Mob)) != 0 || type - 10 <= 0x35) { entity->setPersistent(); } diff --git a/xmake.lua b/xmake.lua index 8455ab6b..87596966 100644 --- a/xmake.lua +++ b/xmake.lua @@ -71,7 +71,7 @@ option("backend") target("LegacyScriptEngine") add_rules("@levibuildscript/linkrule") if is_plat("windows") then - add_defines("NOMINMAX", "UNICODE", "_AMC64_", "_HAS_CXX23=1") + add_defines("NOMINMAX", "UNICODE", "_AMC64_") set_exceptions("none") -- To avoid conflicts with /EHa. add_cxflags( "/EHa", "/utf-8", "/W4", "/w44265", "/w44289", "/w44296", "/w45263", "/w44738", "/w45204") add_cxflags( @@ -224,4 +224,4 @@ target("LegacyScriptEngine") os.mkdir(outputPath) os.cp(langPath, outputPath) end) - end + end \ No newline at end of file