From c1592b079dbe109de189619d45daa846417ea2ce Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Fri, 22 Mar 2024 20:59:02 +1100 Subject: [PATCH] Add readJson and writeJson for some packets --- .../opensb/servercommands/servercommands.lua | 80 +++++++++ assets/opensb/universe_server.config.patch | 7 + source/game/StarChatTypes.cpp | 26 +++ source/game/StarChatTypes.hpp | 4 + source/game/StarNetPackets.cpp | 162 +++++++++++++++--- source/game/StarNetPackets.hpp | 37 +++- 6 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 assets/opensb/scripts/opensb/servercommands/servercommands.lua create mode 100644 assets/opensb/universe_server.config.patch diff --git a/assets/opensb/scripts/opensb/servercommands/servercommands.lua b/assets/opensb/scripts/opensb/servercommands/servercommands.lua new file mode 100644 index 0000000..ca30435 --- /dev/null +++ b/assets/opensb/scripts/opensb/servercommands/servercommands.lua @@ -0,0 +1,80 @@ +local commands = {} +local logHelp = "Available OpenStarbound server commands:\n" +local userHelp = logHelp .. "^cyan;" +local adminHelp = userHelp + +local function cmd(name, description, permission, func) + local first = next(commands) == nil + logHelp = logHelp .. (first and name or ", " .. name) + userHelp = userHelp .. (first and name or ", ^cyan;" .. name) + adminHelp = adminHelp .. (first and name or ", ^cyan;" .. name) + + local keyName = name:lower() + if permission == "tell" then + commands[keyName] = function(connectionId, ...) + return func(universe.isAdmin(connectionId), connectionId, ...) + end + elseif permission == "admin" then + commands[keyName] = function(connectionId, ...) + local error = CommandProcessor.adminCheck(connectionId, description:sub(1, 1):lower() .. description:sub(2)) + if error then + return error + else + return func(connectionId, ...) + end + end + elseif permission == "user" then + commands[keyName] = func + else + error(string.format("Command '%s' has invalid permission", name)) + end +end + +cmd("openhelp", "Get help", "tell", function(isAdmin, connectionId) + return isAdmin and adminHelp or userHelp +end) + +do + +local objects = nil +cmd("packetTest", "Do science", "admin", function(connectionId) + if not objects then + objects = {} + local paths = root.assetsByExtension("object") + for i, v in pairs(paths) do + local json = root.assetJson(v) + objects[i] = {json.objectName, json.shortdescription or json.objectName} + end + end + local item = objects[math.random(#objects)] + universe.sendPacket(connectionId, "GiveItem", { + item = {name = item[1], count = 1} + }) + + return "Can I interest you in a ^cyan;" .. item[2] .. "^reset;?" +end) + +end + +local _init = type(init) == "function" and init +function init(...) + sb.logInfo("%s", logHelp) + if _init then return _init(...) end +end + +local _command = type(command) == "function" and command +function command(commandName, connectionId, args) + local ret = _command and _command(commandName, connectionId, args) + if ret then return ret end + + local command = commands[commandName:lower()] + if command then + local success, ret = pcall(command, connectionId, table.unpack(args)) + if not success then + sb.logError("Error in OpenStarbound server command /%s: %s", commandName, ret) + return "command error: " .. ret + else + return ret + end + end +end \ No newline at end of file diff --git a/assets/opensb/universe_server.config.patch b/assets/opensb/universe_server.config.patch new file mode 100644 index 0000000..55d39cc --- /dev/null +++ b/assets/opensb/universe_server.config.patch @@ -0,0 +1,7 @@ +[ + { + "op" : "add", + "path" : "/commandProcessorScripts/-", + "value" : "/scripts/opensb/servercommands/servercommands.lua" + } +] \ No newline at end of file diff --git a/source/game/StarChatTypes.cpp b/source/game/StarChatTypes.cpp index ae4cd01..a717be3 100644 --- a/source/game/StarChatTypes.cpp +++ b/source/game/StarChatTypes.cpp @@ -46,6 +46,32 @@ ChatReceivedMessage::ChatReceivedMessage(MessageContext context, ConnectionId fr ChatReceivedMessage::ChatReceivedMessage(MessageContext context, ConnectionId fromConnection, String const& fromNick, String const& text, String const& portrait) : context(context), fromConnection(fromConnection), fromNick(fromNick), portrait(portrait), text(text) {} +ChatReceivedMessage::ChatReceivedMessage(Json const& json) : ChatReceivedMessage() { + auto jContext = json.get("context"); + context = MessageContext( + MessageContextModeNames.getLeft(jContext.getString("mode")), + jContext.getString("channelName", "") + ); + fromConnection = json.getUInt("fromConnection", 0); + fromNick = json.getString("fromNick", ""); + portrait = json.getString("portrait", ""); + text = json.getString("text"); +} + +Json ChatReceivedMessage::toJson() const { + return JsonObject{ + {"context", JsonObject{ + {"mode", MessageContextModeNames.getRight(context.mode)}, + {"channelName", context.channelName.empty() ? Json() : Json(context.channelName)} + }}, + {"fromConnection", fromConnection}, + {"fromNick", fromNick.empty() ? Json() : fromNick}, + {"portrait", portrait.empty() ? Json() : portrait}, + {"text", text} + }; +} + + DataStream& operator>>(DataStream& ds, ChatReceivedMessage& receivedMessage) { ds.read(receivedMessage.context); ds.read(receivedMessage.fromConnection); diff --git a/source/game/StarChatTypes.hpp b/source/game/StarChatTypes.hpp index 376ec6d..3feb413 100644 --- a/source/game/StarChatTypes.hpp +++ b/source/game/StarChatTypes.hpp @@ -2,6 +2,7 @@ #include "StarDataStream.hpp" #include "StarGameTypes.hpp" +#include "StarJson.hpp" namespace Star { @@ -43,6 +44,9 @@ struct ChatReceivedMessage { ChatReceivedMessage(); ChatReceivedMessage(MessageContext context, ConnectionId fromConnection, String const& fromNick, String const& text); ChatReceivedMessage(MessageContext context, ConnectionId fromConnection, String const& fromNick, String const& text, String const& portrait); + ChatReceivedMessage(Json const& json); + + Json toJson() const; MessageContext context; diff --git a/source/game/StarNetPackets.cpp b/source/game/StarNetPackets.cpp index 140845a..fd127a2 100644 --- a/source/game/StarNetPackets.cpp +++ b/source/game/StarNetPackets.cpp @@ -1,5 +1,6 @@ #include "StarNetPackets.hpp" #include "StarDataStreamExtra.hpp" +#include "StarJsonExtra.hpp" namespace Star { @@ -79,17 +80,13 @@ EnumMap const PacketTypeNames{ Packet::~Packet() {} -void Packet::readLegacy(DataStream& ds) { - read(ds); -} -void Packet::writeLegacy(DataStream& ds) const { - write(ds); -} +void Packet::readLegacy(DataStream& ds) { read(ds); } +void Packet::writeLegacy(DataStream& ds) const { write(ds); } +void Packet::readJson(Json const& json) {} +Json Packet::writeJson() const { return JsonObject{}; } -PacketCompressionMode Packet::compressionMode() const - { return m_compressionMode; } -void Packet::setCompressionMode(PacketCompressionMode compressionMode) - { m_compressionMode = compressionMode; } +PacketCompressionMode Packet::compressionMode() const { return m_compressionMode; } +void Packet::setCompressionMode(PacketCompressionMode compressionMode) { m_compressionMode = compressionMode; } PacketPtr createPacket(PacketType type) { switch (type) { @@ -168,22 +165,12 @@ PacketPtr createPacket(PacketType type) { } PacketPtr createPacket(PacketType type, Maybe const& args) { - if (!args) - return createPacket(type); + auto packet = createPacket(type); - switch (type) { - case PacketType::Pause: return make_shared(args->getBool("pause"), args->getFloat("timescale", 1.0f)); - case PacketType::ServerInfo: return make_shared(args->getUInt("players"), args->getUInt("maxPlayers")); - case PacketType::GiveItem: return make_shared(ItemDescriptor(args->getObject("ItemDescriptor"))); - case PacketType::UpdateTileProtection: return make_shared(args->getUInt("dungeonId"), args->getBool("protected")); - case PacketType::SetDungeonGravity: return make_shared(args->getUInt("dungeonId"), args->getFloat("gravity")); - case PacketType::SetDungeonBreathable: return make_shared(args->getUInt("dungeonId"), args->getBool("breathable")); - case PacketType::SetPlayerStart: return make_shared(Vec2F{args->getArray("position")[0].toFloat(), args->getArray("position")[1].toFloat()}, args->getBool("respawnInWorld")); - case PacketType::EntityMessage: return make_shared(EntityId(args->getInt("entityId")), args->getString("message"), args->get("JsonArray").toArray(), Uuid(args->getString("Uuid"))); - case PacketType::UpdateWorldProperties: return make_shared(args->getObject("updatedProperties")); - default: - throw StarPacketException(strf("Unrecognized packet type {}", (unsigned int)type)); - } + if (args && !args->isNull()) + packet->readJson(*args); + + return packet; } ProtocolRequestPacket::ProtocolRequestPacket() @@ -265,6 +252,16 @@ void ChatReceivePacket::write(DataStream& ds) const { ds.write(receivedMessage); } +void ChatReceivePacket::readJson(Json const& json) { + receivedMessage = ChatReceivedMessage(json.get("receivedMessage")); +} + +Json ChatReceivePacket::writeJson() const { + return JsonObject{ + {"receivedMessage", receivedMessage.toJson()} + }; +} + UniverseTimeUpdatePacket::UniverseTimeUpdatePacket() { universeTime = 0; } @@ -344,6 +341,18 @@ void PausePacket::write(DataStream& ds) const { ds.write(timescale); } +void PausePacket::readJson(Json const& json) { + pause = json.getBool("pause"); + timescale = json.getFloat("timescale", 1.0f); +} + +Json PausePacket::writeJson() const { + return JsonObject{ + {"pause", pause}, + {"timescale", timescale} + }; +} + ServerInfoPacket::ServerInfoPacket() {} ServerInfoPacket::ServerInfoPacket(uint16_t players, uint16_t maxPlayers) : @@ -362,6 +371,18 @@ void ServerInfoPacket::write(DataStream& ds) const ds.write(maxPlayers); } +void ServerInfoPacket::readJson(Json const& json) { + players = json.getUInt("players"); + maxPlayers = json.getUInt("maxPlayers"); +} + +Json ServerInfoPacket::writeJson() const { + return JsonObject{ + {"players", players}, + {"maxPlayers", maxPlayers} + }; +} + ClientConnectPacket::ClientConnectPacket() {} ClientConnectPacket::ClientConnectPacket(ByteArray assetsDigest, bool allowAssetsMismatch, Uuid playerUuid, @@ -676,6 +697,16 @@ void GiveItemPacket::write(DataStream& ds) const { ds.write(item); } +void GiveItemPacket::readJson(Json const& json) { + item = ItemDescriptor(json.get("item")); +} + +Json GiveItemPacket::writeJson() const { + return JsonObject{ + {"item", item.toJson()} + }; +} + EnvironmentUpdatePacket::EnvironmentUpdatePacket() {} EnvironmentUpdatePacket::EnvironmentUpdatePacket(ByteArray skyDelta, ByteArray weatherDelta) @@ -1019,6 +1050,28 @@ void EntityMessagePacket::write(DataStream& ds) const { ds.write(fromConnection); } +void EntityMessagePacket::readJson(Json const& json) { + auto jEntityId = json.get("entityId"); + if (jEntityId.canConvert(Json::Type::Int)) + entityId = (EntityId)jEntityId.toInt(); + else + entityId = jEntityId.toString(); + message = json.getString("message"); + args = json.getArray("args"); + uuid = Uuid(json.getString("uuid")); + fromConnection = json.getUInt("fromConnection"); +} + +Json EntityMessagePacket::writeJson() const { + return JsonObject{ + {"entityId", entityId.is() ? Json(entityId.get()) : Json(entityId.get())}, + {"message", message}, + {"args", args}, + {"uuid", uuid.hex()}, + {"fromConnection", fromConnection} + }; +} + EntityMessageResponsePacket::EntityMessageResponsePacket() {} EntityMessageResponsePacket::EntityMessageResponsePacket(Either response, Uuid uuid) @@ -1047,6 +1100,17 @@ void UpdateWorldPropertiesPacket::write(DataStream& ds) const { ds.writeMapContainer(updatedProperties); } + +void UpdateWorldPropertiesPacket::readJson(Json const& json) { + updatedProperties = json.getObject("updatedProperties"); +} + +Json UpdateWorldPropertiesPacket::writeJson() const { + return JsonObject{ + {"updatedProperties", updatedProperties}, + }; +} + UpdateTileProtectionPacket::UpdateTileProtectionPacket() {} UpdateTileProtectionPacket::UpdateTileProtectionPacket(DungeonId dungeonId, bool isProtected) @@ -1062,6 +1126,18 @@ void UpdateTileProtectionPacket::write(DataStream& ds) const { ds.write(isProtected); } +void UpdateTileProtectionPacket::readJson(Json const& json) { + dungeonId = json.getUInt("dungeonId"); + isProtected = json.getBool("isProtected"); +} + +Json UpdateTileProtectionPacket::writeJson() const { + return JsonObject{ + {"dungeonId", dungeonId}, + {"isProtected", isProtected} + }; +} + SetDungeonGravityPacket::SetDungeonGravityPacket() {} SetDungeonGravityPacket::SetDungeonGravityPacket(DungeonId dungeonId, Maybe gravity) @@ -1077,6 +1153,18 @@ void SetDungeonGravityPacket::write(DataStream& ds) const { ds.write(gravity); } +void SetDungeonGravityPacket::readJson(Json const& json) { + dungeonId = json.getUInt("dungeonId"); + gravity = json.optFloat("gravity"); +} + +Json SetDungeonGravityPacket::writeJson() const { + return JsonObject{ + {"dungeonId", dungeonId}, + {"gravity", jsonFromMaybe(gravity)} + }; +} + SetDungeonBreathablePacket::SetDungeonBreathablePacket() {} SetDungeonBreathablePacket::SetDungeonBreathablePacket(DungeonId dungeonId, Maybe breathable) @@ -1092,6 +1180,18 @@ void SetDungeonBreathablePacket::write(DataStream& ds) const { ds.write(breathable); } +void SetDungeonBreathablePacket::readJson(Json const& json) { + dungeonId = json.getUInt("dungeonId"); + breathable = json.optBool("breathable"); +} + +Json SetDungeonBreathablePacket::writeJson() const { + return JsonObject{ + {"dungeonId", dungeonId}, + {"breathable", jsonFromMaybe(breathable)} + }; +} + SetPlayerStartPacket::SetPlayerStartPacket() {} SetPlayerStartPacket::SetPlayerStartPacket(Vec2F playerStart, bool respawnInWorld) : playerStart(playerStart), respawnInWorld(respawnInWorld) {} @@ -1106,6 +1206,18 @@ void SetPlayerStartPacket::write(DataStream& ds) const { ds.write(respawnInWorld); } +void SetPlayerStartPacket::readJson(Json const& json) { + playerStart = jsonToVec2F(json.get("playerStart")); + respawnInWorld = json.getBool("respawnInWorld"); +} + +Json SetPlayerStartPacket::writeJson() const { + return JsonObject{ + {"playerStart", jsonFromVec2F(playerStart)}, + {"respawnInWorld", respawnInWorld} + }; +} + FindUniqueEntityResponsePacket::FindUniqueEntityResponsePacket() {} FindUniqueEntityResponsePacket::FindUniqueEntityResponsePacket(String uniqueEntityId, Maybe entityPosition) diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp index 7c76037..71ab09f 100644 --- a/source/game/StarNetPackets.hpp +++ b/source/game/StarNetPackets.hpp @@ -132,6 +132,9 @@ struct Packet { virtual void writeLegacy(DataStream& ds) const; virtual void write(DataStream& ds) const = 0; + virtual void readJson(Json const& json); + virtual Json writeJson() const; + PacketCompressionMode compressionMode() const; void setCompressionMode(PacketCompressionMode compressionMode); @@ -216,6 +219,9 @@ struct ChatReceivePacket : PacketBase { void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + ChatReceivedMessage receivedMessage; }; @@ -271,8 +277,11 @@ struct PausePacket : PacketBase { void writeLegacy(DataStream& ds) const override; void write(DataStream& ds) const override; - bool pause; - float timescale; + void readJson(Json const& json) override; + Json writeJson() const override; + + bool pause = false; + float timescale = 1.0f; }; struct ServerInfoPacket : PacketBase { @@ -282,6 +291,9 @@ struct ServerInfoPacket : PacketBase { void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + uint16_t players; uint16_t maxPlayers; }; @@ -505,6 +517,9 @@ struct GiveItemPacket : PacketBase { void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + ItemDescriptor item; }; @@ -526,6 +541,9 @@ struct UpdateTileProtectionPacket : PacketBase void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + DungeonId dungeonId; bool isProtected; }; @@ -537,6 +555,9 @@ struct SetDungeonGravityPacket : PacketBase { void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + DungeonId dungeonId; Maybe gravity; }; @@ -548,6 +569,9 @@ struct SetDungeonBreathablePacket : PacketBase void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + DungeonId dungeonId; Maybe breathable; }; @@ -559,6 +583,9 @@ struct SetPlayerStartPacket : PacketBase { void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + Vec2F playerStart; bool respawnInWorld; }; @@ -795,6 +822,9 @@ struct EntityMessagePacket : PacketBase { void read(DataStream& ds) override; void write(DataStream& ds) const override; + void readJson(Json const& json) override; + Json writeJson() const override; + Variant entityId; String message; JsonArray args; @@ -820,6 +850,9 @@ struct UpdateWorldPropertiesPacket : PacketBase