builtin/voxelworld: Use voxelworld:node_volume_updated instead of CommitHook for post-commit processing and continuously limit amount of loaded buffers

This commit is contained in:
Perttu Ahola 2014-10-23 19:29:40 +03:00
parent 9fc9f846e2
commit 6384ff4d84
3 changed files with 183 additions and 125 deletions

View File

@ -163,68 +163,6 @@ struct GlobalYSTMap
} }
}; };
// Y-seethrough commit hook
struct YstCommitHook: public voxelworld::CommitHook
{
static constexpr const char *MODULE = "YstCommitHook";
interface::Server *m_server;
YstCommitHook(interface::Server *server):
m_server(server)
{}
void after_commit(voxelworld::Interface *ivoxelworld,
const pv::Vector3DInt32 &chunk_p)
{
interface::VoxelRegistry *voxel_reg = ivoxelworld->get_voxel_reg();
//const auto &chunk_size_voxels = ivoxelworld->get_chunk_size_voxels();
pv::Region chunk_region = ivoxelworld->get_chunk_region_voxels(chunk_p);
ground_plane_lighting::access(m_server,
[&](ground_plane_lighting::Interface *igpl)
{
auto lc = chunk_region.getLowerCorner();
auto uc = chunk_region.getUpperCorner();
//log_nv(MODULE, "yst=[");
for(int z = lc.getZ(); z <= uc.getZ(); z++){
for(int x = lc.getX(); x <= uc.getX(); x++){
int32_t yst0 = igpl->get_yst(x, z);
if(yst0 > uc.getY()){
// Y-seethrough doesn't reach here
continue;
}
int y = uc.getY();
for(;; y--){
VoxelInstance v = ivoxelworld->get_voxel(
pv::Vector3DInt32(x, y, z), true);
if(v.get_id() == interface::VOXELTYPEID_UNDEFINED){
// NOTE: This leaves the chunks below unhandled;
// there would have to be some kind of a dirty
// flag based on which this seach would be
// continued at a later point when the chunk
// gets loaded
break;
}
const auto *def = voxel_reg->get_cached(v);
if(!def)
throw Exception(ss_()+"Undefined voxel: "+
itos(v.get_id()));
bool light_passes = (!def || !def->physically_solid);
if(!light_passes)
break;
}
// The first voxel downwards from the top of the world that
// doesn't pass light
int32_t yst1 = y;
//log_nv(MODULE, "%i -> %i, ", yst0, yst1);
igpl->set_yst(x, z, yst1);
}
}
//log_v(MODULE, "]");
});
}
};
struct Module: public interface::Module, public ground_plane_lighting::Interface struct Module: public interface::Module, public ground_plane_lighting::Interface
{ {
interface::Server *m_server; interface::Server *m_server;
@ -246,9 +184,10 @@ struct Module: public interface::Module, public ground_plane_lighting::Interface
m_server->sub_event(this, Event::t("core:start")); m_server->sub_event(this, Event::t("core:start"));
m_server->sub_event(this, Event::t("core:unload")); m_server->sub_event(this, Event::t("core:unload"));
m_server->sub_event(this, Event::t("core:continue")); m_server->sub_event(this, Event::t("core:continue"));
m_server->sub_event(this, Event::t("network:client_connected"));
m_server->sub_event(this, Event::t("core:tick")); m_server->sub_event(this, Event::t("core:tick"));
m_server->sub_event(this, Event::t("network:client_connected"));
m_server->sub_event(this, Event::t("client_file:files_transmitted")); m_server->sub_event(this, Event::t("client_file:files_transmitted"));
m_server->sub_event(this, Event::t("voxelworld:node_volume_updated"));
voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld) voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld)
{ {
@ -265,20 +204,17 @@ struct Module: public interface::Module, public ground_plane_lighting::Interface
EVENT_VOIDN("core:start", on_start) EVENT_VOIDN("core:start", on_start)
EVENT_VOIDN("core:unload", on_unload) EVENT_VOIDN("core:unload", on_unload)
EVENT_VOIDN("core:continue", on_continue) EVENT_VOIDN("core:continue", on_continue)
EVENT_TYPEN("core:tick", on_tick, interface::TickEvent)
EVENT_TYPEN("network:client_connected", on_client_connected, EVENT_TYPEN("network:client_connected", on_client_connected,
network::NewClient) network::NewClient)
EVENT_TYPEN("core:tick", on_tick, interface::TickEvent)
EVENT_TYPEN("client_file:files_transmitted", on_files_transmitted, EVENT_TYPEN("client_file:files_transmitted", on_files_transmitted,
client_file::FilesTransmitted) client_file::FilesTransmitted)
EVENT_TYPEN("voxelworld:node_volume_updated",
on_node_volume_updated, voxelworld::NodeVolumeUpdated)
} }
void on_start() void on_start()
{ {
voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld)
{
ivoxelworld->add_commit_hook(
up_<YstCommitHook>(new YstCommitHook(m_server)));
});
} }
void on_unload() void on_unload()
@ -289,10 +225,6 @@ struct Module: public interface::Module, public ground_plane_lighting::Interface
{ {
} }
void on_client_connected(const network::NewClient &client_connected)
{
}
void on_tick(const interface::TickEvent &event) void on_tick(const interface::TickEvent &event)
{ {
if(!m_global_yst->m_dirty_sectors.empty()){ if(!m_global_yst->m_dirty_sectors.empty()){
@ -317,6 +249,10 @@ struct Module: public interface::Module, public ground_plane_lighting::Interface
} }
} }
void on_client_connected(const network::NewClient &client_connected)
{
}
void on_files_transmitted(const client_file::FilesTransmitted &event) void on_files_transmitted(const client_file::FilesTransmitted &event)
{ {
int peer = event.recipient; int peer = event.recipient;
@ -336,6 +272,64 @@ struct Module: public interface::Module, public ground_plane_lighting::Interface
send_initial_sectors(peer); send_initial_sectors(peer);
} }
void on_node_volume_updated(const voxelworld::NodeVolumeUpdated &event)
{
if(!event.is_static_chunk)
return;
log_v(MODULE, "on_node_volume_updated(): " PV3I_FORMAT,
PV3I_PARAMS(event.chunk_p));
const pv::Vector3DInt32 &chunk_p = event.chunk_p;
voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld)
{
interface::VoxelRegistry *voxel_reg = ivoxelworld->get_voxel_reg();
//const auto &chunk_size_voxels = ivoxelworld->get_chunk_size_voxels();
pv::Region chunk_region = ivoxelworld->get_chunk_region_voxels(chunk_p);
ground_plane_lighting::access(m_server,
[&](ground_plane_lighting::Interface *igpl)
{
auto lc = chunk_region.getLowerCorner();
auto uc = chunk_region.getUpperCorner();
//log_nv(MODULE, "yst=[");
for(int z = lc.getZ(); z <= uc.getZ(); z++){
for(int x = lc.getX(); x <= uc.getX(); x++){
int32_t yst0 = igpl->get_yst(x, z);
if(yst0 > uc.getY()){
// Y-seethrough doesn't reach here
continue;
}
int y = uc.getY();
for(;; y--){
VoxelInstance v = ivoxelworld->get_voxel(
pv::Vector3DInt32(x, y, z), true);
if(v.get_id() == interface::VOXELTYPEID_UNDEFINED){
// NOTE: This leaves the chunks below unhandled;
// there would have to be some kind of a dirty
// flag based on which this seach would be
// continued at a later point when the chunk
// gets loaded
break;
}
const auto *def = voxel_reg->get_cached(v);
if(!def)
throw Exception(ss_()+"Undefined voxel: "+
itos(v.get_id()));
bool light_passes = (!def || !def->physically_solid);
if(!light_passes)
break;
}
// The first voxel downwards from the top of the world that
// doesn't pass light
int32_t yst1 = y;
//log_nv(MODULE, "%i -> %i, ", yst0, yst1);
igpl->set_yst(x, z, yst1);
}
}
//log_v(MODULE, "]");
});
});
}
void send_initial_sectors(int peer) void send_initial_sectors(int peer)
{ {
for(auto &pair : m_global_yst->m_sectors){ for(auto &pair : m_global_yst->m_sectors){

View File

@ -32,11 +32,16 @@ namespace voxelworld
{} {}
}; };
struct NodeVoxelDataUpdatedEvent: public interface::Event::Private struct NodeVolumeUpdated: public interface::Event::Private
{ {
uint node_id; uint node_id;
bool is_static_chunk = false;
pv::Vector3DInt32 chunk_p; // Only set if is_static_chunk == true
NodeVoxelDataUpdatedEvent(uint node_id): node_id(node_id){} NodeVolumeUpdated(uint node_id, bool is_static_chunk,
const pv::Vector3DInt32 &chunk_p):
node_id(node_id), is_static_chunk(is_static_chunk), chunk_p(chunk_p)
{}
}; };
struct Interface; struct Interface;
@ -46,12 +51,9 @@ namespace voxelworld
virtual ~CommitHook(){} virtual ~CommitHook(){}
virtual void in_thread(voxelworld::Interface *ivoxelworld, virtual void in_thread(voxelworld::Interface *ivoxelworld,
const pv::Vector3DInt32 &chunk_p, const pv::Vector3DInt32 &chunk_p,
sp_<pv::RawVolume<VoxelInstance>> volume){} pv::RawVolume<VoxelInstance> &volume){}
virtual void in_scene(voxelworld::Interface *ivoxelworld, virtual void in_scene(voxelworld::Interface *ivoxelworld,
const pv::Vector3DInt32 &chunk_p, magic::Node *n){} const pv::Vector3DInt32 &chunk_p, magic::Node *n){}
// TODO: Remove this hook callback and use an event instead
virtual void after_commit(voxelworld::Interface *ivoxelworld,
const pv::Vector3DInt32 &chunk_p){}
}; };
struct Interface struct Interface

