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/CHANGELOG.md b/CHANGELOG.md index 865534c6..55c4fe9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- 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 + +- 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 ### Fixed 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 fb9ab5a0..91852718 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,181 @@ 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`/ `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 -`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..250d91ff 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,181 @@ - 返回值:是否成功 - 返回值类型: `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` / `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`。 #### 通过二进制流构建数据包 -`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/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/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/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 {}; } 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 edb03ce4..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) @@ -26,6 +28,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 +55,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..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); @@ -35,10 +37,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 +74,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/PacketAPI.cpp b/src/legacy/api/PacketAPI.cpp index 9380143f..5fa4bbd2 100644 --- a/src/legacy/api/PacketAPI.cpp +++ b/src/legacy/api/PacketAPI.cpp @@ -2,31 +2,44 @@ #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" #include "mc/network/Packet.h" #include "mc/world/item/NetworkItemStackDescriptor.h" +#include +#include + //////////////////// Class Definition //////////////////// ClassDefine PacketClassBuilder = defineClass("LLSE_Packet") .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("writeSignedBigEndianInt", &BinaryStreamClass::writeSignedBigEndianInt) @@ -34,7 +47,7 @@ ClassDefine BinaryStreamClassBuilder = .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 +59,25 @@ ClassDefine BinaryStreamClassBuilder = .instanceFunction("writeBlockPos", &BinaryStreamClass::writeBlockPos) .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(); @@ -89,217 +121,209 @@ 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) { - 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) { +Local PacketClass::sendToClients(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) { +//////////////////// BinaryStream Classes //////////////////// + +template +auto parseScalarArg(Arguments const& args, std::string_view func) { CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; + + 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(); + } + } } - stream->writeDouble(args[0].asNumber().toDouble(), nullptr, nullptr); - return Boolean::newBoolean(true); } - CATCH_AND_THROW + throw WrongArgTypeException(std::string{func}); } -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 {}; +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)); } - stream->writeFloat(args[0].asNumber().toFloat(), nullptr, nullptr); - return Boolean::newBoolean(true); + return Number::newNumber(static_cast(value)); + } else { + return Number::newNumber(static_cast(value)); } - CATCH_AND_THROW } -Local BinaryStreamClass::writeSignedBigEndianInt(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); - try { - auto stream = get(); - if (!stream) { - return {}; - } - stream->writeSignedBigEndianInt(args[0].asNumber().toInt32(), nullptr, nullptr); - return Boolean::newBoolean(true); - } - CATCH_AND_THROW +BinaryStreamClass::BinaryStreamClass(std::shared_ptr const& bs) +: ScriptClass(ConstructFromCpp{}) { + set(bs); } -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 {}; - } - stream->writeSignedInt(args[0].asNumber().toInt32(), 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::writeSignedInt64(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->writeSignedInt64(args[0].asNumber().toInt64(), nullptr, nullptr); - 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); - 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->writeSignedShort(args[0].asNumber().toInt32(), nullptr, nullptr); - return Boolean::newBoolean(true); + return result; } CATCH_AND_THROW } -Local BinaryStreamClass::writeString(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kString); +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->writeString(args[0].asString().toString(), 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::writeUnsignedChar(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->writeByte(args[0].asNumber().toInt32(), nullptr, nullptr); - return Boolean::newBoolean(true); + return new BinaryStreamClass(args.thiz()); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedInt(Arguments const& args) { - CHECK_ARGS_COUNT(args, 1); - CHECK_ARG_TYPE(args[0], ValueKind::kNumber); +Local BinaryStreamClass::getReadPointer(Arguments const& args) { try { auto stream = get(); if (!stream) { return {}; } - stream->writeUnsignedInt(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); - return Boolean::newBoolean(true); + return Number::newNumber(static_cast(stream->mReadPointer)); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedInt64(Arguments const& args) { +Local BinaryStreamClass::setReadPointer(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { @@ -307,27 +331,27 @@ Local BinaryStreamClass::writeUnsignedInt64(Arguments const& args) { if (!stream) { return {}; } - stream->writeUnsignedInt64(static_cast(args[0].asNumber().toInt64()), nullptr, nullptr); + stream->mReadPointer = args[0].asNumber().toInt64(); return Boolean::newBoolean(true); } CATCH_AND_THROW } -Local BinaryStreamClass::writeUnsignedShort(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->writeUnsignedShort(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::writeUnsignedVarInt(Arguments const& args) { +Local BinaryStreamClass::reserve(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kNumber); try { @@ -335,54 +359,100 @@ Local BinaryStreamClass::writeUnsignedVarInt(Arguments const& args) { if (!stream) { return {}; } - stream->writeUnsignedVarInt(static_cast(args[0].asNumber().toInt32()), nullptr, nullptr); + stream->mBuffer.reserve(args[0].asNumber().toInt32()); 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::readBytes(Arguments const& args) { CHECK_ARGS_COUNT(args, 1); 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->writeVarInt(args[0].asNumber().toInt32(), 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 {}; + std::string buffer(length, '\0'); + if (auto res = stream->read(buffer.data(), length); !res) { + throw Exception(fmt::format("{}\nfunction: {}", res.error().code().message(), __func__)); } - stream->writeVarInt64(args[0].asNumber().toInt64(), nullptr, nullptr); - return Boolean::newBoolean(true); + return ByteBuffer::newByteBuffer(buffer.data(), length); } CATCH_AND_THROW } +#define SCALAR_STREAM_MACRO(NAME, ...) \ + Local BinaryStreamClass::write##NAME(Arguments const& args) { \ + try { \ + auto stream = get(); \ + if (!stream) { \ + return {}; \ + } \ + 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 \ + } + +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); try { @@ -414,7 +484,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 +525,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..aa4e8fbd 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,12 +46,16 @@ 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 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 writeSignedBigEndianInt(Arguments const& args); @@ -55,7 +63,6 @@ class BinaryStreamClass : public ScriptClass { 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 +74,25 @@ 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 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); }; 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/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); +} 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/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; 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/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 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",