diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18f3ea6..5a11d4d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,8 @@ add_subdirectory(lib/base)
add_subdirectory(lib/impl)
add_subdirectory(lib/http)
+add_subdirectory(lib/aries)
+
# projects
add_subdirectory(src)
diff --git a/lib/aries/CMakeLists.txt b/lib/aries/CMakeLists.txt
new file mode 100644
index 0000000..d2a0717
--- /dev/null
+++ b/lib/aries/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(ls_aries
+ Tags.cpp
+)
+
+lobbyserver_target(ls_aries)
+
+target_link_libraries(ls_aries PUBLIC
+ base::base
+)
+
+add_library(ls::aries ALIAS ls_aries)
diff --git a/lib/aries/Message.hpp b/lib/aries/Message.hpp
new file mode 100644
index 0000000..be42a77
--- /dev/null
+++ b/lib/aries/Message.hpp
@@ -0,0 +1,22 @@
+#pragma once
+#include
+#include
+
+namespace ls::aries {
+
+ /// The Aries message header.
+ struct [[gnu::packed]] AriesMessageHeader {
+ /// Message FourCC.
+ base::FourCC32_t typeCode {};
+
+ /// Apparently a extra 4 bytes of FourCC?
+ base::FourCC32_t typeCodeHi {};
+
+ /// The size of the message payload. Is network order (big endian), and includes the size of this header
+ base::NetworkOrder messageSize {};
+ };
+
+ // Sanity checking.
+ static_assert(sizeof(AriesMessageHeader) == 12, "Aries message header size is invalid");
+
+} // namespace ls::proto
\ No newline at end of file
diff --git a/lib/aries/MessageIo.hpp b/lib/aries/MessageIo.hpp
new file mode 100644
index 0000000..f366c42
--- /dev/null
+++ b/lib/aries/MessageIo.hpp
@@ -0,0 +1,63 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ls::aries {
+
+ constexpr static auto MAX_PAYLOAD_SIZE_IN_MB = 1;
+ constexpr static auto MAX_PAYLOAD_SIZE_IN_BYTES = MAX_PAYLOAD_SIZE_IN_MB * (1024 * 1024);
+
+ /// Raw read aries massage.
+ struct RawAriesMessage {
+ AriesMessageHeader header;
+ std::vector tagPayload;
+ };
+
+ namespace errors {
+
+ struct TagPayloadTooLarge : std::exception {
+ TagPayloadTooLarge(u32 size)
+ : payloadSize(size) {
+ whatStr = std::format("Tag payload over {} MB (Max is {}MB).", (static_cast(payloadSize) / 1024 / 1024), MAX_PAYLOAD_SIZE_IN_MB);
+ }
+
+ const char* what() const noexcept override {
+ return whatStr.c_str();
+ }
+
+ private:
+ u32 payloadSize;
+ std::string whatStr;
+ };
+
+ } // namespace errors
+
+ /// Reads an Aries message from an Boost.Asio async read stream.
+ template
+ base::Awaitable AsyncReadAriesMessage(AsyncReadStream& stream) {
+ RawAriesMessage res;
+
+ // Read the header first
+ co_await asio::async_read(stream, asio::buffer(&res.header, sizeof(res.header)), asio::deferred);
+
+ auto realPayloadSize = res.header.messageSize - sizeof(res.header);
+
+ // Read tag payload (if there is one)
+ if(res.header.messageSize != sizeof(res.header)) {
+ // Sanity check. I don't expect game payloads to ever reach this large, but who knows.
+ if(realPayloadSize > MAX_PAYLOAD_SIZE_IN_BYTES)
+ throw errors::TagPayloadTooLarge(realPayloadSize);
+
+ res.tagPayload.resize(realPayloadSize);
+
+ co_await asio::async_read(stream, asio::buffer(res.tagPayload), asio::deferred);
+ }
+
+ co_return res;
+ }
+
+} // namespace ls::aries
\ No newline at end of file
diff --git a/lib/aries/README.md b/lib/aries/README.md
new file mode 100644
index 0000000..5547579
--- /dev/null
+++ b/lib/aries/README.md
@@ -0,0 +1,3 @@
+# aries
+
+Library for working with DirtySDK Aries (old-lobby) protocol messages.
\ No newline at end of file
diff --git a/lib/aries/Tags.cpp b/lib/aries/Tags.cpp
new file mode 100644
index 0000000..8ad2046
--- /dev/null
+++ b/lib/aries/Tags.cpp
@@ -0,0 +1,101 @@
+#include
+
+namespace ls::aries {
+
+ bool ParseTagField(std::span tagFieldData, TagMap& outMap) {
+ // Nothing to parse,
+ // which isn't exclusively a failure condition.
+ if(tagFieldData.empty())
+ return true;
+
+ std::string key;
+ std::string val;
+
+ usize inputIndex = 0;
+
+ // TODO: Investigate rewriting this using ragel or something, so it's not something that has to be
+ // heavily maintained or unit tested to avoid bugs.
+ //
+ // Also: maybe we should strongly type or deserialize binary payloads. There are multiple variants of these..
+
+ enum class ReaderState : u32 {
+ InKey, ///< The state machine is currently parsing a key.
+ InValue ///< The state machine is currently parsing a value.
+ } state { ReaderState::InKey };
+
+ // Parse all properties, using a fairly simple state machine to do so.
+ //
+ // State transition mappings:
+ // = - from key to value state (if in key state)
+ // \n - from value to key state (if in value state, otherwise error)
+
+ while(inputIndex != tagFieldData.size()) {
+ switch(tagFieldData[inputIndex]) {
+ case '=':
+ if(state == ReaderState::InKey) {
+ state = ReaderState::InValue;
+ break;
+ } else {
+ // If we're in the value state, we're allowed to nest = signs, I think.
+ // if not, then this is PROBABLY an error state.
+ val += static_cast(tagFieldData[inputIndex]);
+ }
+ break;
+
+ case '\n':
+ if(state == ReaderState::InValue) {
+ outMap[key] = val;
+
+ // Reset the state machine, to read another property.
+ key.clear();
+ val.clear();
+ state = ReaderState::InKey;
+ break;
+ } else {
+ // If we get here in the key state, this is DEFINITELY an error.
+ return false;
+ }
+
+ // Any other characters are not important to the state machine,
+ // and are instead written to the given staging string for the current
+ // state machine state.
+ default:
+ switch(state) {
+ case ReaderState::InKey:
+ key += static_cast(tagFieldData[inputIndex]);
+ break;
+ case ReaderState::InValue:
+ // Skip past quotation marks.
+ if(static_cast(tagFieldData[inputIndex]) == '\"' || static_cast(tagFieldData[inputIndex]) == '\'')
+ break;
+
+ val += static_cast(tagFieldData[inputIndex]);
+ break;
+ }
+ break;
+ }
+
+ inputIndex++;
+ }
+
+ // Parse succeeded
+ return true;
+ }
+
+ void SerializeTagFields(const TagMap& map, std::string& outStr) {
+ std::string tagFieldBuffer{};
+
+ // Reserve a sane amount, to avoid allocations when serializing
+ // (in most cases; larger tag count MIGHT still cause some allocation pressure).
+ tagFieldBuffer.reserve(512);
+
+ // Serialize the tag fields
+ for(auto [key, value] : map)
+ tagFieldBuffer += std::format("{}={}\n", key, value);
+
+ // Null terminate it. (TODO: We shouldn't have to do this anymore, std::string does this on its own)
+ tagFieldBuffer.push_back('\0');
+
+ outStr = std::move(tagFieldBuffer);
+ }
+} // namespace ls::aries
\ No newline at end of file
diff --git a/lib/aries/Tags.hpp b/lib/aries/Tags.hpp
new file mode 100644
index 0000000..ba71039
--- /dev/null
+++ b/lib/aries/Tags.hpp
@@ -0,0 +1,17 @@
+
+#include
+#include
+
+namespace ls::aries {
+
+ using TagMap = std::unordered_map;
+
+ /// Parses tag field data to a TagMap.
+ /// # Returns
+ /// True on success; false otherwise (TODO: Move to exceptions or error_category)
+ bool ParseTagField(std::span tagFieldData, TagMap& outMap);
+
+ /// Serializes a TagMap to a string.
+ void SerializeTagFields(const TagMap& map, std::string& outStr);
+
+}
\ No newline at end of file
diff --git a/lib/base/fourcc.hpp b/lib/base/fourcc.hpp
index 48cb320..424e696 100644
--- a/lib/base/fourcc.hpp
+++ b/lib/base/fourcc.hpp
@@ -18,15 +18,20 @@ namespace base {
case std::endian::big:
return static_cast((fccString[0] << 24) | (fccString[1] << 16) | (fccString[2] << 8) | fccString[3]);
- // endian::native is practically implemented in most standard libraries
- // by aliasing the native endian enumerator, so that it will match
- // one of the two cases here. therefore this code is literally useless
- // and i have no idea why i even wrote it 4 years ago :')
- //default:
- // throw "Invalid endian provided? How'd you do that?"; // NOLINT
+ // endian::native is practically implemented in most standard libraries
+ // by aliasing the native endian enumerator, so that it will match
+ // one of the two cases here. therefore this code is literally useless
+ // and i have no idea why i even wrote it 4 years ago :')
+ // default:
+ // throw "Invalid endian provided? How'd you do that?"; // NOLINT
}
}
+ inline std::string FourCC32ToString(FourCC32_t fcc) {
+ auto* fccAsBytes = std::bit_cast(&fcc);
+ return std::format("{:c}{:c}{:c}{:c}", fccAsBytes[0], fccAsBytes[1], fccAsBytes[2], fccAsBytes[3]);
+ }
+
// TODO: 64-bit version which returns a u64 (if required?)
} // namespace base
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cd53aaf..b216733 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -19,4 +19,5 @@ target_link_libraries(lobbyserver PRIVATE
base::base
base::http
Boost::json
+ ls::aries
)
diff --git a/src/DirtySockClient.cpp b/src/DirtySockClient.cpp
index 9ae827e..7f9243e 100644
--- a/src/DirtySockClient.cpp
+++ b/src/DirtySockClient.cpp
@@ -1,17 +1,18 @@
#include "DirtySockClient.hpp"
-#include
-#include
-#include
+#include
#include "DirtySockServer.hpp"
-constexpr static auto MAX_PAYLOAD_SIZE_IN_MB = 4;
-constexpr static auto MAX_PAYLOAD_SIZE_IN_BYTES = MAX_PAYLOAD_SIZE_IN_MB * (1024 * 1024);
-
// All our Asio/network related ops set this expiry time before they call Asio ops
// so that Beast's stream timer stuff can work its magic and automatically timeout.
-constexpr static auto EXPIRY_TIME = std::chrono::seconds(10);
+//
+// The read timeout is selected as high as it is mostly because Dirtysock's LobbyAPI seems to prefer pings
+// (That is, ICMP Echo Requests) to determine if the server is responding, and does not seem to send
+// periodic data as a result.. A bit of a iffy decision, but it was one made over 20 years ago and it worked
+// for at least 5, so I can't really complain that much.
+constexpr static auto READ_EXPIRY_TIME = std::chrono::seconds(960);
+constexpr static auto WRITE_EXPIRY_TIME = std::chrono::seconds(10);
namespace ls {
@@ -48,39 +49,28 @@ namespace ls {
}
base::Awaitable DirtySockClient::Network_ReadMessage() {
- proto::WireMessageHeader header;
- std::vector propertyBuffer;
-
try {
- // Read the header first
- stream.expires_after(EXPIRY_TIME);
- co_await asio::async_read(stream, asio::buffer(&header, sizeof(header)), asio::deferred);
+ // Set the read expiry time. We only do this once every read because
+ // we want the timer to account throughout the whole AsyncReadAriesMessage
+ // operation, which issues multiple I/O operations to complete its work.
+ stream.expires_after(READ_EXPIRY_TIME);
- auto realPayloadSize = header.payloadSize - sizeof(header);
-
- // Sanity check. I don't expect game payloads to ever reach this large, but who knows.
- if(realPayloadSize > MAX_PAYLOAD_SIZE_IN_BYTES) {
- logger->error("{}: WOAH! Client sent a message with a payload size of {} MB (Max is {}MB).", GetAddress().to_string(), (static_cast(header.payloadSize) / 1024 / 1024), MAX_PAYLOAD_SIZE_IN_MB);
- co_return nullptr;
- }
+ auto res = co_await aries::AsyncReadAriesMessage(stream);
// If the message type isn't in the server's allowed message list, give up.
// (we probably should throw instead...)
if(!server->allowedMessages.empty()) {
- if(!server->allowedMessages.contains(static_cast(header.typeCode)))
+ if(!server->allowedMessages.contains(res.header.typeCode))
co_return nullptr;
}
- propertyBuffer.resize(realPayloadSize);
-
- stream.expires_after(EXPIRY_TIME);
- co_await asio::async_read(stream, asio::buffer(propertyBuffer), asio::deferred);
-
- logger->info("read properties");
-
// this function may fail and also return nullptr. Maybe we should instead throw an exception there
// (that we leave to callers to catch)
- co_return MessageFactory::CreateAndParseMessage(header, propertyBuffer);
+ co_return AriesMessageFactory::CreateAndParseMessage(res.header, res.tagPayload);
+
+ } catch(aries::errors::TagPayloadTooLarge& large) {
+ logger->error("{}: {}", GetAddress().to_string(), large.what());
+ co_return nullptr;
} catch(bsys::system_error& ec) {
// Instead of bubbling up errors we DO care about, rethrow them to the higher level
// calling us.
@@ -100,7 +90,7 @@ namespace ls {
message->SerializeTo(buf);
try {
- stream.expires_after(std::chrono::seconds(EXPIRY_TIME));
+ stream.expires_after(std::chrono::seconds(WRITE_EXPIRY_TIME));
co_await asio::async_write(stream, asio::buffer(buf), asio::deferred);
} catch(bsys::system_error& ec) {
if(ec.code() != asio::error::operation_aborted || ec.code() != beast::error::timeout)
@@ -164,7 +154,7 @@ namespace ls {
}
} catch(bsys::system_error& ec) {
if(ec.code() == asio::error::eof) {
- logger->info("{}: Connection closed", GetAddress().to_string());
+ logger->info("Server \"{}\": {} closed connection", GetServer()->name, GetAddress().to_string());
} else if(ec.code() != asio::error::operation_aborted)
logger->error("{}: Error in DirtySockClient::Coro_ReaderEnd(): {}", GetAddress().to_string(), ec.what());
}
@@ -174,7 +164,7 @@ namespace ls {
}
base::Awaitable DirtySockClient::Run() {
- logger->info("{}: Got connection", GetAddress().to_string());
+ logger->info("Server \"{}\": Got connection from {}", GetServer()->name, GetAddress().to_string());
asio::co_spawn(
stream.get_executor(), [self = shared_from_this()] {
return self->Coro_WriterEnd();
diff --git a/src/DirtySockClient.hpp b/src/DirtySockClient.hpp
index 71e1793..2e3f218 100644
--- a/src/DirtySockClient.hpp
+++ b/src/DirtySockClient.hpp
@@ -12,8 +12,8 @@ namespace ls {
struct DirtySockServer;
struct DirtySockClient : public std::enable_shared_from_this {
- using MessagePtr = base::Ref;
- using ConstMessagePtr = base::Ref;
+ using MessagePtr = base::Ref;
+ using ConstMessagePtr = base::Ref;
using Protocol = asio::ip::tcp;
using Stream = base::BeastStream;
diff --git a/src/DirtySockServer.cpp b/src/DirtySockServer.cpp
index 5b48afc..40e1fe5 100644
--- a/src/DirtySockServer.cpp
+++ b/src/DirtySockServer.cpp
@@ -6,8 +6,8 @@
namespace ls {
- DirtySockServer::DirtySockServer(asio::any_io_executor exec)
- : exec(exec), acceptor(exec) {
+ DirtySockServer::DirtySockServer(asio::any_io_executor exec, std::string_view name)
+ : exec(exec), acceptor(exec), name(name.data(), name.length()) {
}
void DirtySockServer::Start(const Protocol::endpoint& ep) {
@@ -34,7 +34,7 @@ namespace ls {
acceptor.bind(endpoint);
acceptor.listen(asio::socket_base::max_listen_connections);
- logger->info("DirtySockServer listening on {}:{}", endpoint.address().to_string(), endpoint.port());
+ logger->info("DirtySockServer \"{}\" listening on {}:{}", name, endpoint.address().to_string(), endpoint.port());
while(true) {
auto socket = co_await acceptor.async_accept(asio::deferred);
diff --git a/src/DirtySockServer.hpp b/src/DirtySockServer.hpp
index 1858e88..9bcebdb 100644
--- a/src/DirtySockServer.hpp
+++ b/src/DirtySockServer.hpp
@@ -16,7 +16,7 @@ namespace ls {
/// alias for thing
using AllowedMessagesSet = std::set;
- DirtySockServer(asio::any_io_executor exec);
+ DirtySockServer(asio::any_io_executor exec, std::string_view name);
void Start(const Protocol::endpoint& endpoint);
@@ -39,6 +39,7 @@ namespace ls {
std::set> clientSet;
+ std::string name;
base::Ref logger = spdlog::get("ls_dsock_server");
};
diff --git a/src/IMessage.cpp b/src/IMessage.cpp
index 0de4ba9..7ee176e 100644
--- a/src/IMessage.cpp
+++ b/src/IMessage.cpp
@@ -6,166 +6,77 @@
// So debug message can just reply
#include "DirtySockClient.hpp"
-#include "WireMessage.hpp"
namespace ls {
- IMessage::IMessage(const proto::WireMessageHeader& header)
+ IAriesMessage::IAriesMessage(const aries::AriesMessageHeader& header)
: header(header) {
}
- bool IMessage::ParseFromInputBuffer(std::span inputBuffer) {
- // Nothing to parse,
- // which isn't exclusively a failure condition.
- if(inputBuffer.empty())
- return true;
-
- std::string key;
- std::string val;
-
- usize inputIndex = 0;
-
- // TODO: Investigate rewriting this using ragel or something, so it's not something that has to be
- // heavily maintained or unit tested to avoid bugs.
-
- enum class ReaderState : u32 {
- InKey, ///< The state machine is currently parsing a key.
- InValue ///< The state machine is currently parsing a value.
- } state { ReaderState::InKey };
-
- // Parse all properties, using a fairly simple state machine to do so.
- //
- // State transition mappings:
- // = - from key to value state (if in key state)
- // \n - from value to key state (if in value state, otherwise error)
-
- while(inputIndex != inputBuffer.size()) {
- switch(inputBuffer[inputIndex]) {
- case '=':
- if(state == ReaderState::InKey) {
- state = ReaderState::InValue;
- break;
- } else {
- // If we're in the value state, we're allowed to nest = signs, I think.
- // if not, then this is PROBABLY an error state.
- val += static_cast(inputBuffer[inputIndex]);
- }
- break;
-
- case '\n':
- if(state == ReaderState::InValue) {
- properties[key] = val;
-
- // Reset the state machine, to read another property.
- key.clear();
- val.clear();
- state = ReaderState::InKey;
- break;
- } else {
- // If we get here in the key state, this is DEFINITELY an error.
- return false;
- }
-
- // Any other characters are not important to the state machine,
- // and are instead written to the given staging string for the current
- // state machine state.
- default:
- switch(state) {
- case ReaderState::InKey:
- key += static_cast(inputBuffer[inputIndex]);
- break;
- case ReaderState::InValue:
- // Skip past quotation marks.
- if(static_cast(inputBuffer[inputIndex]) == '\"' || static_cast(inputBuffer[inputIndex]) == '\'')
- break;
-
- val += static_cast(inputBuffer[inputIndex]);
- break;
- }
- break;
- }
-
- inputIndex++;
- }
-
- // Parse succeeded
- return true;
+ bool IAriesMessage::ParseFromInputBuffer(std::span inputBuffer) {
+ return aries::ParseTagField(inputBuffer, tagFields);
}
- void IMessage::SerializeTo(std::vector& dataBuffer) const {
+ void IAriesMessage::SerializeTo(std::vector& dataBuffer) const {
std::string serializedProperties;
- // Reserve a sane amount, to avoid allocations when serializing properties
- // (in most cases; larger messages MIGHT still cause some allocation pressure.)
- serializedProperties.reserve(512);
-
- // Serialize properties
- {
- auto i = properties.size();
- for(auto [key, value] : properties)
- if(--i != 0)
- serializedProperties += std::format("{}={}\n", key, value);
- else
- serializedProperties += std::format("{}={}", key, value);
- }
-
- // Null terminate the property data.
- serializedProperties.push_back('\0');
+ aries::SerializeTagFields(tagFields, serializedProperties);
// Create an appropriate header for the data.
- proto::WireMessageHeader header {
- .typeCode = static_cast(TypeCode()),
- .typeCodeHi = 0,
- .payloadSize = sizeof(proto::WireMessageHeader) + serializedProperties.length() - 1
+ aries::AriesMessageHeader newHeader {
+ .typeCode = header.typeCode,
+ .typeCodeHi = header.typeCodeHi,
+ .messageSize = sizeof(aries::AriesMessageHeader) + serializedProperties.length()
};
- auto fullLength = sizeof(proto::WireMessageHeader) + serializedProperties.length();
+ auto fullLength = sizeof(aries::AriesMessageHeader) + serializedProperties.length();
// Resize the output buffer to the right size
dataBuffer.resize(fullLength);
// Write to the output buffer now.
- memcpy(&dataBuffer[0], &header, sizeof(proto::WireMessageHeader));
- memcpy(&dataBuffer[sizeof(proto::WireMessageHeader)], serializedProperties.data(), serializedProperties.length());
+ memcpy(&dataBuffer[0], &newHeader, sizeof(aries::AriesMessageHeader));
+ memcpy(&dataBuffer[sizeof(aries::AriesMessageHeader)], serializedProperties.data(), serializedProperties.length());
}
- const std::optional IMessage::MaybeGetKey(const std::string& key) const {
- if(properties.find(key) == properties.end())
+ const std::optional IAriesMessage::MaybeGetKey(const std::string& key) const {
+ if(tagFields.find(key) == tagFields.end())
return std::nullopt;
else
- return properties.at(key);
+ return tagFields.at(key);
}
- void IMessage::SetOrAddProperty(const std::string& key, const std::string& value) {
- properties[key] = value;
+ void IAriesMessage::SetOrAddProperty(const std::string& key, const std::string& value) {
+ tagFields[key] = value;
}
// message factory
/// Debug message, used to.. well, debug, obviously.
- struct DebugMessage : IMessage {
- explicit DebugMessage(const proto::WireMessageHeader& header)
- : IMessage(header) {
+ struct DebugMessage : IAriesMessage {
+ explicit DebugMessage(const aries::AriesMessageHeader& header)
+ : IAriesMessage(header) {
}
base::Awaitable Process(base::Ref client) override {
- auto* fccbytes = std::bit_cast(&header.typeCode);
+ spdlog::info("Unhandled Aries message \"{}\" \"{}\"", FourCC32ToString(header.typeCode), base::FourCC32ToString(header.typeCodeHi));
+ if(!tagFields.empty()) {
+ spdlog::info("With tags:");
- spdlog::info("Debug Message: FourCC lo: \"{:c}{:c}{:c}{:c}\"", fccbytes[0], fccbytes[1], fccbytes[2], fccbytes[3]);
- spdlog::info("Debug Message Properties:");
-
- for(auto [key, value] : properties)
- spdlog::info("{}: {}", key, value);
-
- // a bit :( however it works to just replay the message.
+ for(auto [key, value] : tagFields)
+ spdlog::info("{}={}", key, value);
+ }
+ // This snippet is a fair bit :( however it works to just replay the message.
+ // And it's like 1kb at most it's not going to hurt much for code that will be snipped off
+ // at some point anyways lol
client->Send(std::make_shared(*this));
co_return;
}
};
- struct MessageWithFourCC : IMessage {
- explicit MessageWithFourCC(const proto::WireMessageHeader& header)
- : IMessage(header) {
+ struct AriesSendOnlyMessage : IAriesMessage {
+ explicit AriesSendOnlyMessage(const aries::AriesMessageHeader& header)
+ : IAriesMessage(header) {
}
base::Awaitable Process(base::Ref client) override {
@@ -174,16 +85,16 @@ namespace ls {
}
};
- MessageFactory::FactoryMap& MessageFactory::GetFactoryMap() {
- static MessageFactory::FactoryMap factoryMap;
+ AriesMessageFactory::FactoryMap& AriesMessageFactory::GetFactoryMap() {
+ static AriesMessageFactory::FactoryMap factoryMap;
return factoryMap;
}
- base::Ref MessageFactory::CreateAndParseMessage(const proto::WireMessageHeader& header, std::span propertyDataBuffer) {
+ base::Ref AriesMessageFactory::CreateAndParseMessage(const aries::AriesMessageHeader& header, std::span propertyDataBuffer) {
const auto& factories = GetFactoryMap();
- base::Ref ret = nullptr;
+ base::Ref ret = nullptr;
- if(const auto it = factories.find(static_cast(header.typeCode)); it != factories.end())
+ if(const auto it = factories.find(header.typeCode); it != factories.end())
ret = (it->second)(header);
else
ret = std::make_shared(header);
@@ -194,14 +105,15 @@ namespace ls {
return ret;
}
- base::Ref MessageFactory::CreateMessageWithFourCC(base::FourCC32_t fourCC) {
- auto fakeHeader = proto::WireMessageHeader {
- static_cast(fourCC),
- 0,
- 0
+ base::Ref AriesMessageFactory::CreateSendMessage(base::FourCC32_t fourCC, base::FourCC32_t fourccHi) {
+ // A fake header so that we can just use the constructor of IAriesMessage
+ auto fakeHeader = aries::AriesMessageHeader {
+ fourCC,
+ fourccHi,
+ sizeof(aries::AriesMessageHeader)
};
- return std::make_shared(fakeHeader);
+ return std::make_shared(fakeHeader);
}
} // namespace ls
\ No newline at end of file
diff --git a/src/IMessage.hpp b/src/IMessage.hpp
index 65ced5b..2f6c792 100644
--- a/src/IMessage.hpp
+++ b/src/IMessage.hpp
@@ -3,16 +3,17 @@
#include
#include
-#include "WireMessage.hpp"
+#include
+#include
namespace ls {
- struct Server;
struct DirtySockClient;
- struct IMessage {
- explicit IMessage(const proto::WireMessageHeader& header);
+ /// A higher level repressentation of an Aries message. Contains a method to process the data.
+ struct IAriesMessage {
+ explicit IAriesMessage(const aries::AriesMessageHeader& header);
- virtual ~IMessage() = default;
+ virtual ~IAriesMessage() = default;
/// Parses from input buffer. The data must live until
/// this function returns.
@@ -20,11 +21,9 @@ namespace ls {
/// error code enumeration..) if the parsing fails.
bool ParseFromInputBuffer(std::span data);
- /// Serializes to a output data buffer.
+ /// Serializes this Aries message to a output data buffer.
void SerializeTo(std::vector& dataBuffer) const;
- base::FourCC32_t TypeCode() const { return static_cast(header.typeCode); }
-
/// Process a single message.
virtual base::Awaitable Process(base::Ref client) = 0;
@@ -32,54 +31,54 @@ namespace ls {
void SetOrAddProperty(const std::string& key, const std::string& value);
- const proto::WireMessageHeader& GetHeader() const { return header; }
+ const aries::AriesMessageHeader& GetHeader() const { return header; }
protected:
- proto::WireMessageHeader header;
+ aries::AriesMessageHeader header;
/// all properties.
- std::unordered_map properties {};
+ aries::TagMap tagFields {};
};
- struct MessageFactory {
+ struct AriesMessageFactory {
/// Creates and parses the given implementation of IMessage.
- static base::Ref CreateAndParseMessage(const proto::WireMessageHeader& header, std::span propertyDataBuffer);
+ static base::Ref CreateAndParseMessage(const aries::AriesMessageHeader& header, std::span propertyDataBuffer);
/// Creates a message intended for sending to a client.
- static base::Ref CreateMessageWithFourCC(base::FourCC32_t fourCC);
+ static base::Ref CreateSendMessage(base::FourCC32_t fourCC, base::FourCC32_t fourccHi = {});
private:
template
- friend struct MessageMixin;
+ friend struct AriesMessageMixIn;
- using FactoryMap = std::unordered_map (*)(const proto::WireMessageHeader&)>;
+ using FactoryMap = std::unordered_map (*)(const aries::AriesMessageHeader&)>;
static FactoryMap& GetFactoryMap();
};
template
- struct MessageMixin : IMessage {
+ struct AriesMessageMixIn : IAriesMessage {
constexpr static auto TYPE_CODE = base::FourCC32();
- explicit MessageMixin(const proto::WireMessageHeader& header)
- : IMessage(header) {
+ explicit AriesMessageMixIn(const aries::AriesMessageHeader& header)
+ : IAriesMessage(header) {
static_cast(registered);
}
private:
static bool Register() {
- MessageFactory::GetFactoryMap().insert({ TYPE_CODE, [](const proto::WireMessageHeader& header) -> base::Ref {
- return std::make_shared(header);
- } });
+ AriesMessageFactory::GetFactoryMap().insert({ TYPE_CODE, [](const aries::AriesMessageHeader& header) -> base::Ref {
+ return std::make_shared(header);
+ } });
return true;
}
static inline bool registered = Register();
};
// :( Makes the boilerplate shorter and sweeter (and easier to change) though.
-#define LS_MESSAGE(T, fourCC) struct T : public ls::MessageMixin
+#define LS_MESSAGE(T, fourCC) struct T : public ls::AriesMessageMixIn
#define LS_MESSAGE_CTOR(T, fourCC) \
- explicit T(const ls::proto::WireMessageHeader& header) \
- : ls::MessageMixin(header) { \
+ explicit T(const ls::aries::AriesMessageHeader& header) \
+ : ls::AriesMessageMixIn(header) { \
}
} // namespace ls
\ No newline at end of file
diff --git a/src/Server.cpp b/src/Server.cpp
index 48a8f1a..a23c9fc 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -16,19 +16,25 @@ namespace ls {
base::Awaitable Server::Start() {
// TODO: make mariadb connection first, if this fails blow up
- lobbyServer = std::make_shared(exec);
+ lobbyRdirServer = std::make_shared(exec, "Redirector server");
+ lobbyRdirServer->SetAllowedMessages({ base::FourCC32<"@dir">() });
- lobbyServer->Start(config.lobbyListenEndpoint);
+ lobbyRdirServer->Start(config.lobbyListenEndpoint);
- if(!lobbyServer->Listening()) {
+ if(!lobbyRdirServer->Listening()) {
// uh oh worm..
- logger->error("for some reason lobby server isnt listening..");
+ logger->error("for some reason the redirector server isnt listening..");
co_return;
}
- buddyServer = std::make_shared(exec);
+ buddyServer = std::make_shared(exec, "Lobby server");
buddyServer->Start(config.buddyListenEndpoint);
+ if(!lobbyRdirServer->Listening()) {
+ // uh oh worm..
+ logger->error("for some reason the lobby server isnt listening..");
+ co_return;
+ }
// TODO: http server? there's apparently some stuff we can have that uses it
diff --git a/src/Server.hpp b/src/Server.hpp
index 25a50f6..f59d9eb 100644
--- a/src/Server.hpp
+++ b/src/Server.hpp
@@ -30,7 +30,7 @@ namespace ls {
base::AsyncConditionVariable stopCv;
bool stopping { false };
- base::Ref lobbyServer;
+ base::Ref lobbyRdirServer;
base::Ref buddyServer;
Config config;
diff --git a/src/WireMessage.hpp b/src/WireMessage.hpp
deleted file mode 100644
index bd853c6..0000000
--- a/src/WireMessage.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-#include
-
-namespace ls::proto {
-
- /// The on-wire message header.
- struct [[gnu::packed]] WireMessageHeader {
- /// Message FourCC.
- u32 typeCode {};
-
- /// Apparently a extra 4 bytes of FourCC?
- u32 typeCodeHi {};
-
- /// The size of the data payload.
- base::NetworkOrder payloadSize {};
- };
-
- // Sanity checking.
- static_assert(sizeof(WireMessageHeader) == 12, "Wire message header size is invalid");
-
-} // namespace ls::proto
\ No newline at end of file
diff --git a/src/messages/RdirMessage.cpp b/src/messages/RdirMessage.cpp
index daa4f56..e2b11d0 100644
--- a/src/messages/RdirMessage.cpp
+++ b/src/messages/RdirMessage.cpp
@@ -6,21 +6,21 @@
// clang-format off
-LS_MESSAGE(AtDirMessage, "@dir") {
- LS_MESSAGE_CTOR(AtDirMessage, "@dir")
+LS_MESSAGE(AriesRedirMessage, "@dir") {
+ LS_MESSAGE_CTOR(AriesRedirMessage, "@dir")
base::Awaitable Process(base::Ref client) override {
spdlog::info("Got redir message!");
- spdlog::info("@dir Properties:");
+ spdlog::info("@dir tags:");
- for(auto [key, value] : properties)
+ for(auto [key, value] : tagFields)
spdlog::info("{}: {}", key, value);
// create our @dir message we send BACK to the client.
- auto rdirOut = ls::MessageFactory::CreateMessageWithFourCC(base::FourCC32<"@dir">());
+ auto rdirOut = ls::AriesMessageFactory::CreateSendMessage(base::FourCC32<"@dir">());
- // TODO: Use the server class to get at this..
+ // TODO: Please use the server class to get at this..
rdirOut->SetOrAddProperty("ADDR", "192.168.1.149");
rdirOut->SetOrAddProperty("PORT", "10998");
// sample