View File

@ -15,6 +15,7 @@
#include "interface/polyvox_numeric.h" #include "interface/polyvox_numeric.h"
#include "interface/polyvox_cereal.h" #include "interface/polyvox_cereal.h"
#include "interface/polyvox_std.h" #include "interface/polyvox_std.h"
#include "interface/os.h"
#include <PolyVoxCore/RawVolume.h> #include <PolyVoxCore/RawVolume.h>
#include <cereal/archives/portable_binary.hpp> #include <cereal/archives/portable_binary.hpp>
#include <cereal/types/string.hpp> #include <cereal/types/string.hpp>
@ -46,12 +47,31 @@ namespace voxelworld {
struct ChunkBuffer struct ChunkBuffer
{ {
sp_<pv::RawVolume<VoxelInstance>> volume; static constexpr const char *MODULE = "ChunkBuffer";
pv::Vector3DInt32 chunk_p; // For logging
up_<pv::RawVolume<VoxelInstance>> volume;
bool dirty = false; // If false, buffer has only been read from so far bool dirty = false; // If false, buffer has only been read from so far
int64_t last_accessed_us = 0;
ChunkBuffer(){}
void timer_reset(const pv::Vector3DInt32 &chunk_p_){
chunk_p = chunk_p_;
last_accessed_us = interface::os::get_timeofday_us();
}
bool unload_if_old(int64_t timeout_us){ // True if not loaded
if(!volume)
return true;
if(interface::os::get_timeofday_us() < last_accessed_us + timeout_us)
return false;
log_d(MODULE, "Unloading chunk " PV3I_FORMAT, PV3I_PARAMS(chunk_p));
volume.reset();
return true;
}
}; };
struct Section struct Section
{ {
static constexpr const char *MODULE = "Section";
pv::Vector3DInt16 section_p; // Position in sections pv::Vector3DInt16 section_p; // Position in sections
pv::Vector3DInt16 chunk_size; pv::Vector3DInt16 chunk_size;
pv::Region contained_chunks; // Position and size in chunks pv::Region contained_chunks; // Position and size in chunks
@ -96,7 +116,7 @@ struct Section
pv::Vector3DInt32 get_chunk_p(size_t chunk_p); pv::Vector3DInt32 get_chunk_p(size_t chunk_p);
ChunkBuffer& get_buffer(const pv::Vector3DInt32 &chunk_p, ChunkBuffer& get_buffer(const pv::Vector3DInt32 &chunk_p,
interface::Server *server); interface::Server *server, size_t *total_buffers_loaded);
}; };
size_t Section::get_chunk_i(const pv::Vector3DInt32 &chunk_p) // global chunk_p size_t Section::get_chunk_i(const pv::Vector3DInt32 &chunk_p) // global chunk_p
@ -127,14 +147,16 @@ pv::Vector3DInt32 Section::get_chunk_p(size_t chunk_i)
} }
ChunkBuffer& Section::get_buffer(const pv::Vector3DInt32 &chunk_p, ChunkBuffer& Section::get_buffer(const pv::Vector3DInt32 &chunk_p,
interface::Server *server) interface::Server *server, size_t *total_buffers_loaded)
{ {
size_t chunk_i = get_chunk_i(chunk_p); size_t chunk_i = get_chunk_i(chunk_p);
ChunkBuffer &buf = chunk_buffers[chunk_i]; ChunkBuffer &buf = chunk_buffers[chunk_i];
buf.timer_reset(chunk_p);
// If loaded, return right away // If loaded, return right away
if(buf.volume) if(buf.volume)
return buf; return buf;
// Not loaded. // Not loaded.
log_d(MODULE, "Loading chunk " PV3I_FORMAT, PV3I_PARAMS(chunk_p));
// Get the static voxel node from the scene and read the volume from it // Get the static voxel node from the scene and read the volume from it
int32_t node_id = node_ids->getVoxelAt(chunk_p); int32_t node_id = node_ids->getVoxelAt(chunk_p);
if(node_id == 0){ if(node_id == 0){
@ -156,9 +178,7 @@ ChunkBuffer& Section::get_buffer(const pv::Vector3DInt32 &chunk_p,
const Variant &var = n->GetVar(StringHash("buildat_voxel_data")); const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
const PODVector<unsigned char> &rawbuf = var.GetBuffer(); const PODVector<unsigned char> &rawbuf = var.GetBuffer();
ss_ data((const char*)&rawbuf[0], rawbuf.Size()); ss_ data((const char*)&rawbuf[0], rawbuf.Size());
up_<pv::RawVolume<VoxelInstance>> volume = buf.volume = interface::deserialize_volume(data);
interface::deserialize_volume(data);
buf.volume = sp_<pv::RawVolume<VoxelInstance>>(std::move(volume));
if(!buf.volume){ if(!buf.volume){
log_w("voxelworld", log_w("voxelworld",
"Section::get_buffer(): Voxel volume could not be " "Section::get_buffer(): Voxel volume could not be "
@ -168,17 +188,18 @@ ChunkBuffer& Section::get_buffer(const pv::Vector3DInt32 &chunk_p,
return; return;
} }
}); });
(*total_buffers_loaded)++;
return buf; return buf;
} }
struct QueuedNodePhysicsUpdate struct QueuedNodePhysicsUpdate
{ {
uint node_id = 0; uint node_id = 0;
sp_<pv::RawVolume<VoxelInstance>> volume; // TODO bool is_static_chunk = false;
// TODO pv::Vector3DInt32 chunk_p; // Only set if is_static_chunk == true
QueuedNodePhysicsUpdate(const uint &node_id, QueuedNodePhysicsUpdate(const uint &node_id):
sp_<pv::RawVolume<VoxelInstance>> volume): node_id(node_id){}
node_id(node_id), volume(volume){}
bool operator>(const QueuedNodePhysicsUpdate &other) const { bool operator>(const QueuedNodePhysicsUpdate &other) const {
return node_id > other.node_id; return node_id > other.node_id;
} }
@ -204,6 +225,9 @@ struct Module: public interface::Module, public voxelworld::Interface
// The world is loaded and unloaded by sections (eg. 2x2x2) // The world is loaded and unloaded by sections (eg. 2x2x2)
pv::Vector3DInt16 m_section_size_chunks = pv::Vector3DInt16(2, 2, 2); pv::Vector3DInt16 m_section_size_chunks = pv::Vector3DInt16(2, 2, 2);
int64_t m_buffer_unload_timeout = 5000000;
size_t m_max_buffers_loaded = 50;
// Sections (this(y,z)=sector, sector(x)=section) // Sections (this(y,z)=sector, sector(x)=section)
sm_<pv::Vector<2, int16_t>, sm_<int16_t, Section>> m_sections; sm_<pv::Vector<2, int16_t>, sm_<int16_t, Section>> m_sections;
// Cache of last used sections (add to end, remove from beginning) // Cache of last used sections (add to end, remove from beginning)
@ -212,6 +236,7 @@ struct Module: public interface::Module, public voxelworld::Interface
// Set of sections that have buffers allocated // Set of sections that have buffers allocated
// (as a sorted array in descending order) // (as a sorted array in descending order)
std::vector<Section*> m_sections_with_loaded_buffers; std::vector<Section*> m_sections_with_loaded_buffers;
size_t m_total_buffers_loaded = 0;
// Set of nodes by node_id that need set_voxel_physics_boxes() // Set of nodes by node_id that need set_voxel_physics_boxes()
// (as a sorted array in descending node_id order) // (as a sorted array in descending node_id order)
@ -382,19 +407,27 @@ struct Module: public interface::Module, public voxelworld::Interface
} }
for(QueuedNodePhysicsUpdate &update: m_nodes_needing_physics_update){ for(QueuedNodePhysicsUpdate &update: m_nodes_needing_physics_update){
uint node_id = update.node_id; uint node_id = update.node_id;
sp_<pv::RawVolume<VoxelInstance>> volume = update.volume;
Node *n = scene->GetNode(node_id); Node *n = scene->GetNode(node_id);
if(!n){ if(!n){
log_w(MODULE, "on_tick(): Node physics update: " log_w(MODULE, "on_tick(): Node physics update: "
"Node %i not found", node_id); "Node %i not found", node_id);
return; return;
} }
// Get volume
const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
const PODVector<unsigned char> &rawbuf = var.GetBuffer();
ss_ data((const char*)&rawbuf[0], rawbuf.Size());
up_<pv::RawVolume<VoxelInstance>> volume =
interface::deserialize_volume(data);
// Update collision shape // Update collision shape
interface::mesh::set_voxel_physics_boxes(n, context, *volume, interface::mesh::set_voxel_physics_boxes(n, context, *volume,
m_voxel_reg.get()); m_voxel_reg.get());
} }
m_nodes_needing_physics_update.clear(); m_nodes_needing_physics_update.clear();
}); });
// Unload stuff if needed
maintain_maximum_buffer_limit();
} }
void on_files_transmitted(const client_file::FilesTransmitted &event) void on_files_transmitted(const client_file::FilesTransmitted &event)
@ -543,7 +576,7 @@ struct Module: public interface::Module, public voxelworld::Interface
} }
} }
run_commit_hooks_in_thread(chunk_p, volume); run_commit_hooks_in_thread(chunk_p, *volume);
ss_ data = interface::serialize_volume_compressed(*volume); ss_ data = interface::serialize_volume_compressed(*volume);
n->SetVar(StringHash("buildat_voxel_data"), Variant( n->SetVar(StringHash("buildat_voxel_data"), Variant(
@ -551,8 +584,8 @@ struct Module: public interface::Module, public voxelworld::Interface
run_commit_hooks_in_scene(chunk_p, n); run_commit_hooks_in_scene(chunk_p, n);
// TODO: Remove this hook callback and use an event instead m_server->emit_event("voxelworld:node_volume_updated",
run_commit_hooks_after_commit(chunk_p); new NodeVolumeUpdated(n->GetID(), true, chunk_p));
// There are no collision shapes initially, but add the rigid body now // There are no collision shapes initially, but add the rigid body now
RigidBody *body = n->CreateComponent<RigidBody>(LOCAL); RigidBody *body = n->CreateComponent<RigidBody>(LOCAL);
@ -603,10 +636,9 @@ struct Module: public interface::Module, public voxelworld::Interface
new GenerationRequest(section_p)); new GenerationRequest(section_p));
} }
void mark_node_for_physics_update(uint node_id, void mark_node_for_physics_update(uint node_id)
sp_<pv::RawVolume<VoxelInstance>> volume)
{ {
QueuedNodePhysicsUpdate update(node_id, volume); QueuedNodePhysicsUpdate update(node_id);
auto it = std::lower_bound(m_nodes_needing_physics_update.begin(), auto it = std::lower_bound(m_nodes_needing_physics_update.begin(),
m_nodes_needing_physics_update.end(), update, m_nodes_needing_physics_update.end(), update,
std::greater<QueuedNodePhysicsUpdate>()); std::greater<QueuedNodePhysicsUpdate>());
@ -623,7 +655,7 @@ struct Module: public interface::Module, public voxelworld::Interface
// modify the volume // modify the volume
void run_commit_hooks_in_thread( void run_commit_hooks_in_thread(
const pv::Vector3DInt32 &chunk_p, const pv::Vector3DInt32 &chunk_p,
sp_<pv::RawVolume<VoxelInstance>> volume) pv::RawVolume<VoxelInstance> &volume)
{ {
for(up_<CommitHook> &hook : m_commit_hooks) for(up_<CommitHook> &hook : m_commit_hooks)
hook->in_thread(this, chunk_p, volume); hook->in_thread(this, chunk_p, volume);
@ -636,10 +668,38 @@ struct Module: public interface::Module, public voxelworld::Interface
hook->in_scene(this, chunk_p, n); hook->in_scene(this, chunk_p, n);
} }
void run_commit_hooks_after_commit(const pv::Vector3DInt32 &chunk_p) void unload_old_buffers(int64_t unload_timeout, size_t max_buffers)
{ {
for(up_<CommitHook> &hook : m_commit_hooks) // Swap out the current set
hook->after_commit(this, chunk_p); std::vector<Section*> sections_with_loaded_buffers;
sections_with_loaded_buffers.swap(m_sections_with_loaded_buffers);
// Allocate a new set to put back stuff that is still loaded
m_sections_with_loaded_buffers.reserve(
sections_with_loaded_buffers.size());
// Go through the swapped set, putting back sections that are still loaded
m_total_buffers_loaded = 0;
for(Section *section : sections_with_loaded_buffers){
size_t num_loaded = 0;
for(size_t i = 0; i < section->chunk_buffers.size(); i++){
ChunkBuffer &chunk_buffer = section->chunk_buffers[i];
bool unloaded = chunk_buffer.unload_if_old(unload_timeout);
if(!unloaded){
num_loaded++;
m_total_buffers_loaded++;
}
}
if(num_loaded > 0)
m_sections_with_loaded_buffers.push_back(section);
}
// Call recursively if too many buffers are still loaded
if(unload_timeout > 1000 && m_total_buffers_loaded > max_buffers)
unload_old_buffers(unload_timeout / 4, max_buffers);
}
void maintain_maximum_buffer_limit()
{
if(m_total_buffers_loaded > m_max_buffers_loaded)
unload_old_buffers(m_buffer_unload_timeout, m_max_buffers_loaded);
} }
// Interface // Interface
@ -740,18 +800,14 @@ struct Module: public interface::Module, public voxelworld::Interface
// TODO: Commit only the current chunk // TODO: Commit only the current chunk
commit(); commit();
// Volume will be used after access_scene()
sp_<pv::RawVolume<VoxelInstance>> volume;
m_server->access_scene([&](Scene *scene) m_server->access_scene([&](Scene *scene)
{ {
Node *n = scene->GetNode(node_id); Node *n = scene->GetNode(node_id);
const Variant &var = n->GetVar(StringHash("buildat_voxel_data")); const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
const PODVector<unsigned char> &buf = var.GetBuffer(); const PODVector<unsigned char> &buf = var.GetBuffer();
ss_ data((const char*)&buf[0], buf.Size()); ss_ data((const char*)&buf[0], buf.Size());
volume = sp_<pv::RawVolume<VoxelInstance>>(std::move( up_<pv::RawVolume<VoxelInstance>> volume =
interface::deserialize_volume(data) interface::deserialize_volume(data);
));
pv::Vector3DInt32 voxel_p( pv::Vector3DInt32 voxel_p(
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(), p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(),
@ -765,7 +821,7 @@ struct Module: public interface::Module, public voxelworld::Interface
PV3I_PARAMS(section_p), PV3I_PARAMS(voxel_p)); PV3I_PARAMS(section_p), PV3I_PARAMS(voxel_p));
volume->setVoxelAt(voxel_p, v); volume->setVoxelAt(voxel_p, v);
run_commit_hooks_in_thread(chunk_p, volume); run_commit_hooks_in_thread(chunk_p, *volume);
ss_ new_data = interface::serialize_volume_compressed(*volume); ss_ new_data = interface::serialize_volume_compressed(*volume);
@ -777,10 +833,10 @@ struct Module: public interface::Module, public voxelworld::Interface
}); });
// Mark node for collision box update // Mark node for collision box update
mark_node_for_physics_update(node_id, volume); mark_node_for_physics_update(node_id);
// TODO: Remove this hook callback and use an event instead m_server->emit_event("voxelworld:node_volume_updated",
run_commit_hooks_after_commit(chunk_p); new NodeVolumeUpdated(node_id, true, chunk_p));
} }
void set_voxel(const pv::Vector3DInt32 &p, const interface::VoxelInstance &v, void set_voxel(const pv::Vector3DInt32 &p, const interface::VoxelInstance &v,
@ -803,8 +859,12 @@ struct Module: public interface::Module, public voxelworld::Interface
return; return;
} }
// Unload stuff if needed
maintain_maximum_buffer_limit();
// Set in buffer // Set in buffer
ChunkBuffer &buf = section->get_buffer(chunk_p, m_server); ChunkBuffer &buf = section->get_buffer(chunk_p, m_server,
&m_total_buffers_loaded);
if(!buf.volume){ if(!buf.volume){
if(!disable_warnings){ if(!disable_warnings){
log_w(MODULE, "set_voxel() p=" PV3I_FORMAT ", v=%i: Couldn't get " log_w(MODULE, "set_voxel() p=" PV3I_FORMAT ", v=%i: Couldn't get "
@ -833,19 +893,20 @@ struct Module: public interface::Module, public voxelworld::Interface
} }
// Commit and unload chunk buffer // Commit and unload chunk buffer
// TODO: Unload after a timeout instead of always
void commit_chunk_buffer(Section *section, size_t chunk_i) void commit_chunk_buffer(Section *section, size_t chunk_i)
{ {
ChunkBuffer &chunk_buffer = section->chunk_buffers[chunk_i]; ChunkBuffer &chunk_buffer = section->chunk_buffers[chunk_i];
if(!chunk_buffer.dirty){ if(!chunk_buffer.dirty){
// No changes made; unload buffer volume and return // No changes
chunk_buffer.volume.reset();
return; return;
} }
pv::Vector3DInt32 chunk_p = section->get_chunk_p(chunk_i); pv::Vector3DInt32 chunk_p = section->get_chunk_p(chunk_i);
uint node_id = section->node_ids->getVoxelAt(chunk_p); uint node_id = section->node_ids->getVoxelAt(chunk_p);
log_d(MODULE, "commit_chunk_buffer(): Updating node %i volume (chunk "
PV3I_FORMAT ")", node_id, PV3I_PARAMS(chunk_p));
if(node_id == 0){ if(node_id == 0){
log_w(MODULE, "commit_chunk_buffer() chunk_i=%zu: " log_w(MODULE, "commit_chunk_buffer() chunk_i=%zu: "
"No node found for chunk " PV3I_FORMAT "No node found for chunk " PV3I_FORMAT
@ -855,7 +916,7 @@ struct Module: public interface::Module, public voxelworld::Interface
return; return;
} }
run_commit_hooks_in_thread(chunk_p, chunk_buffer.volume); run_commit_hooks_in_thread(chunk_p, *chunk_buffer.volume);
ss_ new_data = interface::serialize_volume_compressed( ss_ new_data = interface::serialize_volume_compressed(
*chunk_buffer.volume); *chunk_buffer.volume);
@ -900,21 +961,19 @@ struct Module: public interface::Module, public voxelworld::Interface
} }
network::access(m_server, [&](network::Interface *inetwork){ network::access(m_server, [&](network::Interface *inetwork){
for(auto &peer_id: peers){ for(auto &peer_id: peers){
inetwork->send(peer_id, "voxelworld:node_voxel_data_updated", inetwork->send(peer_id, "voxelworld:node_volume_updated",
os.str()); os.str());
} }
}); });
// Mark node for collision box update // Mark node for collision box update
mark_node_for_physics_update(node_id, chunk_buffer.volume); mark_node_for_physics_update(node_id);
// Reset dirty flag // Reset dirty flag
chunk_buffer.dirty = false; chunk_buffer.dirty = false;
// Unload buffer volume
chunk_buffer.volume.reset();
// TODO: Remove this hook callback and use an event instead m_server->emit_event("voxelworld:node_volume_updated",
run_commit_hooks_after_commit(chunk_p); new NodeVolumeUpdated(node_id, true, chunk_p));
} }
size_t num_buffers_loaded() size_t num_buffers_loaded()
@ -926,14 +985,13 @@ struct Module: public interface::Module, public voxelworld::Interface
{ {
if(m_sections_with_loaded_buffers.empty()) if(m_sections_with_loaded_buffers.empty())
return; return;
log_v(MODULE, "commit(): %zu sections have loaded buffers", log_d(MODULE, "commit(): %zu sections have loaded buffers",
m_sections_with_loaded_buffers.size()); m_sections_with_loaded_buffers.size());
for(Section *section : m_sections_with_loaded_buffers){ for(Section *section : m_sections_with_loaded_buffers){
for(size_t i = 0; i < section->chunk_buffers.size(); i++){ for(size_t i = 0; i < section->chunk_buffers.size(); i++){
commit_chunk_buffer(section, i); commit_chunk_buffer(section, i);
} }
} }
m_sections_with_loaded_buffers.clear();
} }
VoxelInstance get_voxel(const pv::Vector3DInt32 &p, bool disable_warnings) VoxelInstance get_voxel(const pv::Vector3DInt32 &p, bool disable_warnings)
@ -952,8 +1010,12 @@ struct Module: public interface::Module, public voxelworld::Interface
return VoxelInstance(interface::VOXELTYPEID_UNDEFINED); return VoxelInstance(interface::VOXELTYPEID_UNDEFINED);
} }
// Unload stuff if needed
maintain_maximum_buffer_limit();
// Get from buffer // Get from buffer
ChunkBuffer &buf = section->get_buffer(chunk_p, m_server); ChunkBuffer &buf = section->get_buffer(chunk_p, m_server,
&m_total_buffers_loaded);
if(!buf.volume){ if(!buf.volume){
if(!disable_warnings){ if(!disable_warnings){
log_w(MODULE, "get_voxel() p=" PV3I_FORMAT ": Couldn't get " log_w(MODULE, "get_voxel() p=" PV3I_FORMAT ": Couldn't get "