1
0
Fork 0
Univerxel/src/client/world/DistantUniverse.cpp

216 lines
9.0 KiB
C++

#include "DistantUniverse.hpp"
#include <Tracy.hpp>
#include "Area.hpp"
#include "../contouring/Abstract.hpp"
#include "../../core/world/raycast.hpp"
#include "../../core/utils/logger.hpp"
#include "Chunk.hpp"
using namespace world::client;
DistantUniverse::DistantUniverse(const connection& ct, const options& opt, const std::string& contouring): Universe(contouring), peer(ct),
loadDistance(opt.loadDistance), keepDistance(opt.keepDistance), serverDistance(0) { }
DistantUniverse::~DistantUniverse() { }
void DistantUniverse::update(voxel_pos pos, float deltaTime) {
const auto cur_chunk = glm::divide(pos);
const auto chunkChange = cur_chunk != last_chunk;
last_chunk = cur_chunk;
pullNetwork(pos);
{ // Update alive areas
ZoneScopedN("World");
for (auto& area: areas) {
ZoneScopedN("Area");
const bool chunkChangeArea = (false && area.second->move(glm::vec3(deltaTime))) || chunkChange; // TODO: area.velocity
const chunk_pos diff = glm::divide(pos - area.second->getOffset().as_voxel());
auto &chunks = area.second->setChunks();
if (glm::length2(diff) <= glm::pow2(keepDistance + area.second->getChunks().getRadius())) {
ZoneScopedN("Alive");
auto it_c = chunks.begin();
while (it_c != chunks.end()) {
if (glm::length2(diff - it_c->first) > glm::pow2(keepDistance)) {
it_c = chunks.erase(it_c);
} else {
if(const auto neighbors = std::dynamic_pointer_cast<world::client::EdittableChunk>(it_c->second)->update(deltaTime, true /*FIXME: rnd from contouring*/)) {
contouring->onUpdate(std::make_pair(area.first, it_c->first), diff, chunks, neighbors.value());
}
++it_c;
}
}
if (chunkChangeArea) { // Request missing chunks
ZoneScopedN("Missing");
std::vector<chunk_pos> missing;
//TODO: use easy sphere fill
const int queryDistance = std::min(loadDistance, serverDistance);
for (int x = -queryDistance; x <= queryDistance; x++) {
for (int y = -queryDistance; y <= queryDistance; y++) {
for (int z = -queryDistance; z <= queryDistance; z++) {
const auto dist2 = x * x + y * y + z * z;
const auto p = diff + chunk_pos(x, y, z);
if (dist2 <= queryDistance * queryDistance && chunks.inRange(p)) {
if (chunks.find(p) != chunks.end()) {
contouring->onNotify(std::make_pair(area.first, p), diff, chunks);
} else {
missing.push_back(p);
}
}
}}}
if(!missing.empty()) {
auto packet = net::Client::makePacket(net::client_packet_type::MISSING_CHUNKS, NULL,
sizeof(area_id) + missing.size() * sizeof(chunk_pos), ENET_PACKET_FLAG_RELIABLE, peer.getSalt());
packet.write(area.first);
packet.write(missing.data(), missing.size() * sizeof(chunk_pos));
peer.send(packet.get(), net::channel_type::RELIABLE);
}
}
}
}
}
contouring->update(pos, areas);
}
void DistantUniverse::pullNetwork(voxel_pos pos) {
ZoneScopedN("Pull");
using namespace net;
peer.pull(
[&](packet_t* packet, channel_type){
const server_packet_type type = static_cast<server_packet_type>(*packet->data);
switch (type) {
case server_packet_type::CAPABILITIES: {
PacketReader(packet, true).read(serverDistance);
break;
}
case server_packet_type::COMPRESSION: {
if(dict.has_value())
break;
dict.emplace(packet->data + sizeof(server_packet_type), packet->dataLength - sizeof(server_packet_type));
LOG_T("Compression dictionnary loaded");
break;
}
case server_packet_type::TELEPORT: {
PacketReader(packet).read(pos);
onTeleport(pos);
break;
}
case server_packet_type::AREAS: {
auto reader = PacketReader(packet, true);
while(!reader.isFull()) {
area_id id;
if(!reader.read(id))
break;
world::Area::params p;
if(!reader.read(p))
break;
if (auto it = areas.find(id); it != areas.end()) {
std::dynamic_pointer_cast<Area>(it->second)->update(p);
} else {
areas.emplace(id, std::make_shared<Area>(p));
}
}
break;
}
case server_packet_type::CHUNK: {
ZoneScopedN("Chunk");
if (!dict.has_value())
break;
auto reader = PacketReader(packet, true);
area_<chunk_pos> pos;
if(!reader.read(pos))
break;
auto it = areas.find(pos.first);
if(it == areas.end()) {
LOG_W("Chunk area not found " << pos.first.index);
break;
}
if(!it->second->getChunks().inRange(pos.second)) {
LOG_W("Chunk out of area " << pos.first.index);
break;
}
auto data = reader.remaning();
std::vector<char> buffer;
if(auto err = dict.value().decompress(data, buffer)) {
LOG_E("Corrupted chunk packet " << err.value());
break;
}
vec_istream idata(buffer);
std::istream iss(&idata);
auto ck = std::make_shared<world::client::EdittableChunk>(iss);
ck->invalidate(geometry::Faces::All);
auto ptr = std::dynamic_pointer_cast<world::Chunk>(ck);
auto &chunks = it->second->setChunks();
if (auto it_c = chunks.find(pos.second); it_c != chunks.end()) {
it_c->second = ptr;
} else {
chunks.emplace(pos.second, ptr);
}
break;
}
case server_packet_type::EDITS: {
ZoneScopedN("Edits");
auto reader = PacketReader(packet, true);
area_id id;
if(!reader.read(id))
break;
auto it = areas.find(id);
if(it == areas.end()) {
LOG_W("Edit area not found " << id.index);
break;
}
while(!reader.isFull()) {
chunk_pos pos = chunk_pos(INT_MAX);
reader.read(pos);
chunk_voxel_idx count = 0;
reader.read(count);
if (auto ptr = it->second->setChunks().findInRange(pos)) {
auto chunk = std::dynamic_pointer_cast<world::client::EdittableChunk>(ptr.value());
for (auto i = 0; i < count && !reader.isFull(); i++) {
Chunk::Edit edit;
reader.read(edit);
chunk->apply(edit);
}
} else {
reader.skip(count * sizeof(Chunk::Edit));
}
}
break;
}
default:
LOG_W("Bad packet from server");
break;
}
},
[](disconnect_reason){ });
}
void DistantUniverse::emit(const action::packet &action) {
if(const auto move = std::get_if<action::Move>(&action)) {
peer.send(net::client_packet_type::MOVE, move->pos, net::channel_type::NOTIFY);
} else if(const auto fillCube = std::get_if<action::FillCube>(&action)) {
peer.send(net::client_packet_type::FILL_CUBE, *fillCube, net::channel_type::RELIABLE);
} else {
LOG_W("Bad action " << action.index());
}
}
Universe::ray_result DistantUniverse::raycast(const geometry::Ray &ray) const {
return Raycast(ray, areas);
}