Compare commits
3 Commits
5d0009c763
...
f5c46d5b46
Author | SHA1 | Date |
---|---|---|
May B. | f5c46d5b46 | |
May B. | f7c2f7069e | |
May B. | 9e9bb8987f |
|
@ -0,0 +1,29 @@
|
||||||
|
image: gcc
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install -y --no-install-recommends cmake libssl-dev libvulkan-dev xorg-dev doxygen graphviz
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- mkdir build # compile
|
||||||
|
- cd build
|
||||||
|
- cmake ..
|
||||||
|
- make -j2 univerxel univerxel-client univerxel-server docs
|
||||||
|
- mkdir -p ../out/full ../out/client ../out/server/content # package artifacts
|
||||||
|
- cp -r univerxel content ../out/full
|
||||||
|
- cp -r univerxel-client content ../out/client
|
||||||
|
- rm ../out/client/content/cert.pem ../out/client/content/key.pem ../out/client/content/zstd.dict
|
||||||
|
- cp univerxel-server ../out/server
|
||||||
|
- cp content/cert.pem content/key.pem content/zstd.dict ../out/server/content
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- out/full
|
||||||
|
- out/client
|
||||||
|
- out/server
|
||||||
|
- build/docs
|
||||||
|
expire_in: 1 week
|
||||||
|
# cache:
|
||||||
|
# paths:
|
||||||
|
# - "*.o"
|
|
@ -41,6 +41,26 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Run server (gdb)",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/build/univerxel-server",
|
||||||
|
"args": ["server.toml"],
|
||||||
|
"preLaunchTask": "Build server",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/build",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Activer l'impression en mode Pretty pour gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Run server (gdb debug)",
|
"name": "Run server (gdb debug)",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
|
|
|
@ -100,6 +100,12 @@
|
||||||
"dependsOrder": "sequence",
|
"dependsOrder": "sequence",
|
||||||
"dependsOn": ["cmake debug", "make"]
|
"dependsOn": ["cmake debug", "make"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Build server",
|
||||||
|
"group": "build",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn": ["cmake", "make server"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Build debug server",
|
"label": "Build debug server",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* \file server.cpp
|
* \file client.cpp
|
||||||
* \brief Univerxel client
|
* \brief Univerxel client
|
||||||
* \author Maelys Bois
|
* \author Maelys Bois
|
||||||
* \version 0.0.1
|
* \version 0.0.1
|
||||||
|
|
|
@ -24,6 +24,7 @@ inline std::string toHexa(const glm::vec4& rgb) {
|
||||||
return sstr.str();
|
return sstr.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Client side configuration
|
||||||
struct options {
|
struct options {
|
||||||
public:
|
public:
|
||||||
options(toml::node_view<toml::node> config) {
|
options(toml::node_view<toml::node> config) {
|
||||||
|
|
|
@ -370,10 +370,9 @@ namespace contouring {
|
||||||
const auto area_offset = glm::divide(std::get<0>(area.first).as_voxel());
|
const auto area_offset = glm::divide(std::get<0>(area.first).as_voxel());
|
||||||
robin_hood::unordered_set<chunk_pos> done;
|
robin_hood::unordered_set<chunk_pos> done;
|
||||||
for (const auto& occ: occlusion) {
|
for (const auto& occ: occlusion) {
|
||||||
const geometry::Ray ray(start, occ, dist);
|
geometry::Ray::iterator it(geometry::Ray(start, occ, dist));
|
||||||
std::vector<voxel_pos> points; //TODO: iterator
|
glm::llvec3 point;
|
||||||
ray.grid(points);
|
while (it.next(point)) {
|
||||||
for(auto& point: points) {
|
|
||||||
auto it = area.second.find(glm::lvec3(point) - area_offset);
|
auto it = area.second.find(glm::lvec3(point) - area_offset);
|
||||||
const auto buffer = solid ? it->second.first : it->second.second;
|
const auto buffer = solid ? it->second.first : it->second.second;
|
||||||
if(it != area.second.end() && buffer != NULL && done.insert(it->first).second) {
|
if(it != area.second.end() && buffer != NULL && done.insert(it->first).second) {
|
||||||
|
|
|
@ -61,9 +61,17 @@ int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length
|
||||||
case picoquic_callback_stream_fin: {
|
case picoquic_callback_stream_fin: {
|
||||||
assert(stream_ctx::IsServerId(stream_id));
|
assert(stream_ctx::IsServerId(stream_id));
|
||||||
auto stream_ctx = (in_stream_ctx *)v_stream_ctx;
|
auto stream_ctx = (in_stream_ctx *)v_stream_ctx;
|
||||||
|
const auto is_fin = fin_or_event == picoquic_callback_stream_fin;
|
||||||
|
|
||||||
/* Data arrival on stream #x, maybe with fin mark */
|
if (stream_ctx == NULL) {
|
||||||
if (stream_ctx == NULL) { // New stream from server
|
if (is_fin) { // Single frame packet
|
||||||
|
if (length > 0) {
|
||||||
|
onPacket(data::out_view(bytes, length), PacketFlags::TINY);
|
||||||
|
}
|
||||||
|
reset(stream_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// New long stream from server
|
||||||
stream_ctx = receive(stream_id);
|
stream_ctx = receive(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +79,7 @@ int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length
|
||||||
stream_ctx->buffer.write(bytes, length);
|
stream_ctx->buffer.write(bytes, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fin_or_event == picoquic_callback_stream_fin) {
|
if (is_fin) {
|
||||||
if (onPacket(data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) {
|
if (onPacket(data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) {
|
||||||
close(stream_ctx);
|
close(stream_ctx);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,6 +12,7 @@ enum queue: uint8_t {
|
||||||
count
|
count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Network client
|
||||||
class Client final: public Context, public Connection {
|
class Client final: public Context, public Connection {
|
||||||
public:
|
public:
|
||||||
Client(const address& ct,
|
Client(const address& ct,
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
class Window;
|
class Window;
|
||||||
class Camera;
|
class Camera;
|
||||||
|
/// Graphics pipeline
|
||||||
namespace render {
|
namespace render {
|
||||||
class Model;
|
class Model;
|
||||||
class LodModel;
|
class LodModel;
|
||||||
|
|
|
@ -323,7 +323,7 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons
|
||||||
const auto flags = ImGuiInputTextFlags_EnterReturnsTrue;
|
const auto flags = ImGuiInputTextFlags_EnterReturnsTrue;
|
||||||
// | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
|
// | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
|
||||||
ImGui::GetColumnWidth();
|
ImGui::GetColumnWidth();
|
||||||
if (ImGui::InputText("", state.console.buffer.data(), state.console.buffer.size(), flags /*, TODO: callback, context*/) && strlen(state.console.buffer.data()) > 0)
|
if (ImGui::InputText("", state.console.buffer.data(), state.console.buffer.size(), flags /*TODO: completion (, callback, context)*/) && strlen(state.console.buffer.data()) > 0)
|
||||||
actions |= Actions::Message;
|
actions |= Actions::Message;
|
||||||
|
|
||||||
// Auto-focus on window apparition
|
// Auto-focus on window apparition
|
||||||
|
|
|
@ -65,7 +65,7 @@ std::optional<Image::properties> Image::Read(const std::string& imagepath, std::
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
//FIXME: miplevels with size < block size (2 last) are corrupted
|
//WONT-FIX: miplevels with size < block size (2 last) are corrupted
|
||||||
const uint maxMipmapLevels = 1 + std::floor(std::log2(std::max(info.size.height, info.size.width))) - 2;
|
const uint maxMipmapLevels = 1 + std::floor(std::log2(std::max(info.size.height, info.size.width))) - 2;
|
||||||
info.mipmapLevels = std::min(maxMipmapLevels, info.mipmapLevels);
|
info.mipmapLevels = std::min(maxMipmapLevels, info.mipmapLevels);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "api/Images.hpp"
|
#include "api/Images.hpp"
|
||||||
|
|
||||||
struct windowOptions;
|
struct windowOptions;
|
||||||
|
/// OpenGL graphics pipeline
|
||||||
namespace render::gl {
|
namespace render::gl {
|
||||||
|
|
||||||
/// OpenGL rendering
|
/// OpenGL rendering
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace render {
|
||||||
namespace render::gl {
|
namespace render::gl {
|
||||||
class Renderer;
|
class Renderer;
|
||||||
}
|
}
|
||||||
/// OpenGL programs
|
|
||||||
namespace pass {
|
namespace pass {
|
||||||
/// OpenGL shaders pipeline
|
/// OpenGL shaders pipeline
|
||||||
class Program {
|
class Program {
|
||||||
|
|
|
@ -188,7 +188,7 @@ bool Renderer::Load(Window& window, const renderOptions& opt, const windowOption
|
||||||
appInfo.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
appInfo.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
||||||
appInfo.pEngineName = "No Engine";
|
appInfo.pEngineName = "No Engine";
|
||||||
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||||
appInfo.apiVersion = VK_API_VERSION_1_2;
|
appInfo.apiVersion = VK_MAKE_VERSION(1, 2, 0);
|
||||||
|
|
||||||
VkInstanceCreateInfo createInfo{};
|
VkInstanceCreateInfo createInfo{};
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "forward.hpp"
|
#include "forward.hpp"
|
||||||
|
|
||||||
struct windowOptions;
|
struct windowOptions;
|
||||||
|
/// Vulkan graphics pipeline
|
||||||
namespace render::vk {
|
namespace render::vk {
|
||||||
class Allocator;
|
class Allocator;
|
||||||
class SwapChain;
|
class SwapChain;
|
||||||
|
|
|
@ -40,7 +40,7 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) {
|
||||||
if (glm::length2(diff - it_c->first) > glm::pow2(keepDistance)) {
|
if (glm::length2(diff - it_c->first) > glm::pow2(keepDistance)) {
|
||||||
it_c = chunks.erase(it_c);
|
it_c = chunks.erase(it_c);
|
||||||
} else {
|
} else {
|
||||||
if(const auto neighbors = std::dynamic_pointer_cast<world::client::EdittableChunk>(it_c->second)->update(deltaTime, true /*FIXME: rnd from contouring*/)) {
|
if(const auto neighbors = std::dynamic_pointer_cast<world::client::EdittableChunk>(it_c->second)->update(deltaTime, true /*MAYBE: random update*/)) {
|
||||||
contouring->onUpdate(std::make_pair(area.first, it_c->first), diff, chunks, neighbors.value());
|
contouring->onUpdate(std::make_pair(area.first, it_c->first), diff, chunks, neighbors.value());
|
||||||
}
|
}
|
||||||
++it_c;
|
++it_c;
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include "../data/mem.hpp"
|
||||||
#include "../utils/logger.hpp"
|
#include "../utils/logger.hpp"
|
||||||
|
|
||||||
namespace data {
|
namespace data {
|
||||||
|
|
||||||
|
/// File loader
|
||||||
class file_content: public std::vector<char> {
|
class file_content: public std::vector<char> {
|
||||||
public:
|
public:
|
||||||
// Read first
|
/// Read first
|
||||||
file_content(const std::vector<std::string>& paths): std::vector<char>() {
|
file_content(const std::vector<std::string>& paths, data::out_view prefix = data::out_view(nullptr, 0)):
|
||||||
|
std::vector<char>(), prefix_size(prefix.size())
|
||||||
|
{
|
||||||
std::ifstream is = [&]() {
|
std::ifstream is = [&]() {
|
||||||
for(auto& path: paths) {
|
for(auto& path: paths) {
|
||||||
std::ifstream is(path, std::ios::in | std::ios::binary | std::ios::ate);
|
std::ifstream is(path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||||
|
@ -22,10 +25,18 @@ public:
|
||||||
}();
|
}();
|
||||||
const auto end = is.tellg();
|
const auto end = is.tellg();
|
||||||
is.seekg(0, std::ios::beg);
|
is.seekg(0, std::ios::beg);
|
||||||
resize(end - is.tellg());
|
resize(end - is.tellg() + prefix_size);
|
||||||
is.read(data(), size());
|
memcpy(data(), prefix.ptr, prefix_size);
|
||||||
|
is.read(data() + prefix_size, size());
|
||||||
is.close();
|
is.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t prefix_size;
|
||||||
|
|
||||||
|
/// Data after prefix
|
||||||
|
data::out_view content() const {
|
||||||
|
return data::out_view((const uint8_t*)data() + prefix_size, size() - prefix_size);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,7 +4,9 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
/// Generation indexing and containers (index, generation)
|
||||||
namespace data::generational {
|
namespace data::generational {
|
||||||
|
/// Generation element identifier
|
||||||
struct id {
|
struct id {
|
||||||
id(size_t index, size_t generation = 0): index(index), generation(generation) { }
|
id(size_t index, size_t generation = 0): index(index), generation(generation) { }
|
||||||
id(): id(0) { }
|
id(): id(0) { }
|
||||||
|
@ -14,6 +16,7 @@ namespace data::generational {
|
||||||
bool operator==(const id &i) const { return index == i.index && generation == i.generation; }
|
bool operator==(const id &i) const { return index == i.index && generation == i.generation; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Closed generational allocator
|
||||||
class allocator {
|
class allocator {
|
||||||
public:
|
public:
|
||||||
allocator() { }
|
allocator() { }
|
||||||
|
@ -68,6 +71,7 @@ namespace data::generational {
|
||||||
std::vector<size_t> freed;
|
std::vector<size_t> freed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Generational container accessible by id
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class vector {
|
class vector {
|
||||||
public:
|
public:
|
||||||
|
@ -229,6 +233,7 @@ namespace data::generational {
|
||||||
std::vector<size_t> freed;
|
std::vector<size_t> freed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Dummy generational container without allocation nor validation. A view to vector
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class view_vector: public std::vector<std::optional<T>> {
|
class view_vector: public std::vector<std::optional<T>> {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace data {
|
namespace data {
|
||||||
|
|
||||||
|
/// Map vector to istream
|
||||||
struct vec_istream: std::streambuf {
|
struct vec_istream: std::streambuf {
|
||||||
vec_istream(std::vector<char> &vec) {
|
vec_istream(std::vector<char> &vec) {
|
||||||
this->setg(&vec[0], &vec[0], &vec[0] + vec.size());
|
this->setg(&vec[0], &vec[0], &vec[0] + vec.size());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Abstract data with moving cursor
|
||||||
struct view {
|
struct view {
|
||||||
view(size_t size): siz(size) { }
|
view(size_t size): siz(size) { }
|
||||||
|
|
||||||
|
@ -21,6 +24,7 @@ struct view {
|
||||||
constexpr size_t size() const { return siz; }
|
constexpr size_t size() const { return siz; }
|
||||||
constexpr bool isDone() const { return cur >= siz; }
|
constexpr bool isDone() const { return cur >= siz; }
|
||||||
};
|
};
|
||||||
|
/// Vector with in_view interface
|
||||||
struct in_vector {
|
struct in_vector {
|
||||||
/// 512 Mébibits
|
/// 512 Mébibits
|
||||||
static constexpr size_t MAX_SIZE = 1ul << 26;
|
static constexpr size_t MAX_SIZE = 1ul << 26;
|
||||||
|
@ -40,6 +44,7 @@ struct in_vector {
|
||||||
memcpy(writeTo(len), data, len);
|
memcpy(writeTo(len), data, len);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Abstract data writer
|
||||||
struct in_view: view {
|
struct in_view: view {
|
||||||
in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) {
|
in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) {
|
||||||
assert(ptr != nullptr);
|
assert(ptr != nullptr);
|
||||||
|
@ -56,6 +61,7 @@ struct in_view: view {
|
||||||
memcpy(writeTo(len), data, len);
|
memcpy(writeTo(len), data, len);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/// Abstract data reader
|
||||||
struct out_view: view {
|
struct out_view: view {
|
||||||
out_view(): out_view(nullptr, 0) { }
|
out_view(): out_view(nullptr, 0) { }
|
||||||
out_view(const uint8_t *ptr, size_t size): view(size), ptr(ptr) { }
|
out_view(const uint8_t *ptr, size_t size): view(size), ptr(ptr) { }
|
||||||
|
@ -74,7 +80,9 @@ struct out_view: view {
|
||||||
constexpr const uint8_t* data() const { return ptr; }
|
constexpr const uint8_t* data() const { return ptr; }
|
||||||
constexpr size_t remaining() const { return size() - cur; }
|
constexpr size_t remaining() const { return size() - cur; }
|
||||||
};
|
};
|
||||||
|
/// Pointer to opaque owned memory
|
||||||
using handle_t = std::shared_ptr<const void>;
|
using handle_t = std::shared_ptr<const void>;
|
||||||
|
/// out_view with owned data
|
||||||
struct out_buffer: out_view {
|
struct out_buffer: out_view {
|
||||||
out_buffer(): out_view() { }
|
out_buffer(): out_view() { }
|
||||||
out_buffer(const out_view& view, const handle_t& handle):
|
out_buffer(const out_view& view, const handle_t& handle):
|
||||||
|
|
|
@ -83,6 +83,55 @@ namespace geometry {
|
||||||
}
|
}
|
||||||
points.push_back(current);
|
points.push_back(current);
|
||||||
}
|
}
|
||||||
|
/// Iterator implementation of grid
|
||||||
|
struct iterator {
|
||||||
|
enum class axis { X, Y, Z };
|
||||||
|
|
||||||
|
iterator(const Ray& r): current(r.from.as_voxel()),
|
||||||
|
d(r.dir * r.dist), l(glm::abs(d)),
|
||||||
|
dir((l.x >= l.y) && (l.x >= l.z) ? axis::X : ((l.y >= l.x) && (l.y >= l.z) ? axis::Y : axis::Z)),
|
||||||
|
count(dir == axis::X ? l.x : (dir == axis::Y ? l.y : l.z)),
|
||||||
|
inc(swap(glm::llvec3((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1), dir)),
|
||||||
|
delta(swap(l << 1ll, dir)), err_1(delta.y - count), err_2(delta.z - count) { }
|
||||||
|
|
||||||
|
static constexpr inline glm::llvec3 swap(const glm::llvec3& in, const axis dir) { return dir == axis::X ? in :
|
||||||
|
(dir == axis::Y ? glm::llvec3(in.y, in.z, in.x) : glm::llvec3(in.z, in.x, in.y)); }
|
||||||
|
|
||||||
|
static constexpr inline long long& get(glm::llvec3& in, const axis dir, const axis target) {
|
||||||
|
const axis v = (axis)(((int)dir + (int)target) % 3);
|
||||||
|
return v == axis::X ? in.x : (v == axis::Y ? in.y : in.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool next(glm::llvec3& out) {
|
||||||
|
if (count < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count--;
|
||||||
|
out = current;
|
||||||
|
if (err_1 > 0) {
|
||||||
|
get(current, dir, axis::Y) += inc.y;
|
||||||
|
err_1 -= delta.x;
|
||||||
|
}
|
||||||
|
if (err_2 > 0) {
|
||||||
|
get(current, dir, axis::Z) += inc.z;
|
||||||
|
err_2 -= delta.x;
|
||||||
|
}
|
||||||
|
err_1 += delta.y;
|
||||||
|
err_2 += delta.z;
|
||||||
|
get(current, dir, axis::X) += inc.x;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::llvec3 current;
|
||||||
|
const glm::llvec3 d;
|
||||||
|
const glm::llvec3 l;
|
||||||
|
const axis dir;
|
||||||
|
glm::llvec3::value_type count;
|
||||||
|
const glm::llvec3 inc;
|
||||||
|
const glm::llvec3 delta;
|
||||||
|
int err_1;
|
||||||
|
int err_2;
|
||||||
|
};
|
||||||
|
|
||||||
IBox::ContainmentType intersect(const IBox& box) const {
|
IBox::ContainmentType intersect(const IBox& box) const {
|
||||||
const glm::llvec3 start = from.as_voxel();
|
const glm::llvec3 start = from.as_voxel();
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
|
/// Abstract stream
|
||||||
struct stream_ctx {
|
struct stream_ctx {
|
||||||
stream_ctx(uint64_t id): stream_id(id) { }
|
stream_ctx(uint64_t id): stream_id(id) { }
|
||||||
uint64_t stream_id;
|
uint64_t stream_id;
|
||||||
|
@ -17,6 +18,7 @@ struct stream_ctx {
|
||||||
|
|
||||||
friend bool operator==(const stream_ctx &a, const stream_ctx &b) { return a.stream_id == b.stream_id; }
|
friend bool operator==(const stream_ctx &a, const stream_ctx &b) { return a.stream_id == b.stream_id; }
|
||||||
};
|
};
|
||||||
|
/// Outgoing stream
|
||||||
struct out_stream_ctx: stream_ctx {
|
struct out_stream_ctx: stream_ctx {
|
||||||
out_stream_ctx(uint64_t id, uint8_t queue_id, const data::out_buffer& buf):
|
out_stream_ctx(uint64_t id, uint8_t queue_id, const data::out_buffer& buf):
|
||||||
stream_ctx(id), buffer(buf), queue_id(queue_id) { }
|
stream_ctx(id), buffer(buf), queue_id(queue_id) { }
|
||||||
|
@ -24,13 +26,16 @@ struct out_stream_ctx: stream_ctx {
|
||||||
data::out_buffer buffer;
|
data::out_buffer buffer;
|
||||||
uint8_t queue_id;
|
uint8_t queue_id;
|
||||||
};
|
};
|
||||||
|
/// Incoming stream
|
||||||
struct in_stream_ctx: stream_ctx {
|
struct in_stream_ctx: stream_ctx {
|
||||||
in_stream_ctx(uint64_t id): stream_ctx(id) { }
|
in_stream_ctx(uint64_t id): stream_ctx(id) { }
|
||||||
data::in_vector buffer;
|
data::in_vector buffer;
|
||||||
};
|
};
|
||||||
|
/// Informations about packets
|
||||||
enum class PacketFlags {
|
enum class PacketFlags {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
DATAGRAM = 1 << 0,
|
DATAGRAM = 1 << 0,
|
||||||
|
TINY = 1 << 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Abstract QUIC context
|
/// Abstract QUIC context
|
||||||
|
@ -155,7 +160,6 @@ private:
|
||||||
std::forward_list<in_stream_ctx> incoming;
|
std::forward_list<in_stream_ctx> incoming;
|
||||||
|
|
||||||
// MAYBE: add bidirectional streams
|
// MAYBE: add bidirectional streams
|
||||||
// TODO: premanant streams: background, priority
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,11 +4,13 @@
|
||||||
#include "../utils/logger.hpp"
|
#include "../utils/logger.hpp"
|
||||||
#include <picoquic/picosocks.h>
|
#include <picoquic/picosocks.h>
|
||||||
|
|
||||||
|
/// Network protocol
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
/// Application Protocol Negotiation
|
/// Application Protocol Negotiation
|
||||||
constexpr auto ALPN = "univerxel_quic";
|
constexpr auto ALPN = "univerxel_quic";
|
||||||
|
|
||||||
|
/// Reasons for client server connection to break
|
||||||
enum class disconnect_reason: uint16_t {
|
enum class disconnect_reason: uint16_t {
|
||||||
UNEXPECTED = 0,
|
UNEXPECTED = 0,
|
||||||
// Client quit
|
// Client quit
|
||||||
|
@ -20,6 +22,7 @@ enum class disconnect_reason: uint16_t {
|
||||||
PROTOCOL_VIOLATION = 15,
|
PROTOCOL_VIOLATION = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Packets from server to client
|
||||||
enum class server_packet_type: uint8_t {
|
enum class server_packet_type: uint8_t {
|
||||||
/// MAYBE: HELLO = 0,
|
/// MAYBE: HELLO = 0,
|
||||||
|
|
||||||
|
@ -50,7 +53,6 @@ enum class server_packet_type: uint8_t {
|
||||||
|
|
||||||
/// Update entities instances position and velocity
|
/// Update entities instances position and velocity
|
||||||
/// {size_t(entity), size_t(count), {size_t(index), ifvec3(pos), vec3(velocity)}[]}[]
|
/// {size_t(entity), size_t(count), {size_t(index), ifvec3(pos), vec3(velocity)}[]}[]
|
||||||
/// FIXME: fromat to fit PICOQUIC_MAX_PACKET_SIZE
|
|
||||||
ENTITIES = 21,
|
ENTITIES = 21,
|
||||||
|
|
||||||
/// World compression dictionary
|
/// World compression dictionary
|
||||||
|
@ -64,6 +66,7 @@ enum class server_packet_type: uint8_t {
|
||||||
/// char[] (not null terminated)
|
/// char[] (not null terminated)
|
||||||
MESSAGE = 29,
|
MESSAGE = 29,
|
||||||
};
|
};
|
||||||
|
/// Packets from client to server
|
||||||
enum class client_packet_type: uint8_t {
|
enum class client_packet_type: uint8_t {
|
||||||
/// Interact with voxels
|
/// Interact with voxels
|
||||||
/// actions::FillShape
|
/// actions::FillShape
|
||||||
|
@ -84,6 +87,7 @@ enum class client_packet_type: uint8_t {
|
||||||
|
|
||||||
constexpr auto MAX_PENDING_CHUNK_COUNT = 256;
|
constexpr auto MAX_PENDING_CHUNK_COUNT = 256;
|
||||||
|
|
||||||
|
/// Server address as name(ip or dns name) and port
|
||||||
struct address {
|
struct address {
|
||||||
std::string host;
|
std::string host;
|
||||||
int port;
|
int port;
|
||||||
|
@ -103,9 +107,12 @@ struct address {
|
||||||
return os << ct.host << ':' << ct.port;
|
return os << ct.host << ':' << ct.port;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/// Server properties
|
||||||
struct exposure: address {
|
struct exposure: address {
|
||||||
uint32_t max_connections;
|
uint32_t max_connections;
|
||||||
|
/// TLS private key
|
||||||
std::string key;
|
std::string key;
|
||||||
|
/// TLS public key
|
||||||
std::string cert;
|
std::string cert;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
//TODO: preallocate static data pool
|
//TODO: preallocate static data pool
|
||||||
|
|
||||||
|
/// Helper to write allocated packets
|
||||||
class PacketWriter final {
|
class PacketWriter final {
|
||||||
public:
|
public:
|
||||||
PacketWriter(size_t size): buffer((uint8_t*)malloc(size), size) { }
|
PacketWriter(size_t size): buffer((uint8_t*)malloc(size), size) { }
|
||||||
|
@ -27,6 +29,37 @@ public:
|
||||||
write(&d, sizeof(d));
|
write(&d, sizeof(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct varying_part {
|
||||||
|
varying_part(data::in_view &buffer): buffer(buffer), visible_size(0) { }
|
||||||
|
~varying_part() {
|
||||||
|
buffer.siz = buffer.cur + visible_size;
|
||||||
|
buffer.cur = buffer.siz;
|
||||||
|
}
|
||||||
|
|
||||||
|
data::in_view &buffer;
|
||||||
|
size_t visible_size;
|
||||||
|
|
||||||
|
constexpr size_t size() const { return visible_size; }
|
||||||
|
void* data() { return buffer.writeTo(0); }
|
||||||
|
void reserve(size_t target) {
|
||||||
|
if (target >= buffer.siz - buffer.cur) {
|
||||||
|
auto old = buffer.ptr;
|
||||||
|
buffer.ptr = (uint8_t*)malloc(target);
|
||||||
|
memcpy(buffer.ptr, old, buffer.siz);
|
||||||
|
free(old);
|
||||||
|
buffer.siz = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void resize(size_t target) {
|
||||||
|
reserve(target);
|
||||||
|
visible_size = target;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// Only from resize, write, resize down
|
||||||
|
varying_part varying() {
|
||||||
|
return varying_part(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
bool isFull() const {
|
bool isFull() const {
|
||||||
return buffer.isDone();
|
return buffer.isDone();
|
||||||
}
|
}
|
||||||
|
@ -67,6 +100,7 @@ private:
|
||||||
data::in_view buffer;
|
data::in_view buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Helper to read out_view
|
||||||
class PacketReader final {
|
class PacketReader final {
|
||||||
public:
|
public:
|
||||||
PacketReader(const data::out_view& buf): buffer(buf) { }
|
PacketReader(const data::out_view& buf): buffer(buf) { }
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "world/actions.hpp"
|
#include "world/actions.hpp"
|
||||||
#include "geometry/Faces.hpp"
|
#include "geometry/Faces.hpp"
|
||||||
|
|
||||||
|
/// Link between world::server::SharedUniverse and world::client::LocalUniverse
|
||||||
struct server_handle {
|
struct server_handle {
|
||||||
bool running = false;
|
bool running = false;
|
||||||
const world::client::area_map *areas;
|
const world::client::area_map *areas;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
#include <vector>
|
#include "../data/mem.hpp"
|
||||||
#include <cassert>
|
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
|
|
||||||
namespace zstd {
|
namespace zstd {
|
||||||
|
@ -73,6 +72,7 @@ namespace zstd {
|
||||||
class dict_set {
|
class dict_set {
|
||||||
public:
|
public:
|
||||||
dict_set(const std::vector<char>& data) { load(data.data(), data.size()); }
|
dict_set(const std::vector<char>& data) { load(data.data(), data.size()); }
|
||||||
|
dict_set(const data::out_view& data) { load(data.data(), data.size()); }
|
||||||
~dict_set() {
|
~dict_set() {
|
||||||
ZSTD_freeCDict(c);
|
ZSTD_freeCDict(c);
|
||||||
ZSTD_freeDDict(d);
|
ZSTD_freeDDict(d);
|
||||||
|
|
|
@ -7,20 +7,19 @@ namespace world {
|
||||||
|
|
||||||
template<class Areas>
|
template<class Areas>
|
||||||
Universe::ray_result Raycast(const geometry::Ray& ray, const Areas& areas) {
|
Universe::ray_result Raycast(const geometry::Ray& ray, const Areas& areas) {
|
||||||
//TODO: iterator
|
|
||||||
//MAYBE: ray + offset to get float precision
|
//MAYBE: ray + offset to get float precision
|
||||||
std::vector<voxel_pos> points;
|
|
||||||
ray.grid(points);
|
|
||||||
Universe::ray_result target;
|
Universe::ray_result target;
|
||||||
size_t dist = points.size();
|
size_t dist = UINT32_MAX - 1;
|
||||||
for(auto& area: areas) {
|
for(auto& area: areas) {
|
||||||
if(ray.intersect(area.second->getBounding()) != geometry::IBox::ContainmentType::Disjoint) {
|
if(ray.intersect(area.second->getBounding()) != geometry::IBox::ContainmentType::Disjoint) {
|
||||||
const auto &offset = area.second->getOffset().as_voxel();
|
const auto &offset = area.second->getOffset().as_voxel();
|
||||||
const auto &chunks = area.second->getChunks();
|
const auto &chunks = area.second->getChunks();
|
||||||
std::shared_ptr<world::Chunk> chunk = nullptr;
|
std::shared_ptr<world::Chunk> chunk = nullptr;
|
||||||
chunk_pos chunk_vec(INT_MAX);
|
chunk_pos chunk_vec(INT_MAX);
|
||||||
for (size_t i = 0; i < dist; i++) {
|
geometry::Ray::iterator it(ray);
|
||||||
const auto pos = points[i] - offset;
|
glm::llvec3 point;
|
||||||
|
for (size_t i = 0; i < dist && it.next(point); i++) {
|
||||||
|
const auto pos = point - offset;
|
||||||
const chunk_pos cPos = glm::divide(pos);
|
const chunk_pos cPos = glm::divide(pos);
|
||||||
if(cPos != chunk_vec) {
|
if(cPos != chunk_vec) {
|
||||||
if (const auto it = chunks.find(cPos); it != chunks.end()) {
|
if (const auto it = chunks.find(cPos); it != chunks.end()) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
namespace config::server {
|
namespace config::server {
|
||||||
|
|
||||||
|
/// Server side configuration
|
||||||
struct options {
|
struct options {
|
||||||
public:
|
public:
|
||||||
options(toml::node_view<toml::node> config): world() {
|
options(toml::node_view<toml::node> config): world() {
|
||||||
|
|
|
@ -7,17 +7,16 @@ namespace net::server {
|
||||||
uint64_t stream_id, uint8_t* bytes, size_t length,
|
uint64_t stream_id, uint8_t* bytes, size_t length,
|
||||||
picoquic_call_back_event_t fin_or_event, void* callback_ctx, void* v_stream_ctx) {
|
picoquic_call_back_event_t fin_or_event, void* callback_ctx, void* v_stream_ctx) {
|
||||||
|
|
||||||
if (fin_or_event == picoquic_callback_almost_ready)
|
|
||||||
return 0; //NOTE: only use full connections
|
|
||||||
|
|
||||||
auto server = (Server*)picoquic_get_default_callback_context(picoquic_get_quic_ctx(cnx));
|
auto server = (Server*)picoquic_get_default_callback_context(picoquic_get_quic_ctx(cnx));
|
||||||
auto peer = (Peer*)callback_ctx;
|
auto peer = (Peer*)callback_ctx;
|
||||||
if (peer == nullptr || peer == (void*)server) {
|
if (fin_or_event != picoquic_callback_almost_ready) {
|
||||||
assert(fin_or_event == picoquic_callback_ready);
|
if (peer == nullptr || peer == (void*)server) {
|
||||||
peer = server->connect(cnx);
|
assert(fin_or_event == picoquic_callback_ready);
|
||||||
picoquic_set_callback(cnx, connection_callback, peer);
|
peer = server->connect(cnx);
|
||||||
|
picoquic_set_callback(cnx, connection_callback, peer);
|
||||||
|
}
|
||||||
|
assert(peer == nullptr || peer->contains(cnx) || fin_or_event == picoquic_callback_close);
|
||||||
}
|
}
|
||||||
assert(peer == nullptr || peer->contains(cnx) || fin_or_event == picoquic_callback_close);
|
|
||||||
assert(v_stream_ctx == nullptr || ((net::stream_ctx*)v_stream_ctx)->stream_id == stream_id);
|
assert(v_stream_ctx == nullptr || ((net::stream_ctx*)v_stream_ctx)->stream_id == stream_id);
|
||||||
|
|
||||||
return server->connectionCallback(peer, stream_id, bytes, length, fin_or_event, v_stream_ctx);
|
return server->connectionCallback(peer, stream_id, bytes, length, fin_or_event, v_stream_ctx);
|
||||||
|
@ -59,9 +58,17 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s
|
||||||
case picoquic_callback_stream_fin: {
|
case picoquic_callback_stream_fin: {
|
||||||
assert(stream_ctx::IsClientId(stream_id));
|
assert(stream_ctx::IsClientId(stream_id));
|
||||||
auto stream_ctx = (in_stream_ctx *)v_stream_ctx;
|
auto stream_ctx = (in_stream_ctx *)v_stream_ctx;
|
||||||
|
const auto is_fin = fin_or_event == picoquic_callback_stream_fin;
|
||||||
|
|
||||||
/* Data arrival on stream #x, maybe with fin mark */
|
if (stream_ctx == NULL) {
|
||||||
if (stream_ctx == NULL) { // New stream from peer
|
if (is_fin) { // Single frame packet
|
||||||
|
if (length > 0) {
|
||||||
|
onPacket(peer, data::out_view(bytes, length), PacketFlags::TINY);
|
||||||
|
}
|
||||||
|
peer->reset(stream_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// New long stream from server
|
||||||
stream_ctx = peer->receive(stream_id);
|
stream_ctx = peer->receive(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +76,7 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s
|
||||||
stream_ctx->buffer.write(bytes, length);
|
stream_ctx->buffer.write(bytes, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fin_or_event == picoquic_callback_stream_fin) {
|
if (is_fin) {
|
||||||
if (onPacket(peer, data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) {
|
if (onPacket(peer, data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) {
|
||||||
peer->close(stream_ctx);
|
peer->close(stream_ctx);
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,8 +170,12 @@ int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, s
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case picoquic_callback_almost_ready:
|
case picoquic_callback_almost_ready:
|
||||||
// NOTE: handled is static callback
|
assert(peer == nullptr);
|
||||||
// MAYBE: use pre-connect for server status
|
// MAYBE: use pre-connect for server status
|
||||||
|
if (Connection::GetSize(peers) >= max_connections) {
|
||||||
|
LOG_W("Server is full");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case picoquic_callback_ready:
|
case picoquic_callback_ready:
|
||||||
/* TODO: Check that the transport parameters are what the sample expects */
|
/* TODO: Check that the transport parameters are what the sample expects */
|
||||||
|
|
|
@ -50,7 +50,6 @@ public:
|
||||||
picoquic_call_back_event_t fin_or_event, void *v_stream_ctx);
|
picoquic_call_back_event_t fin_or_event, void *v_stream_ctx);
|
||||||
|
|
||||||
bool isRunning() const { return true; }
|
bool isRunning() const { return true; }
|
||||||
constexpr bool isFull(uint32_t val) const { return val >= max_connections; }
|
|
||||||
|
|
||||||
template<typename C>
|
template<typename C>
|
||||||
void iterPeers(C call) {
|
void iterPeers(C call) {
|
||||||
|
|
|
@ -14,12 +14,14 @@
|
||||||
using namespace world::server;
|
using namespace world::server;
|
||||||
|
|
||||||
const auto AREAS_FILE = "/areas.idx";
|
const auto AREAS_FILE = "/areas.idx";
|
||||||
|
const auto COMPRESSION_PREFIX = (uint8_t)net::server_packet_type::COMPRESSION;
|
||||||
|
|
||||||
Universe::Universe(const Universe::options &options): host(options.connection,
|
Universe::Universe(const Universe::options &options): host(options.connection,
|
||||||
[&](net::server::Peer* peer) { return onConnect(peer); },
|
[&](net::server::Peer* peer) { return onConnect(peer); },
|
||||||
[&](net::server::Peer* peer, bool is_app, uint16_t reason) { return onDisconnect(peer, is_app, reason); },
|
[&](net::server::Peer* peer, bool is_app, uint16_t reason) { return onDisconnect(peer, is_app, reason); },
|
||||||
[&](net::server::Peer* peer, const data::out_view &buf, net::PacketFlags flags) { return onPacket(peer, buf, flags); }
|
[&](net::server::Peer* peer, const data::out_view &buf, net::PacketFlags flags) { return onPacket(peer, buf, flags); }
|
||||||
), dict_content({options.folderPath + "/zstd.dict", "content/zstd.dict"}), dicts(dict_content), dict_write_ctx(dicts.make_writer())
|
), dict_content({options.folderPath + "/zstd.dict", "content/zstd.dict"}, data::out_view(&COMPRESSION_PREFIX, sizeof(COMPRESSION_PREFIX))),
|
||||||
|
dicts(dict_content.content()), dict_write_ctx(dicts.make_writer())
|
||||||
{
|
{
|
||||||
setOptions(options);
|
setOptions(options);
|
||||||
folderPath = options.folderPath;
|
folderPath = options.folderPath;
|
||||||
|
@ -449,17 +451,11 @@ std::optional<uint16_t> Universe::onConnect(net::server::Peer* peer) {
|
||||||
LOG_I("Client connect from " << peer->getAddress());
|
LOG_I("Client connect from " << peer->getAddress());
|
||||||
net_client* client = new net_client(entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)}));
|
net_client* client = new net_client(entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)}));
|
||||||
peer->ctx = client;
|
peer->ctx = client;
|
||||||
if (host.isFull(client->instanceId.index)) {
|
|
||||||
LOG_W("Server is full");
|
|
||||||
return (uint16_t)net::disconnect_reason::FULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
peer->send(net::PacketWriter::Of(net::server_packet_type::CAPABILITIES, loadDistance));
|
peer->send(net::PacketWriter::Of(net::server_packet_type::CAPABILITIES, loadDistance));
|
||||||
|
|
||||||
//TODO: lock while not received
|
//TODO: lock while not received
|
||||||
//MAYBE: add net::server_packet_type::COMPRESSION to dict_content: zero copy
|
peer->send(data::out_buffer(data::out_view((uint8_t*)dict_content.data(), dict_content.size()), nullptr), net::server::queue::CHUNK);
|
||||||
peer->send(net::PacketWriter::Of(net::server_packet_type::COMPRESSION, dict_content.data(), dict_content.size()), net::server::queue::CHUNK);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
auto player = findEntity(PLAYER_ENTITY_ID, client->instanceId);
|
auto player = findEntity(PLAYER_ENTITY_ID, client->instanceId);
|
||||||
auto packet = net::PacketWriter(net::server_packet_type::TELEPORT, sizeof(size_t) + sizeof(voxel_pos));
|
auto packet = net::PacketWriter(net::server_packet_type::TELEPORT, sizeof(size_t) + sizeof(voxel_pos));
|
||||||
|
@ -586,12 +582,12 @@ data::out_buffer Universe::serializeChunk(area_<chunk_pos> id, const std::shared
|
||||||
ZoneScopedN("Chunk");
|
ZoneScopedN("Chunk");
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
data->write(out);
|
data->write(out);
|
||||||
std::vector<char> buffer;
|
auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id));
|
||||||
//FIXME: avoid buffer copy
|
|
||||||
dict_write_ctx.compress(out.str(), buffer);
|
|
||||||
auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id) + buffer.size());
|
|
||||||
packet.write(id);
|
packet.write(id);
|
||||||
packet.write(buffer.data(), buffer.size());
|
{
|
||||||
|
auto vec = packet.varying();
|
||||||
|
dict_write_ctx.compress(out.str(), vec);
|
||||||
|
}
|
||||||
return packet.finish();
|
return packet.finish();
|
||||||
}
|
}
|
||||||
void Universe::broadcastMessage(const std::string& text) {
|
void Universe::broadcastMessage(const std::string& text) {
|
||||||
|
|
Loading…
Reference in New Issue