Skip to content

Commit

Permalink
feat: binary messages (#2154)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pospelove authored Nov 14, 2024
1 parent ef9d6d5 commit 5338801
Show file tree
Hide file tree
Showing 68 changed files with 1,096 additions and 1,114 deletions.
20 changes: 20 additions & 0 deletions serialization/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
project(serialization)

include(${CMAKE_SOURCE_DIR}/cmake/apply_default_settings.cmake)

file(GLOB serialization_SRC "include/*.h" "src/*.cpp" "src/*.h")
add_library(serialization STATIC ${serialization_SRC})
target_include_directories(serialization PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_include_directories(serialization PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src)

apply_default_settings(TARGETS serialization)

find_package(slikenet CONFIG REQUIRED)
target_link_libraries(serialization PUBLIC SLikeNet)

find_package(simdjson CONFIG REQUIRED)
target_link_libraries(serialization PUBLIC simdjson::simdjson)

find_path(JSON_INCLUDE_DIR NAMES json.hpp PATH_SUFFIXES nlohmann)
get_filename_component(JSON_INCLUDE_DIR ${JSON_INCLUDE_DIR} DIRECTORY)
target_include_directories(serialization PUBLIC ${JSON_INCLUDE_DIR})
2 changes: 2 additions & 0 deletions serialization/cmakeproj.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
list(APPEND CMAKEPROJ_PROJECTS serialization)
set(CMAKEPROJ_PRIORITY_serialization 1)
108 changes: 108 additions & 0 deletions serialization/include/archives/BitStreamInputArchive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#pragma once
#include "concepts/Concepts.h"
#include <optional>
#include <slikenet/BitStream.h>
#include <stdexcept>
#include <string>
#include <vector>

#include "../impl/BitStreamUtil.h"
#include "../impl/BitStreamUtil.ipp"

#include <spdlog/spdlog.h>

class BitStreamInputArchive
{
public:
explicit BitStreamInputArchive(SLNet::BitStream& bitStream)
: bs(bitStream)
{
}

template <IntegralConstant T>
BitStreamInputArchive& Serialize(const char* key, T&)
{
// Compile time constant. Do nothing
// Maybe worth adding equality check
bs.IgnoreBytes(sizeof(typename T::value_type));
// spdlog::info("!!! deserialized integral constant {}", key);
return *this;
}

template <StringLike T>
BitStreamInputArchive& Serialize(const char* key, T& value)
{
value.clear();

uint32_t n = 0;
Serialize("size", n);

// TODO: check n before resizing, so that we don't allocate a huge vector
for (size_t i = 0; i < n; ++i) {
typename T::value_type element;
Serialize("element", element);
value.push_back(element);
}
return *this;
}

// Specialization for std::array
template <typename T, std::size_t N>
BitStreamInputArchive& Serialize(const char* key, std::array<T, N>& value)
{
for (size_t i = 0; i < N; ++i) {
Serialize("element", value[i]);
}
return *this;
}

template <ContainerLike T>
BitStreamInputArchive& Serialize(const char* key, T& value)
{
value.clear();

uint32_t n = 0;
Serialize("size", n);

// TODO: check n before resizing, so that we don't allocate a huge vector
for (size_t i = 0; i < n; ++i) {
typename T::value_type element;
Serialize("element", element);
value.push_back(element);
}
return *this;
}

template <Optional T>
BitStreamInputArchive& Serialize(const char* key, T& value)
{
bool hasValue = false;
Serialize("hasValue", hasValue);
if (hasValue) {
typename T::value_type actualValue;
Serialize("value", actualValue);
value = actualValue;
} else {
value = std::nullopt;
}
return *this;
}

template <Arithmetic T>
BitStreamInputArchive& Serialize(const char* key, T& value)
{
SerializationUtil::ReadFromBitStream(bs, value);
// spdlog::info("!!! deserialized arithmetic {} {}", key, value);
return *this;
}

template <NoneOfTheAbove T>
BitStreamInputArchive& Serialize(const char* key, T& value)
{
value.Serialize(*this);
// spdlog::info("!!! deserialized none of the above {}", key);
return *this;
}

SLNet::BitStream& bs;
};
96 changes: 96 additions & 0 deletions serialization/include/archives/BitStreamOutputArchive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#pragma once
#include "concepts/Concepts.h"
#include <optional>
#include <slikenet/BitStream.h>
#include <stdexcept>
#include <string>
#include <vector>

#include "../impl/BitStreamUtil.h"
#include "../impl/BitStreamUtil.ipp"

#include <spdlog/spdlog.h>

class BitStreamOutputArchive
{
public:
explicit BitStreamOutputArchive(SLNet::BitStream& bitStream)
: bs(bitStream)
{
}

template <IntegralConstant T>
BitStreamOutputArchive& Serialize(const char* key, T& value)
{
using ValueType = typename T::value_type;
auto actualValue = static_cast<ValueType>(value);
SerializationUtil::WriteToBitStream(bs, actualValue);
// spdlog::info("!!! serialized integral constant {} {}", key,
// static_cast<int>(static_cast<char>(value)));
return *this;
}

template <StringLike T>
BitStreamOutputArchive& Serialize(const char* key, T& value)
{
uint32_t n = static_cast<uint32_t>(value.size());
Serialize("size", n);

for (size_t i = 0; i < n; ++i) {
Serialize("element", value[i]);
}
return *this;
}

// Specialization for std::array
template <typename T, std::size_t N>
BitStreamOutputArchive& Serialize(const char* key, std::array<T, N>& value)
{
for (size_t i = 0; i < N; ++i) {
Serialize("element", value[i]);
}
return *this;
}

template <ContainerLike T>
BitStreamOutputArchive& Serialize(const char* key, T& value)
{
uint32_t n = static_cast<uint32_t>(value.size());
Serialize("size", n);

for (size_t i = 0; i < n; ++i) {
Serialize("element", value[i]);
}
return *this;
}

template <Optional T>
BitStreamOutputArchive& Serialize(const char* key, T& value)
{
bool hasValue = value.has_value();
Serialize("hasValue", hasValue);
if (hasValue) {
typename T::value_type& actualValue = *value;
Serialize("value", actualValue);
}
return *this;
}

template <Arithmetic T>
BitStreamOutputArchive& Serialize(const char* key, T& value)
{
SerializationUtil::WriteToBitStream(bs, value);
// spdlog::info("!!! serialized arithmetic {} {}", key, value);
return *this;
}

template <NoneOfTheAbove T>
BitStreamOutputArchive& Serialize(const char* key, T& value)
{
value.Serialize(*this);
// spdlog::info("!!! serialized none of the above {}");
return *this;
}

SLNet::BitStream& bs;
};
100 changes: 100 additions & 0 deletions serialization/include/archives/JsonInputArchive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#pragma once
#include "concepts/Concepts.h"
#include <nlohmann/json.hpp>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>

class JsonInputArchive
{
public:
explicit JsonInputArchive(const nlohmann::json& json)
: j(json)
{
}

template <IntegralConstant T>
JsonInputArchive& Serialize(const char* key, T& value)
{
// Compile time constant. Do nothing
// Maybe worth adding equality check
return *this;
}

template <StringLike T>
JsonInputArchive& Serialize(const char* key, T& value)
{
value = j.at(key).get<std::string>();
return *this;
}

// Specialization for std::array
template <typename T, std::size_t N>
JsonInputArchive& Serialize(const char* key, std::array<T, N>& value)
{
const auto& arr = j.at(key);
if (arr.size() != N) {
throw std::runtime_error(
"JSON array size does not match std::array size.");
}

nlohmann::json childArchiveInput = nlohmann::json::object();
for (size_t i = 0; i < N; ++i) {
childArchiveInput["element"] = arr.at(i);
JsonInputArchive childArchive(childArchiveInput);
childArchive.Serialize("element", value[i]);
}
return *this;
}

template <ContainerLike T>
JsonInputArchive& Serialize(const char* key, T& value)
{
value.clear();

const auto& arr = j.at(key);
nlohmann::json childArchiveInput = nlohmann::json::object();
for (auto& elementJson : arr) {
typename T::value_type element;
childArchiveInput["element"] = elementJson;
JsonInputArchive childArchive(childArchiveInput);
childArchive.Serialize("element", element);
value.insert(value.end(), element);
}
return *this;
}

template <Optional T>
JsonInputArchive& Serialize(const char* key, T& value)
{
auto it = j.find(key);
if (it != j.end() && !it->is_null()) {
typename T::value_type actualValue;
Serialize(key, actualValue);
value = actualValue;
} else {
value = std::nullopt;
}
return *this;
}

template <Arithmetic T>
JsonInputArchive& Serialize(const char* key, T& value)
{
value = j.at(key).get<T>();
return *this;
}

template <NoneOfTheAbove T>
JsonInputArchive& Serialize(const char* key, T& value)
{
const auto& obj = j.at(key);
JsonInputArchive childArchive(obj);
value.Serialize(childArchive);
return *this;
}

private:
const nlohmann::json& j;
};
68 changes: 68 additions & 0 deletions serialization/include/archives/JsonOutputArchive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once
#include "concepts/Concepts.h"
#include <nlohmann/json.hpp>
#include <vector>

class JsonOutputArchive
{
public:
template <IntegralConstant T>
JsonOutputArchive& Serialize(const char* key, const T& value)
{
j[key] = T::value;
return *this;
}

template <StringLike T>
JsonOutputArchive& Serialize(const char* key, const T& value)
{
j[key] = value;
return *this;
}

template <ContainerLike T>
JsonOutputArchive& Serialize(const char* key, const T& value)
{
nlohmann::json arr = nlohmann::json::array();
for (auto& element : value) {
JsonOutputArchive childArchive;
childArchive.Serialize("element", element);
arr.push_back(childArchive.j["element"]);
}
j[key] = arr;
return *this;
}

template <Optional T>
JsonOutputArchive& Serialize(const char* key, const T& value)
{
if (value.has_value()) {
Serialize(key, *value);
} else {
// enable this if you want to serialize nulls
// j[key] = nlohmann::json{};
}
return *this;
}

template <Arithmetic T>
JsonOutputArchive& Serialize(const char* key, const T& value)
{
j[key] = value;
return *this;
}

template <NoneOfTheAbove T>
JsonOutputArchive& Serialize(const char* key, const T& value)
{
nlohmann::json arr = nlohmann::json::object();

JsonOutputArchive childArchive;
const_cast<T&>(value).Serialize(childArchive);

j[key] = childArchive.j;
return *this;
}

nlohmann::json j = nlohmann::json::object();
};
Loading

0 comments on commit 5338801

Please sign in to comment.