Basic gravity using pseudo sphere collider
This commit is contained in:
parent
96f4bbf11c
commit
7f8e249a18
|
@ -68,6 +68,8 @@ public:
|
||||||
world.trustMajorant = config["world"]["trust_majorant"].value_or(world.trustMajorant);
|
world.trustMajorant = config["world"]["trust_majorant"].value_or(world.trustMajorant);
|
||||||
world.editPrediction = config["world"]["edit_prediction"].value_or(world.editPrediction);
|
world.editPrediction = config["world"]["edit_prediction"].value_or(world.editPrediction);
|
||||||
world.editHandling = config["world"]["edit_handling"].value_or(world.editHandling);
|
world.editHandling = config["world"]["edit_handling"].value_or(world.editHandling);
|
||||||
|
world.movePrediction = config["world"]["move_prediction"].value_or(world.movePrediction);
|
||||||
|
world.moveCollision = config["world"]["move_collision"].value_or(world.moveCollision);
|
||||||
voxel_density = config["world"]["voxel_density"].value_or(voxel_density);
|
voxel_density = config["world"]["voxel_density"].value_or(voxel_density);
|
||||||
|
|
||||||
#ifndef STANDALONE
|
#ifndef STANDALONE
|
||||||
|
@ -148,6 +150,8 @@ public:
|
||||||
{"trust_majorant", world.trustMajorant},
|
{"trust_majorant", world.trustMajorant},
|
||||||
{"edit_prediction", world.editPrediction},
|
{"edit_prediction", world.editPrediction},
|
||||||
{"edit_handling", world.editHandling},
|
{"edit_handling", world.editHandling},
|
||||||
|
{"move_prediction", world.movePrediction},
|
||||||
|
{"move_collision", world.moveCollision},
|
||||||
{"voxel_density", voxel_density}
|
{"voxel_density", voxel_density}
|
||||||
}));
|
}));
|
||||||
if(connection.has_value()) {
|
if(connection.has_value()) {
|
||||||
|
|
|
@ -128,6 +128,19 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (options.movePrediction) {
|
||||||
|
entities.remove([&](size_t eId, Entity &entity) {
|
||||||
|
entity.instances.remove([&](size_t iId, Universe::Entity::Instance &inst) {
|
||||||
|
if (eId == PLAYER_ENTITY_ID.index && iId == playerId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MAYBE: leap after entities packet
|
||||||
|
inst.pos += inst.velocity * deltaTime;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
contouring->update(pos, areas);
|
contouring->update(pos, areas);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +185,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case server_packet_type::COMPRESSION: {
|
case server_packet_type::COMPRESSION: {
|
||||||
const auto remain = packet.readAll();
|
const auto remain = packet.readRemaining();
|
||||||
dict.emplace(remain.data(), remain.size());
|
dict.emplace(remain.data(), remain.size());
|
||||||
LOG_T("Compression dictionnary loaded");
|
LOG_T("Compression dictionnary loaded");
|
||||||
mayQueryChunks = true;
|
mayQueryChunks = true;
|
||||||
|
@ -187,13 +200,13 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case server_packet_type::MESSAGE: {
|
case server_packet_type::MESSAGE: {
|
||||||
const auto remain = packet.readAll();
|
const auto remain = packet.readRemaining();
|
||||||
onMessage(std::string((const char*)remain.data(), remain.size()));
|
onMessage(std::string((const char*)remain.data(), remain.size()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case server_packet_type::AREAS: {
|
case server_packet_type::AREAS: {
|
||||||
while(!packet.isFull()) {
|
while(!packet.isDone()) {
|
||||||
area_id id;
|
area_id id;
|
||||||
world::Area::params p;
|
world::Area::params p;
|
||||||
if(!packet.read(id) || !packet.read(p))
|
if(!packet.read(id) || !packet.read(p))
|
||||||
|
@ -228,7 +241,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
// MAYBE: then create virtual or non chunk of single material
|
// MAYBE: then create virtual or non chunk of single material
|
||||||
uint16_t full = 0;
|
uint16_t full = 0;
|
||||||
uint16_t total = 0;
|
uint16_t total = 0;
|
||||||
while (!packet.isFull()) {
|
while (!packet.isDone()) {
|
||||||
region_chunk_pos cpos;
|
region_chunk_pos cpos;
|
||||||
Voxel voxel;
|
Voxel voxel;
|
||||||
if (!packet.read(cpos) || !packet.read(voxel))
|
if (!packet.read(cpos) || !packet.read(voxel))
|
||||||
|
@ -247,7 +260,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
if (!dict.has_value())
|
if (!dict.has_value())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (packet.isFull()) {
|
if (packet.isDone()) {
|
||||||
mayQueryChunks = true;
|
mayQueryChunks = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +280,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> buffer;
|
std::vector<char> buffer;
|
||||||
if(auto err = dict.value().decompress(packet.readAll(), buffer)) {
|
if(auto err = dict.value().decompress(packet.readRemaining(), buffer)) {
|
||||||
LOG_E("Corrupted chunk packet " << err.value());
|
LOG_E("Corrupted chunk packet " << err.value());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -313,14 +326,14 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!packet.isFull()) {
|
while(!packet.isDone()) {
|
||||||
chunk_pos pos = chunk_pos(INT_MAX);
|
chunk_pos pos = chunk_pos(INT_MAX);
|
||||||
packet.read(pos);
|
packet.read(pos);
|
||||||
chunk_voxel_idx count = 0;
|
chunk_voxel_idx count = 0;
|
||||||
packet.read(count);
|
packet.read(count);
|
||||||
if (auto ptr = it->second->setChunks().findInRange(pos)) {
|
if (auto ptr = it->second->setChunks().findInRange(pos)) {
|
||||||
auto chunk = std::dynamic_pointer_cast<Chunk>(ptr.value());
|
auto chunk = std::dynamic_pointer_cast<Chunk>(ptr.value());
|
||||||
for (auto i = 0; i < count && !packet.isFull(); i++) {
|
for (auto i = 0; i < count && !packet.isDone(); i++) {
|
||||||
Chunk::Edit edit;
|
Chunk::Edit edit;
|
||||||
packet.read(edit);
|
packet.read(edit);
|
||||||
chunk->apply(edit);
|
chunk->apply(edit);
|
||||||
|
@ -334,7 +347,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
|
|
||||||
case server_packet_type::ENTITY_TYPES: {
|
case server_packet_type::ENTITY_TYPES: {
|
||||||
entities.clear();
|
entities.clear();
|
||||||
while(!packet.isFull()) {
|
while(!packet.isDone()) {
|
||||||
size_t index;
|
size_t index;
|
||||||
glm::usvec3 size;
|
glm::usvec3 size;
|
||||||
glm::vec3 scale;
|
glm::vec3 scale;
|
||||||
|
@ -351,7 +364,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
|
|
||||||
case server_packet_type::ENTITIES: {
|
case server_packet_type::ENTITIES: {
|
||||||
entities.remove([](size_t, Entity &entity) { entity.instances.clear(); return false; });
|
entities.remove([](size_t, Entity &entity) { entity.instances.clear(); return false; });
|
||||||
while(!packet.isFull()) {
|
while(!packet.isDone()) {
|
||||||
size_t entity_idx;
|
size_t entity_idx;
|
||||||
size_t count;
|
size_t count;
|
||||||
if (!(packet.read(entity_idx) && packet.read(count)))
|
if (!(packet.read(entity_idx) && packet.read(count)))
|
||||||
|
@ -360,7 +373,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
if (auto entity = entities.get(entity_idx)) {
|
if (auto entity = entities.get(entity_idx)) {
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
continue;
|
continue;
|
||||||
for (size_t i = 0; i < count && !packet.isFull(); i++) {
|
for (size_t i = 0; i < count && !packet.isDone(); i++) {
|
||||||
size_t idx;
|
size_t idx;
|
||||||
glm::ifvec3 pos;
|
glm::ifvec3 pos;
|
||||||
glm::vec3 vel;
|
glm::vec3 vel;
|
||||||
|
@ -410,15 +423,9 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
auto area = std::get_if<Universe::Entity::area_t>(&it->shape);
|
auto area = std::get_if<Universe::Entity::area_t>(&it->shape);
|
||||||
assert(area);
|
assert(area);
|
||||||
area->clear();
|
area->clear();
|
||||||
glm::i64 i = 0;
|
while (!packet.isDone()) {
|
||||||
while (!packet.isFull()) {
|
|
||||||
i++;
|
|
||||||
size_t size = 0;
|
|
||||||
if (!packet.read(size))
|
|
||||||
break;
|
|
||||||
|
|
||||||
std::vector<char> buffer;
|
std::vector<char> buffer;
|
||||||
if(auto err = dict.value().decompress(packet.readPart(size), buffer)) {
|
if(auto err = dict.value().decompress(packet.readSizedPart(), buffer)) {
|
||||||
LOG_E("Corrupted entity chunk packet " << err.value());
|
LOG_E("Corrupted entity chunk packet " << err.value());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -427,15 +434,15 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
|
||||||
area->push_back(std::make_shared<Chunk>(iss));
|
area->push_back(std::make_shared<Chunk>(iss));
|
||||||
}
|
}
|
||||||
auto scale = glm::lvec3(1) + glm::divide(it->size);
|
auto scale = glm::lvec3(1) + glm::divide(it->size);
|
||||||
if (i != scale.x * scale.y * scale.z) {
|
if ((long)area->size() != scale.x * scale.y * scale.z) {
|
||||||
LOG_E("Corrupted entity area");
|
LOG_E("Corrupted entity area " << area->size() << "!=" << (scale.x * scale.y * scale.z));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
contouring->onEntityLoad(id, scale, *area);
|
contouring->onEntityLoad(id, scale, *area);
|
||||||
} else {
|
} else {
|
||||||
auto model = std::get_if<Universe::Entity::model_t>(&it->shape);
|
auto model = std::get_if<Universe::Entity::model_t>(&it->shape);
|
||||||
assert(model);
|
assert(model);
|
||||||
auto remain = packet.readAll();
|
auto remain = packet.readRemaining();
|
||||||
model->resize(remain.size());
|
model->resize(remain.size());
|
||||||
//MAYBE: avoid storing model
|
//MAYBE: avoid storing model
|
||||||
memcpy(model->data(), remain.data(), remain.size());
|
memcpy(model->data(), remain.data(), remain.size());
|
||||||
|
|
|
@ -29,6 +29,10 @@ namespace world::client {
|
||||||
bool editPrediction = true;
|
bool editPrediction = true;
|
||||||
/// Get only edits commands and compute locally
|
/// Get only edits commands and compute locally
|
||||||
bool editHandling = true;
|
bool editHandling = true;
|
||||||
|
/// Apply velocity locally. Smooth entities positions
|
||||||
|
bool movePrediction = true;
|
||||||
|
/// Check for entities collisions
|
||||||
|
bool moveCollision = false;
|
||||||
};
|
};
|
||||||
struct connection: net::address {
|
struct connection: net::address {
|
||||||
connection(): net::address{"127.0.0.1", 4242} { }
|
connection(): net::address{"127.0.0.1", 4242} { }
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace glm {
|
||||||
}
|
}
|
||||||
inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
|
inline ifvec3 operator+(const offset_t& v) const { return ifvec3(raw, offset + v); }
|
||||||
inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); }
|
inline ifvec3 operator-(const offset_t &v) const { return ifvec3(raw, offset - v); }
|
||||||
|
inline ifvec3 operator-(const ifvec3 &v) const { return ifvec3(raw - v.raw, offset - v.offset); }
|
||||||
inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
|
inline ifvec3 operator/(int i) const { return ifvec3(raw / i, offset / (i * 1.f), false); }
|
||||||
inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
|
inline ifvec3 operator*(int i) const { return ifvec3(raw * i, offset * (i * 1.f)); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -76,7 +76,8 @@ namespace glm {
|
||||||
constexpr T inline max_axis(const vec<3, T> &v) {
|
constexpr T inline max_axis(const vec<3, T> &v) {
|
||||||
return std::max({v.x, v.y, v.z});
|
return std::max({v.x, v.y, v.z});
|
||||||
}
|
}
|
||||||
ivec3 inline inormalize(const ivec3 &v) {
|
template <typename T>
|
||||||
|
ivec3 inline inormalize(const vec<3, T> &v) {
|
||||||
const auto a = glm::abs(v);
|
const auto a = glm::abs(v);
|
||||||
return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) :
|
return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) :
|
||||||
(a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z)));
|
(a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z)));
|
||||||
|
|
|
@ -24,6 +24,131 @@ 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; }
|
||||||
};
|
};
|
||||||
|
// Abstract data writer
|
||||||
|
struct in_view: view {
|
||||||
|
in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) {
|
||||||
|
assert(ptr != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* ptr;
|
||||||
|
uint8_t* writeTo(size_t len) {
|
||||||
|
assert(cur + len <= siz);
|
||||||
|
auto p = ptr + cur;
|
||||||
|
cur += len;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
void write(const uint8_t* data, size_t len) {
|
||||||
|
memcpy(writeTo(len), data, len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// Writer with Dynamic allocation and subparts
|
||||||
|
struct in_writer: public in_view {
|
||||||
|
private:
|
||||||
|
static constexpr auto EMPTY_OFFSET = 0x1000;
|
||||||
|
static uint8_t* alloc(size_t size) { return (uint8_t*)(size > 0 ? ::malloc(size) : reinterpret_cast<uint8_t*>(EMPTY_OFFSET)); }
|
||||||
|
static uint8_t* realloc(uint8_t* ptr, size_t size) { return (uint8_t*)::realloc(ptr, size); }
|
||||||
|
static void free(uint8_t* ptr) { ::free(ptr); }
|
||||||
|
|
||||||
|
inline void auto_size(size_t len) {
|
||||||
|
if (cur + len > siz)
|
||||||
|
resize(cur + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
in_writer(size_t init_size = 0): in_view(alloc(init_size), init_size), vis_siz(init_size) { }
|
||||||
|
~in_writer() { if (ptr != reinterpret_cast<uint8_t*>(EMPTY_OFFSET)) free(ptr); }
|
||||||
|
|
||||||
|
size_t vis_siz = 0;
|
||||||
|
|
||||||
|
uint8_t* data() { return ptr; }
|
||||||
|
constexpr size_t size() const { return vis_siz; }
|
||||||
|
|
||||||
|
void write(const void* data, size_t len) {
|
||||||
|
in_view::write((uint8_t*)data, len);
|
||||||
|
}
|
||||||
|
/// Write without resize
|
||||||
|
template<typename D>
|
||||||
|
void write(const D& d) {
|
||||||
|
write(&d, sizeof(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* pushTo(size_t len) {
|
||||||
|
auto_size(len);
|
||||||
|
return writeTo(len);
|
||||||
|
}
|
||||||
|
void push(const void* data, size_t len) {
|
||||||
|
auto_size(len);
|
||||||
|
in_view::write((uint8_t*)data, len);
|
||||||
|
}
|
||||||
|
template<typename D>
|
||||||
|
void push(const D& d) {
|
||||||
|
push(&d, sizeof(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getCursor() const { return cur; }
|
||||||
|
void writeAt(size_t pos, const void* data, size_t len) {
|
||||||
|
assert(pos + len <= vis_siz);
|
||||||
|
memcpy(ptr + pos, data, len);
|
||||||
|
}
|
||||||
|
template<typename D>
|
||||||
|
void writeAt(size_t pos, const D& d) {
|
||||||
|
writeAt(pos, &d, sizeof(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(size_t target) {
|
||||||
|
if (target >= siz) {
|
||||||
|
ptr = realloc(ptr, target);
|
||||||
|
siz = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void resize(size_t target) {
|
||||||
|
reserve(target);
|
||||||
|
vis_siz = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFull() const {
|
||||||
|
return cur >= vis_siz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must take ptr ownership before
|
||||||
|
void reset() {
|
||||||
|
ptr = reinterpret_cast<uint8_t*>(EMPTY_OFFSET);
|
||||||
|
cur = 0;
|
||||||
|
siz = 0;
|
||||||
|
vis_siz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct part {
|
||||||
|
part(in_writer* parent): up(parent), start(parent->cur) { }
|
||||||
|
in_writer* up;
|
||||||
|
size_t start;
|
||||||
|
|
||||||
|
uint8_t* data() { return up->data() + start; }
|
||||||
|
constexpr size_t size() const { return up->size() - start; }
|
||||||
|
|
||||||
|
void reserve(size_t size) {
|
||||||
|
up->reserve(start + size);
|
||||||
|
}
|
||||||
|
void resize(size_t size) {
|
||||||
|
up->resize(start + size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<typename CB>
|
||||||
|
size_t pushPart(const CB& callback) {
|
||||||
|
const auto prev = cur;
|
||||||
|
callback(part(this));
|
||||||
|
cur = vis_siz;
|
||||||
|
return cur - prev;
|
||||||
|
}
|
||||||
|
template<typename CB>
|
||||||
|
size_t pushSizedPart(const CB& callback) {
|
||||||
|
const auto size_cur = cur;
|
||||||
|
push<size_t>(0);
|
||||||
|
const size_t size = pushPart(callback);
|
||||||
|
writeAt(size_cur, size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
};
|
||||||
/// Vector with in_view interface
|
/// Vector with in_view interface
|
||||||
struct in_vector {
|
struct in_vector {
|
||||||
/// 512 Mébibits
|
/// 512 Mébibits
|
||||||
|
@ -44,23 +169,6 @@ struct in_vector {
|
||||||
memcpy(writeTo(len), data, len);
|
memcpy(writeTo(len), data, len);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Abstract data writer
|
|
||||||
struct in_view: view {
|
|
||||||
in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) {
|
|
||||||
assert(ptr != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* ptr;
|
|
||||||
uint8_t* writeTo(size_t len) {
|
|
||||||
assert(cur + len <= siz);
|
|
||||||
auto p = ptr + cur;
|
|
||||||
cur += len;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
void write(const uint8_t* data, size_t len) {
|
|
||||||
memcpy(writeTo(len), data, len);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/// Abstract data reader
|
/// Abstract data reader
|
||||||
struct out_view: view {
|
struct out_view: view {
|
||||||
out_view(): out_view(nullptr, 0) { }
|
out_view(): out_view(nullptr, 0) { }
|
||||||
|
@ -80,6 +188,42 @@ 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; }
|
||||||
};
|
};
|
||||||
|
/// Helper to read from out_view
|
||||||
|
struct out_reader: public out_view {
|
||||||
|
out_reader(const out_view& buf): out_view(buf.ptr, buf.siz) { cur = buf.cur; }
|
||||||
|
|
||||||
|
template<typename D>
|
||||||
|
const D* read() {
|
||||||
|
return cur + sizeof(D) <= size() ?
|
||||||
|
(const D*)readFrom(sizeof(D)) : nullptr;
|
||||||
|
}
|
||||||
|
template<typename D>
|
||||||
|
bool read(D& out) {
|
||||||
|
const auto ptr = read<D>();
|
||||||
|
if (ptr == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
out = *ptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool skip(size_t size) {
|
||||||
|
readFrom(size);
|
||||||
|
return !isDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
data::out_view readPart(size_t size) {
|
||||||
|
return data::out_view(readFrom(size), size);
|
||||||
|
}
|
||||||
|
data::out_view readSizedPart() {
|
||||||
|
size_t size = 0;
|
||||||
|
read(size);
|
||||||
|
return readPart(size);
|
||||||
|
}
|
||||||
|
data::out_view readRemaining() {
|
||||||
|
return readPart(remaining());
|
||||||
|
}
|
||||||
|
};
|
||||||
/// Pointer to opaque owned memory
|
/// 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
|
/// out_view with owned data
|
||||||
|
@ -92,6 +236,9 @@ struct out_buffer: out_view {
|
||||||
out_buffer(view, std::shared_ptr<const void>(view.ptr)) { }
|
out_buffer(view, std::shared_ptr<const void>(view.ptr)) { }
|
||||||
/// Take ptr ownership
|
/// Take ptr ownership
|
||||||
out_buffer(const uint8_t *ptr, size_t size): out_buffer(out_view(ptr, size)) { }
|
out_buffer(const uint8_t *ptr, size_t size): out_buffer(out_view(ptr, size)) { }
|
||||||
|
/// Take ptr ownership (release writer)
|
||||||
|
out_buffer(in_writer& writer):
|
||||||
|
out_buffer(writer.data(), writer.size()) { writer.reset(); }
|
||||||
|
|
||||||
handle_t handle = nullptr;
|
handle_t handle = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,100 +8,19 @@ namespace net {
|
||||||
//TODO: preallocate static data pool
|
//TODO: preallocate static data pool
|
||||||
|
|
||||||
/// Helper to write allocated packets
|
/// Helper to write allocated packets
|
||||||
class PacketWriter final {
|
class PacketWriter final: public data::in_writer {
|
||||||
public:
|
public:
|
||||||
PacketWriter(size_t size): buffer((uint8_t*)malloc(size), size), visible_size(size) { }
|
PacketWriter(size_t size): data::in_writer(size) { }
|
||||||
PacketWriter(uint8_t type, size_t data_size): PacketWriter(sizeof(type) + data_size) {
|
PacketWriter(uint8_t type, size_t data_size): PacketWriter(sizeof(type) + data_size) {
|
||||||
write(type);
|
write(type);
|
||||||
}
|
}
|
||||||
PacketWriter(net::client_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { }
|
PacketWriter(net::client_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { }
|
||||||
PacketWriter(net::server_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { }
|
PacketWriter(net::server_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { }
|
||||||
~PacketWriter() {
|
|
||||||
if (buffer.ptr != nullptr)
|
|
||||||
free(buffer.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const void* data, size_t len) {
|
|
||||||
buffer.write((uint8_t*)data, len);
|
|
||||||
}
|
|
||||||
template<typename D>
|
|
||||||
void write(const D& d) {
|
|
||||||
write(&d, sizeof(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t getCursor() const { return buffer.cur; }
|
|
||||||
void writeAt(size_t pos, const void* data, size_t len) {
|
|
||||||
assert(pos + len <= buffer.siz);
|
|
||||||
memcpy(buffer.ptr + pos, data, len);
|
|
||||||
}
|
|
||||||
template<typename D>
|
|
||||||
void writeAt(size_t pos, const D& d) {
|
|
||||||
writeAt(pos, &d, sizeof(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
void reserve(size_t target) {
|
|
||||||
if (target >= buffer.siz - buffer.cur) {
|
|
||||||
const auto size = target + buffer.cur;
|
|
||||||
buffer.ptr = (uint8_t *)realloc(buffer.ptr, size);
|
|
||||||
buffer.siz = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void resize(size_t target) {
|
|
||||||
reserve(target);
|
|
||||||
visible_size = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writePush(const void* data, size_t len) {
|
|
||||||
if (buffer.cur + len > buffer.siz)
|
|
||||||
resize(buffer.cur + len);
|
|
||||||
write(data, len);
|
|
||||||
}
|
|
||||||
template<typename D>
|
|
||||||
void writePush(const D& d) {
|
|
||||||
writePush(&d, sizeof(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct varying_part {
|
|
||||||
varying_part(data::in_view &buffer, size_t& p_size): buffer(buffer), parent_size(p_size), visible_size(0) { }
|
|
||||||
~varying_part() {
|
|
||||||
buffer.cur += visible_size;
|
|
||||||
parent_size += visible_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
data::in_view &buffer;
|
|
||||||
size_t &parent_size;
|
|
||||||
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) {
|
|
||||||
const auto size = target + buffer.cur;
|
|
||||||
buffer.ptr = (uint8_t *)realloc(buffer.ptr, size);
|
|
||||||
buffer.siz = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void resize(size_t target) {
|
|
||||||
reserve(target);
|
|
||||||
visible_size = target;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/// Only from resize, write, resize down
|
|
||||||
varying_part varying() {
|
|
||||||
return varying_part(buffer, visible_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFull() const {
|
|
||||||
return buffer.cur >= visible_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
data::out_buffer finish() {
|
data::out_buffer finish() {
|
||||||
assert(isFull());
|
assert(isFull());
|
||||||
data::out_buffer cpy(buffer.ptr, visible_size);
|
return data::out_buffer(*this);
|
||||||
buffer.ptr = nullptr;
|
|
||||||
buffer.siz = 0;
|
|
||||||
return cpy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
|
@ -126,50 +45,12 @@ public:
|
||||||
static data::out_buffer Of(net::server_packet_type type, const D& data) {
|
static data::out_buffer Of(net::server_packet_type type, const D& data) {
|
||||||
return Of(type, &data, sizeof(data));
|
return Of(type, &data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
data::in_view buffer;
|
|
||||||
size_t visible_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper to read out_view
|
/// Helper to read out_view
|
||||||
class PacketReader final {
|
class PacketReader final: public data::out_reader {
|
||||||
public:
|
public:
|
||||||
PacketReader(const data::out_view& buf): buffer(buf) { }
|
PacketReader(const data::out_view& buf): data::out_reader(buf) { }
|
||||||
|
|
||||||
template<typename D>
|
|
||||||
const D* read() {
|
|
||||||
return buffer.cur + sizeof(D) <= buffer.size() ?
|
|
||||||
(const D*)buffer.readFrom(sizeof(D)) : nullptr;
|
|
||||||
}
|
|
||||||
template<typename D>
|
|
||||||
bool read(D& out) {
|
|
||||||
const auto ptr = read<D>();
|
|
||||||
if (ptr == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
out = *ptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool skip(size_t size) {
|
|
||||||
buffer.readFrom(size);
|
|
||||||
return !isFull();
|
|
||||||
}
|
|
||||||
|
|
||||||
data::out_view readPart(size_t size) {
|
|
||||||
return data::out_view(buffer.readFrom(size), size);
|
|
||||||
}
|
|
||||||
data::out_view readAll() {
|
|
||||||
return readPart(buffer.size() - buffer.cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFull() const {
|
|
||||||
return buffer.isDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
data::out_view buffer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -30,6 +30,8 @@ namespace world::server {
|
||||||
std::optional<double> getCurvature() const override { return GetCurvature(generator_properties); }
|
std::optional<double> getCurvature() const override { return GetCurvature(generator_properties); }
|
||||||
static std::optional<double> GetCurvature(const generator::params &);
|
static std::optional<double> GetCurvature(const generator::params &);
|
||||||
|
|
||||||
|
inline const glm::vec3 getGravity(const voxel_pos& p) const { return generator->getGravity(p); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
shared_guarded<regions_t> regions;
|
shared_guarded<regions_t> regions;
|
||||||
|
|
||||||
|
|
|
@ -306,7 +306,7 @@ void Universe::update(float deltaTime) {
|
||||||
const auto areaDiff = glm::divide(it->second->getOffset().as_voxel());
|
const auto areaDiff = glm::divide(it->second->getOffset().as_voxel());
|
||||||
|
|
||||||
std::vector<chunk_pos> inAreaPlayers;
|
std::vector<chunk_pos> inAreaPlayers;
|
||||||
players.iter([&](entity_id, Entity::Instance player) {
|
players.iter([&](const entity_id&, const Entity::Instance& player) {
|
||||||
const chunk_pos diff = glm::divide(player.pos.as_voxel() - it->second->getOffset().as_voxel());
|
const chunk_pos diff = glm::divide(player.pos.as_voxel() - it->second->getOffset().as_voxel());
|
||||||
if (glm::length2(diff) <= areaRange)
|
if (glm::length2(diff) <= areaRange)
|
||||||
inAreaPlayers.push_back(diff);
|
inAreaPlayers.push_back(diff);
|
||||||
|
@ -414,6 +414,9 @@ void Universe::update(float deltaTime) {
|
||||||
ZoneScopedN("Entities");
|
ZoneScopedN("Entities");
|
||||||
size_t item_count = 0;
|
size_t item_count = 0;
|
||||||
entities.remove([&](entity_id type, Entity &val) {
|
entities.remove([&](entity_id type, Entity &val) {
|
||||||
|
//TODO: find mass center
|
||||||
|
const auto OFFSET = glm::vec3(val.size) * val.scale / 2.f;
|
||||||
|
const auto DIST2 = glm::pow2(loadDistance); //NOTE: must be < keepDistance + glm::length(OFFSET) to avoid collision miss
|
||||||
val.instances.remove([&](entity_id, Entity::Instance &inst) {
|
val.instances.remove([&](entity_id, Entity::Instance &inst) {
|
||||||
if (type == PLAYER_ENTITY_ID) {
|
if (type == PLAYER_ENTITY_ID) {
|
||||||
//MAYBE: update players ?
|
//MAYBE: update players ?
|
||||||
|
@ -421,14 +424,25 @@ void Universe::update(float deltaTime) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inst.pos += inst.velocity * deltaTime;
|
//TODO: partition space
|
||||||
if (true /*FIXME: remove far entities ? glm::length2(glm::divide(pos - inst.pos.as_voxel())) <= glm::pow2(keepDistance);*/) {
|
auto pos = inst.pos + OFFSET;
|
||||||
|
for(const auto& area: areas) {
|
||||||
|
//NOTE: consider entity mass negligible
|
||||||
|
inst.velocity += area.second->getGravity((pos - area.second->getOffset()).as_voxel()) * deltaTime;
|
||||||
|
}
|
||||||
|
//MAYBE: friction
|
||||||
|
if (move(pos, inst.velocity, 1, glm::max_axis(OFFSET))) {
|
||||||
|
inst.velocity = glm::vec3(0);
|
||||||
|
}
|
||||||
|
inst.pos = pos - OFFSET;
|
||||||
|
if (entities.at(PLAYER_ENTITY_ID).instances.contains([&](const entity_id&, const Entity::Instance& player) {
|
||||||
|
return glm::length2(glm::divide((player.pos - inst.pos).as_voxel())) <= DIST2;
|
||||||
|
})) {
|
||||||
item_count++;
|
item_count++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//TODO: Save to files if !temporary
|
||||||
return true;
|
return true;
|
||||||
//MAYBE: Store in region ?
|
|
||||||
//MAYBE: Save to files
|
|
||||||
});
|
});
|
||||||
return !val.permanant && val.instances.empty();
|
return !val.permanant && val.instances.empty();
|
||||||
});
|
});
|
||||||
|
@ -558,7 +572,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case client_packet_type::MESSAGE: {
|
case client_packet_type::MESSAGE: {
|
||||||
const auto ref = packet.readAll();
|
const auto ref = packet.readRemaining();
|
||||||
broadcastMessage("Player" + std::to_string(peer->getCtx<net_client>()->instanceId.index)
|
broadcastMessage("Player" + std::to_string(peer->getCtx<net_client>()->instanceId.index)
|
||||||
+ ": " + std::string((const char*)ref.data(), ref.size()));
|
+ ": " + std::string((const char*)ref.data(), ref.size()));
|
||||||
break;
|
break;
|
||||||
|
@ -573,7 +587,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
|
||||||
break;
|
break;
|
||||||
if (auto area = areas.find(id); area != areas.end()) {
|
if (auto area = areas.find(id); area != areas.end()) {
|
||||||
const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel());
|
const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel());
|
||||||
while (!packet.isFull()) {
|
while (!packet.isDone()) {
|
||||||
region_pos rpos;
|
region_pos rpos;
|
||||||
if (!packet.read(rpos))
|
if (!packet.read(rpos))
|
||||||
break;
|
break;
|
||||||
|
@ -582,10 +596,8 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
|
||||||
if (glm::length2(areaOffset - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) <= glm::pow2(loadDistance + REGION_LENGTH * 2)) {
|
if (glm::length2(areaOffset - glm::lvec3(it_r->first) * glm::lvec3(REGION_LENGTH)) <= glm::pow2(loadDistance + REGION_LENGTH * 2)) {
|
||||||
auto packet = PacketWriter(server_packet_type::REGION, sizeof(area_<region_pos>));
|
auto packet = PacketWriter(server_packet_type::REGION, sizeof(area_<region_pos>));
|
||||||
packet.write<area_<region_pos>>(std::make_pair(id, rpos));
|
packet.write<area_<region_pos>>(std::make_pair(id, rpos));
|
||||||
{
|
packet.pushPart([&](PacketWriter::part part) {
|
||||||
auto vec = packet.varying();
|
it_r->second->averages(part); });
|
||||||
it_r->second->averages(vec);
|
|
||||||
}
|
|
||||||
peer->send(packet.finish(), net::server::CHUNK);
|
peer->send(packet.finish(), net::server::CHUNK);
|
||||||
} else {
|
} else {
|
||||||
LOG_T("Request out of range region");
|
LOG_T("Request out of range region");
|
||||||
|
@ -611,7 +623,7 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
|
||||||
auto &chunks = area->second->getChunks();
|
auto &chunks = area->second->getChunks();
|
||||||
const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel());
|
const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel());
|
||||||
|
|
||||||
for (size_t i = 0; !packet.isFull() && i < MAX_PENDING_CHUNK_COUNT; i++) {
|
for (size_t i = 0; !packet.isDone() && i < MAX_PENDING_CHUNK_COUNT; i++) {
|
||||||
chunk_pos cpos;
|
chunk_pos cpos;
|
||||||
if (!packet.read(cpos))
|
if (!packet.read(cpos))
|
||||||
break;
|
break;
|
||||||
|
@ -642,15 +654,8 @@ bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net:
|
||||||
for (auto it = area->begin(); it != area->end(); ++it) {
|
for (auto it = area->begin(); it != area->end(); ++it) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
std::dynamic_pointer_cast<Chunk>(*it)->write(out);
|
std::dynamic_pointer_cast<Chunk>(*it)->write(out);
|
||||||
auto size_pos = packet.getCursor();
|
packet.pushSizedPart([&](PacketWriter::part part) {
|
||||||
size_t size = 0;
|
dict_write_ctx.compress(out.str(), part); });
|
||||||
packet.writePush(size);
|
|
||||||
{
|
|
||||||
auto vec = packet.varying();
|
|
||||||
dict_write_ctx.compress(out.str(), vec);
|
|
||||||
size = vec.size();
|
|
||||||
}
|
|
||||||
packet.writeAt(size_pos, size);
|
|
||||||
}
|
}
|
||||||
peer->send(packet.finish(), net::server::CHUNK);
|
peer->send(packet.finish(), net::server::CHUNK);
|
||||||
} else {
|
} else {
|
||||||
|
@ -691,10 +696,8 @@ data::out_buffer Universe::serializeChunk(area_<chunk_pos> id, const std::shared
|
||||||
data->write(out);
|
data->write(out);
|
||||||
auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id));
|
auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id));
|
||||||
packet.write(id);
|
packet.write(id);
|
||||||
{
|
packet.pushPart([&](net::PacketWriter::part part){
|
||||||
auto vec = packet.varying();
|
dict_write_ctx.compress(out.str(), part); });
|
||||||
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) {
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace world::generator {
|
||||||
/// Generate chunk voxels
|
/// Generate chunk voxels
|
||||||
/// MAYBE: use template to avoid virtual
|
/// MAYBE: use template to avoid virtual
|
||||||
virtual void generate(const chunk_pos &at, std::array<Voxel, CHUNK_SIZE> &out) = 0;
|
virtual void generate(const chunk_pos &at, std::array<Voxel, CHUNK_SIZE> &out) = 0;
|
||||||
|
/// Get gravity vector at given point
|
||||||
|
virtual glm::vec3 getGravity(const voxel_pos& point) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate empty space
|
// Generate empty space
|
||||||
|
@ -24,6 +26,7 @@ namespace world::generator {
|
||||||
void generate(const chunk_pos &, std::array<Voxel, CHUNK_SIZE> &out) override {
|
void generate(const chunk_pos &, std::array<Voxel, CHUNK_SIZE> &out) override {
|
||||||
out.fill(Voxel());
|
out.fill(Voxel());
|
||||||
}
|
}
|
||||||
|
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Endless cave network
|
/// Endless cave network
|
||||||
|
@ -51,6 +54,7 @@ namespace world::generator {
|
||||||
out[i] = Voxel(material, density);
|
out[i] = Voxel(material, density);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
|
||||||
private:
|
private:
|
||||||
Params params;
|
Params params;
|
||||||
Noise density;
|
Noise density;
|
||||||
|
@ -110,6 +114,12 @@ namespace world::generator {
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
glm::vec3 getGravity(const voxel_pos& pos) const override {
|
||||||
|
const auto heightRatio = static_cast<float>((getHeight(pos) - params.height) / params.height);
|
||||||
|
const auto scale = params.height >> 7;
|
||||||
|
const auto mass = scale * scale * scale; //FIXME: average material density
|
||||||
|
return -getSurfaceDir(pos) * std::max(0.f, 1.f - heightRatio * heightRatio) * glm::vec3(mass);
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
static constexpr PlanetShape shape = PS;
|
static constexpr PlanetShape shape = PS;
|
||||||
static constexpr glm::f64 getHeight(const voxel_pos &p) {
|
static constexpr glm::f64 getHeight(const voxel_pos &p) {
|
||||||
|
@ -119,6 +129,15 @@ namespace world::generator {
|
||||||
return glm::length(glm::dvec3(p));
|
return glm::length(glm::dvec3(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Centre inverse direction
|
||||||
|
// NOTE: So center point is consider mass center
|
||||||
|
static constexpr glm::vec3 getSurfaceDir(const voxel_pos &p) {
|
||||||
|
if constexpr(shape == PlanetShape::Cube) {
|
||||||
|
return glm::inormalize(p);
|
||||||
|
} else {
|
||||||
|
return glm::normalize(glm::dvec3(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
Params params;
|
Params params;
|
||||||
Noise density;
|
Noise density;
|
||||||
Noise displacement;
|
Noise displacement;
|
||||||
|
|
Loading…
Reference in New Issue