Commit 7f8e249a authored by Shu's avatar Shu 🍠
Browse files

Basic gravity using pseudo sphere collider

parent 96f4bbf1
......@@ -68,6 +68,8 @@ public:
world.trustMajorant = config["world"]["trust_majorant"].value_or(world.trustMajorant);
world.editPrediction = config["world"]["edit_prediction"].value_or(world.editPrediction);
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);
#ifndef STANDALONE
......@@ -148,6 +150,8 @@ public:
{"trust_majorant", world.trustMajorant},
{"edit_prediction", world.editPrediction},
{"edit_handling", world.editHandling},
{"move_prediction", world.movePrediction},
{"move_collision", world.moveCollision},
{"voxel_density", voxel_density}
}));
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);
}
......@@ -172,7 +185,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
}
case server_packet_type::COMPRESSION: {
const auto remain = packet.readAll();
const auto remain = packet.readRemaining();
dict.emplace(remain.data(), remain.size());
LOG_T("Compression dictionnary loaded");
mayQueryChunks = true;
......@@ -187,13 +200,13 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
}
case server_packet_type::MESSAGE: {
const auto remain = packet.readAll();
const auto remain = packet.readRemaining();
onMessage(std::string((const char*)remain.data(), remain.size()));
break;
}
case server_packet_type::AREAS: {
while(!packet.isFull()) {
while(!packet.isDone()) {
area_id id;
world::Area::params 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
uint16_t full = 0;
uint16_t total = 0;
while (!packet.isFull()) {
while (!packet.isDone()) {
region_chunk_pos cpos;
Voxel 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())
break;
if (packet.isFull()) {
if (packet.isDone()) {
mayQueryChunks = true;
break;
}
......@@ -267,7 +280,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
}
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());
break;
}
......@@ -313,14 +326,14 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
break;
}
while(!packet.isFull()) {
while(!packet.isDone()) {
chunk_pos pos = chunk_pos(INT_MAX);
packet.read(pos);
chunk_voxel_idx count = 0;
packet.read(count);
if (auto ptr = it->second->setChunks().findInRange(pos)) {
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;
packet.read(edit);
chunk->apply(edit);
......@@ -334,7 +347,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
case server_packet_type::ENTITY_TYPES: {
entities.clear();
while(!packet.isFull()) {
while(!packet.isDone()) {
size_t index;
glm::usvec3 size;
glm::vec3 scale;
......@@ -351,7 +364,7 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
case server_packet_type::ENTITIES: {
entities.remove([](size_t, Entity &entity) { entity.instances.clear(); return false; });
while(!packet.isFull()) {
while(!packet.isDone()) {
size_t entity_idx;
size_t 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 (count == 0)
continue;
for (size_t i = 0; i < count && !packet.isFull(); i++) {
for (size_t i = 0; i < count && !packet.isDone(); i++) {
size_t idx;
glm::ifvec3 pos;
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);
assert(area);
area->clear();
glm::i64 i = 0;
while (!packet.isFull()) {
i++;
size_t size = 0;
if (!packet.read(size))
break;
while (!packet.isDone()) {
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());
break;
}
......@@ -427,15 +434,15 @@ bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) {
area->push_back(std::make_shared<Chunk>(iss));
}
auto scale = glm::lvec3(1) + glm::divide(it->size);
if (i != scale.x * scale.y * scale.z) {
LOG_E("Corrupted entity area");
if ((long)area->size() != scale.x * scale.y * scale.z) {
LOG_E("Corrupted entity area " << area->size() << "!=" << (scale.x * scale.y * scale.z));
break;
}
contouring->onEntityLoad(id, scale, *area);
} else {
auto model = std::get_if<Universe::Entity::model_t>(&it->shape);
assert(model);
auto remain = packet.readAll();
auto remain = packet.readRemaining();
model->resize(remain.size());
//MAYBE: avoid storing model
memcpy(model->data(), remain.data(), remain.size());
......
......@@ -29,6 +29,10 @@ namespace world::client {
bool editPrediction = true;
/// Get only edits commands and compute locally
bool editHandling = true;
/// Apply velocity locally. Smooth entities positions
bool movePrediction = true;
/// Check for entities collisions
bool moveCollision = false;
};
struct connection: net::address {
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 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)); }
};
......
......@@ -76,7 +76,8 @@ namespace glm {
constexpr T inline max_axis(const vec<3, T> &v) {
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);
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)));
......
......@@ -24,6 +24,131 @@ struct view {
constexpr size_t size() const { return 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
struct in_vector {
/// 512 Mébibits
......@@ -44,23 +169,6 @@ struct in_vector {
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
struct out_view: view {
out_view(): out_view(nullptr, 0) { }
......@@ -80,6 +188,42 @@ struct out_view: view {
constexpr const uint8_t* data() const { return ptr; }
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
using handle_t = std::shared_ptr<const void>;
/// out_view with owned data
......@@ -92,6 +236,9 @@ struct out_buffer: out_view {
out_buffer(view, std::shared_ptr<const void>(view.ptr)) { }
/// Take ptr ownership
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;
};
......
......@@ -8,100 +8,19 @@ namespace net {
//TODO: preallocate static data pool
/// Helper to write allocated packets
class PacketWriter final {
class PacketWriter final: public data::in_writer {
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) {
write(type);
}
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() {
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]]
data::out_buffer finish() {
assert(isFull());
data::out_buffer cpy(buffer.ptr, visible_size);
buffer.ptr = nullptr;
buffer.siz = 0;
return cpy;
return data::out_buffer(*this);
}
[[nodiscard]]
......@@ -126,50 +45,12 @@ public:
static data::out_buffer Of(net::server_packet_type type, const D& data) {
return Of(type, &data, sizeof(data));
}
private:
data::in_view buffer;
size_t visible_size;
};
/// Helper to read out_view
class PacketReader final {
class PacketReader final: public data::out_reader {
public:
PacketReader(const data::out_view& buf): buffer(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;
PacketReader(const data::out_view& buf): data::out_reader(buf) { }
};
}
\ No newline at end of file
......@@ -30,6 +30,8 @@ namespace world::server {
std::optional<double> getCurvature() const override { return GetCurvature(generator_properties); }
static std::optional<double> GetCurvature(const generator::params &);
inline const glm::vec3 getGravity(const voxel_pos& p) const { return generator->getGravity(p); }
private:
shared_guarded<regions_t> regions;
......
......@@ -306,7 +306,7 @@ void Universe::update(float deltaTime) {
const auto areaDiff = glm::divide(it->second->getOffset().as_voxel());
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());
if (glm::length2(diff) <= areaRange)
inAreaPlayers.push_back(diff);
......@@ -414,6 +414,9 @@ void Universe::update(float deltaTime) {
ZoneScopedN("Entities");
size_t item_count = 0;
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) {
if (type == PLAYER_ENTITY_ID) {
//MAYBE: update players ?
......@@ -421,14 +424,25 @@ void Universe::update(float deltaTime) {
return false;
}
inst.pos += inst.velocity * deltaTime;
if (true /*FIXME: remove far entities ? glm::length2(glm::divide(pos - inst.pos.as_voxel())) <= glm::pow2(keepDistance);*/) {
//TODO: partition space
auto pos = inst.pos + OFFSET;
for(const auto& area: areas) {
//NOTE: consider entity mass negligible