everything: voxelworld::Interface::set_voxel() and everything from there to updating geometry and physics on the client

This commit is contained in:
Perttu Ahola 2014-10-17 11:49:47 +03:00
parent 8f196bb0e6
commit 12465d8e3b
14 changed files with 298 additions and 47 deletions

View File

@ -251,8 +251,9 @@ struct Module: public interface::Module, public network::Interface
// Grab Peer (which contains socket)
auto it = m_peers.find(recipient);
if(it == m_peers.end()){
throw Exception(ss_()+"network::send(): Peer "+itos(recipient) +
" doesn't exist");
log_w(MODULE, "network::send(): Peer %i doesn't exist",
recipient);
return;
}
Peer &peer = it->second;

View File

@ -15,9 +15,15 @@ namespace Urho3D
namespace replicate
{
namespace magic = Urho3D;
using interface::Event;
typedef size_t PeerId;
struct Interface
{
virtual sv_<PeerId> find_peers_that_know_node(uint node_id) = 0;
virtual void emit_after_next_sync(Event event) = 0;
};
inline bool access(interface::Server *server,

View File

@ -55,7 +55,9 @@ struct Module: public interface::Module, public replicate::Interface
// NOTE: We use pointers to SceneReplicationStates as Connection pointers in
// other replication states in order to scene->CleanupConnection()
// without an actual Connection object (which we don't want to use)
sm_<network::PeerInfo::Id, magic::SceneReplicationState> m_scene_states;
sm_<PeerId, magic::SceneReplicationState> m_scene_states;
sv_<Event> m_events_to_emit_after_next_sync;
Module(interface::Server *server):
interface::Module("replicate"),
@ -137,11 +139,16 @@ struct Module: public interface::Module, public replicate::Interface
log_d(MODULE, "replicate::on_tick");
sync_changes();
for(Event &event : m_events_to_emit_after_next_sync){
m_server->emit_event(std::move(event));
}
m_events_to_emit_after_next_sync.clear();
}
void sync_changes()
{
sv_<network::PeerInfo::Id> peers;
sv_<PeerId> peers;
network::access(m_server, [&](network::Interface *inetwork){
peers = inetwork->list_peers();
});
@ -175,7 +182,7 @@ struct Module: public interface::Module, public replicate::Interface
});
}
void sync_node(network::PeerInfo::Id peer,
void sync_node(PeerId peer,
uint node_id, magic::HashSet<uint> &nodes_to_process,
magic::Scene *scene, magic::SceneReplicationState &scene_state)
{
@ -209,7 +216,7 @@ struct Module: public interface::Module, public replicate::Interface
}
}
void sync_create_node(network::PeerInfo::Id peer, Node *node,
void sync_create_node(PeerId peer, Node *node,
magic::HashSet<uint> &nodes_to_process,
Scene *scene, magic::SceneReplicationState &scene_state)
{
@ -264,7 +271,7 @@ struct Module: public interface::Module, public replicate::Interface
scene_state.dirtyNodes_.Erase(node->GetID());
}
void sync_existing_node(network::PeerInfo::Id peer,
void sync_existing_node(PeerId peer,
Node *node, magic::NodeReplicationState &node_state,
magic::HashSet<uint> &nodes_to_process,
Scene *scene, magic::SceneReplicationState &scene_state)
@ -278,9 +285,9 @@ struct Module: public interface::Module, public replicate::Interface
}
// Handle changed attributes
if(node_state.dirtyAttributes_.Count()){
log_d(MODULE, "sync_existing_node(): %zu: Changed attributes",
node->GetID());
if(node_state.dirtyAttributes_.Count() || node_state.dirtyVars_.Size()){
log_d(MODULE, "sync_existing_node(): %zu: Changed attributes "
"or variables", node->GetID());
const magic::Vector<magic::AttributeInfo> &attributes =
*node->GetNetworkAttributes();
uint num = attributes.Size();
@ -428,8 +435,7 @@ struct Module: public interface::Module, public replicate::Interface
scene_state.dirtyNodes_.Erase(node->GetID());
}
void send_to_peer(network::PeerInfo::Id peer,
const ss_ &name, const magic::VectorBuffer &buf)
void send_to_peer(PeerId peer, const ss_ &name, const magic::VectorBuffer &buf)
{
log_d(MODULE, "%s: Update size: %zu, data=%s",
cs(name), buf.GetBuffer().Size());
@ -453,6 +459,26 @@ struct Module: public interface::Module, public replicate::Interface
// Interface
sv_<PeerId> find_peers_that_know_node(uint node_id)
{
sv_<PeerId> result;
for(auto &pair: m_scene_states){
PeerId peer_id = pair.first;
magic::SceneReplicationState &scene_state = pair.second;
auto &node_states = scene_state.nodeStates_;
auto it = node_states.Find(node_id);
if(it != node_states.End()){
result.push_back(peer_id);
}
}
return result;
}
void emit_after_next_sync(Event event)
{
m_events_to_emit_after_next_sync.push_back(std::move(event));
}
void* get_interface()
{
return dynamic_cast<Interface*>(this);

View File

@ -31,6 +31,13 @@ namespace voxelworld
{}
};
struct NodeVoxelDataUpdatedEvent: public interface::Event::Private
{
uint node_id;
NodeVoxelDataUpdatedEvent(uint node_id): node_id(node_id){}
};
struct Interface
{
virtual void load_or_generate_section(

View File

@ -42,6 +42,25 @@ M.section_size_voxels = nil
function M.init()
log:info("voxelworld.init()")
local node_update_queue = buildat.SpatialUpdateQueue()
local function queue_initial_node_update(node)
-- TODO: node:GetWorldPosition() is not at center of node
node_update_queue:put(node:GetWorldPosition(),
INITIAL_GEOMETRY_NEAR_WEIGHT, camera_far_clip * 1.2,
nil, nil, {
type = "geometry",
current_lod = 0,
node_id = node:GetID(),
})
-- TODO: node:GetWorldPosition() is not at center of node
node_update_queue:put(node:GetWorldPosition(),
INITIAL_PHYSICS_NEAR_WEIGHT, PHYSICS_DISTANCE, nil, nil, {
type = "physics",
node_id = node:GetID(),
})
end
buildat.sub_packet("voxelworld:init", function(data)
local values = cereal.binary_input(data, {"object",
{"chunk_size_voxels", {"object",
@ -55,15 +74,21 @@ function M.init()
{"z", "int32_t"},
}},
})
log:info(dump(values))
log:info("voxelworld:init: "..dump(values))
M.chunk_size_voxels = buildat.Vector3(values.chunk_size_voxels)
M.section_size_chunks = buildat.Vector3(values.section_size_chunks)
M.section_size_voxels =
M.chunk_size_voxels:mul_components(M.section_size_chunks)
end)
--local node_update_queue = SpatialUpdateQueue()
local node_update_queue = buildat.SpatialUpdateQueue()
buildat.sub_packet("voxelworld:node_voxel_data_updated", function(data)
local values = cereal.binary_input(data, {"object",
{"node_id", "int32_t"},
})
log:info("voxelworld:node_voxel_data_updated: "..dump(values))
local node = replicate.main_scene:GetNode(values.node_id)
queue_initial_node_update(node)
end)
local function update_voxel_geometry(node)
local data = node:GetVar("buildat_voxel_data"):GetBuffer()
@ -258,21 +283,7 @@ function M.init()
replicate.sub_sync_node_added({}, function(node)
if not node:GetVar("buildat_voxel_data"):IsEmpty() then
-- TODO: node:GetWorldPosition() is not at center of node
node_update_queue:put(node:GetWorldPosition(),
INITIAL_GEOMETRY_NEAR_WEIGHT, camera_far_clip * 1.2,
nil, nil, {
type = "geometry",
current_lod = 0,
node_id = node:GetID(),
})
-- Create physics stuff when node comes closer than 100
-- TODO: node:GetWorldPosition() is not at center of node
node_update_queue:put(node:GetWorldPosition(),
INITIAL_PHYSICS_NEAR_WEIGHT, PHYSICS_DISTANCE, nil, nil, {
type = "physics",
node_id = node:GetID(),
})
queue_initial_node_update(node)
end
--local name = node:GetName()
end)

View File

@ -3,6 +3,7 @@
#include "voxelworld/api.h"
#include "network/api.h"
#include "client_file/api.h"
#include "replicate/api.h"
#include "core/log.h"
#include "interface/module.h"
#include "interface/server.h"
@ -309,6 +310,7 @@ struct Module: public interface::Module, public voxelworld::Interface
m_server->sub_event(this, Event::t("client_file:files_transmitted"));
m_server->sub_event(this, Event::t(
"network:packet_received/voxelworld:get_section"));
m_server->sub_event(this, Event::t("voxelworld:node_voxel_data_updated"));
m_server->access_scene([&](Scene *scene)
{
@ -427,6 +429,8 @@ struct Module: public interface::Module, public voxelworld::Interface
client_file::FilesTransmitted)
EVENT_TYPEN("network:packet_received/voxelworld:get_section",
on_get_section, network::Packet)
EVENT_TYPEN("voxelworld:node_voxel_data_updated",
on_node_voxel_data_updated, voxelworld::NodeVoxelDataUpdatedEvent)
}
void on_start()
@ -566,6 +570,29 @@ struct Module: public interface::Module, public voxelworld::Interface
packet.sender, PV3I_PARAMS(section_p));
}
void on_node_voxel_data_updated(const NodeVoxelDataUpdatedEvent &event)
{
// NOTE: This delayed event is used so that when this is received,
// replicate has already sent the data to clients
// Notify clients that know the node
sv_<replicate::PeerId> peers;
replicate::access(m_server, [&](replicate::Interface *ireplicate){
peers = ireplicate->find_peers_that_know_node(event.node_id);
});
std::ostringstream os(std::ios::binary);
{
cereal::PortableBinaryOutputArchive ar(os);
ar((int32_t)event.node_id);
}
network::access(m_server, [&](network::Interface *inetwork){
for(auto &peer_id : peers){
inetwork->send(peer_id, "voxelworld:node_voxel_data_updated",
os.str());
}
});
}
// Get section if exists
Section* get_section(const pv::Vector3DInt16 &section_p)
{
@ -929,6 +956,13 @@ struct Module: public interface::Module, public voxelworld::Interface
new_data.size())));
});
// Tell replicate to emit events once it has done its job
replicate::access(m_server, [&](replicate::Interface *ireplicate){
ireplicate->emit_after_next_sync(Event(
"voxelworld:node_voxel_data_updated",
new NodeVoxelDataUpdatedEvent(node_id)));
});
// Mark node for collision box update
mark_node_for_physics_update(node_id, chunk_buffer.volume);

View File

@ -95,6 +95,24 @@ magic.SubscribeToEvent("KeyDown", function(event_type, event_data)
end
end)
magic.SubscribeToEvent("MouseButtonDown", function(event_type, event_data)
local button = event_data:GetInt("Button")
log:info(""..button)
if button == 1 then
local p = player_node.position
local data = cereal.binary_output({
p = {x = p.x, y = p.y, z = p.z},
}, {"object",
{"p", {"object",
{"x", "int32_t"},
{"y", "int32_t"},
{"z", "int32_t"},
}},
})
buildat.send_packet("main:place_voxel", data)
end
end)
magic.SubscribeToEvent("Update", function(event_type, event_data)
--log:info("Update")
if player_node then

View File

@ -23,14 +23,34 @@
#include <cereal/types/unordered_map.hpp>
#include <cereal/types/vector.hpp>
namespace digger {
namespace magic = Urho3D;
namespace pv = PolyVox;
using interface::Event;
using interface::VoxelInstance;
// TODO: Move to a header (core/types_polyvox.h or something)
#define PV3I_FORMAT "(%i, %i, %i)"
#define PV3I_PARAMS(p) p.getX(), p.getY(), p.getZ()
// TODO: Move to a header (core/cereal_polyvox.h or something)
namespace cereal {
template<class Archive>
void save(Archive &archive, const pv::Vector3DInt32 &v){
archive((int32_t)v.getX(), (int32_t)v.getY(), (int32_t)v.getZ());
}
template<class Archive>
void load(Archive &archive, pv::Vector3DInt32 &v){
int32_t x, y, z;
archive(x, y, z);
v.setX(x); v.setY(y); v.setZ(z);
}
}
namespace digger {
using namespace Urho3D;
struct Module: public interface::Module
@ -53,6 +73,8 @@ struct Module: public interface::Module
m_server->sub_event(this, Event::t("core:tick"));
m_server->sub_event(this, Event::t("client_file:files_transmitted"));
m_server->sub_event(this, Event::t("voxelworld:generation_request"));
m_server->sub_event(this, Event::t(
"network:packet_received/main:place_voxel"));
}
void event(const Event::Type &type, const Event::Private *p)
@ -64,6 +86,8 @@ struct Module: public interface::Module
on_files_transmitted, client_file::FilesTransmitted)
EVENT_TYPEN("voxelworld:generation_request",
on_generation_request, voxelworld::GenerationRequest)
EVENT_TYPEN("network:packet_received/main:place_voxel",
on_place_voxel, network::Packet)
}
void on_start()
@ -251,20 +275,37 @@ struct Module: public interface::Module
for(int y1=y; y1<y+4; y1++){
pv::Vector3DInt32 p(x, y1, z);
ivoxelworld->set_voxel(p, VoxelInstance(3));
ivoxelworld->set_voxel(p, VoxelInstance(3), true);
}
for(int x1 = x-2; x1 <= x+2; x1++){
for(int y1 = y+3; y1 <= y+7; y1++){
for(int z1 = z-2; z1 <= z+2; z1++){
pv::Vector3DInt32 p(x1, y1, z1);
ivoxelworld->set_voxel(p, VoxelInstance(5));
ivoxelworld->set_voxel(p, VoxelInstance(5), true);
}
}
}
}
});
}
void on_place_voxel(const network::Packet &packet)
{
pv::Vector3DInt32 voxel_p;
{
std::istringstream is(packet.data, std::ios::binary);
cereal::PortableBinaryInputArchive ar(is);
ar(voxel_p);
}
log_v(MODULE, "C%i: on_place_voxel(): p=" PV3I_FORMAT,
packet.sender, PV3I_PARAMS(voxel_p));
voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld)
{
ivoxelworld->set_voxel(voxel_p, VoxelInstance(2));
});
}
};
extern "C" {

View File

@ -417,10 +417,11 @@ void CState::setup_packet_handlers()
uint node_id = msg.ReadNetID();
Node *node = scene->GetNode(node_id);
if(node){
log_d(MODULE, "Updating node %i", node_id);
log_d(MODULE, "Updating node %i (LatestDataUpdate)", node_id);
node->ReadLatestDataUpdate(msg);
} else {
log_w(MODULE, "Out-of-order node data ignored for %i", node_id);
// Note: Network/Connection.cpp would buffer this
}
};
@ -432,36 +433,55 @@ void CState::setup_packet_handlers()
uint c_id = msg.ReadNetID();
Component *c = scene->GetComponent(c_id);
if(c){
log_d(MODULE, "Updating component %i", c_id);
log_d(MODULE, "Updating component %i (LatestDataUpdate)", c_id);
c->ReadLatestDataUpdate(msg);
c->ApplyAttributes();
} else {
log_w(MODULE, "Out-of-order component data ignored for %i", c_id);
// Note: Network/Connection.cpp would buffer this
}
};
m_packet_handlers["replicate:node_delta_update"] =
[this](const ss_ &packet_name, const ss_ &data)
{
log_w("TODO: %s", cs(packet_name));
magic::Scene *scene = m_app->get_scene();
magic::MemoryBuffer msg(data.c_str(), data.size());
uint node_id = msg.ReadNetID();
Node *node = scene->GetNode(node_id);
if(node){
log_d(MODULE, "Updating node %i (DeltaUpdate)", node_id);
node->ReadDeltaUpdate(msg);
} else {
log_w(MODULE, "Out-of-order node data ignored for %i", node_id);
// Note: Network/Connection.cpp would NOT buffer this
}
// Read user variables
uint num_vars = msg.ReadVLE();
while(num_vars){
auto key = msg.ReadStringHash();
node->SetVar(key, msg.ReadVariant());
num_vars--;
}
};
m_packet_handlers["replicate:component_delta_update"] =
[this](const ss_ &packet_name, const ss_ &data)
{
log_w("TODO: %s", cs(packet_name));
log_w(MODULE, "TODO: %s", cs(packet_name));
// Note: Network/Connection.cpp would NOT buffer this
};
m_packet_handlers["replicate:remove_node"] =
[this](const ss_ &packet_name, const ss_ &data)
{
log_w("TODO: %s", cs(packet_name));
log_w(MODULE, "TODO: %s", cs(packet_name));
};
m_packet_handlers["replicate:remove_component"] =
[this](const ss_ &packet_name, const ss_ &data)
{
log_w("TODO: %s", cs(packet_name));
log_w(MODULE, "TODO: %s", cs(packet_name));
};
m_packet_handlers[""] =

View File

@ -472,7 +472,7 @@ void set_voxel_geometry(CustomGeometry *cg, Context *context,
AtlasSegmentReference seg_ref = voxel_def0->textures[face_id];
if(seg_ref.atlas_id == interface::ATLAS_UNDEFINED){
// This is usually intentional for invisible voxels
log_t(MODULE, "Voxel %i face %i atlas undefined", voxel_id0, face_id);
//log_t(MODULE, "Voxel %i face %i atlas undefined", voxel_id0, face_id);
continue;
}
const AtlasSegmentCache *aseg = atlas_reg->get_texture(seg_ref);
@ -686,7 +686,7 @@ void set_voxel_lod_geometry(int lod, CustomGeometry *cg, Context *context,
AtlasSegmentReference seg_ref = voxel_def0->lod_textures[lod_i][face_id];
if(seg_ref.atlas_id == interface::ATLAS_UNDEFINED){
// This is usually intentional for invisible voxels
log_t(MODULE, "Voxel %i face %i atlas undefined", voxel_id0, face_id);
//log_t(MODULE, "Voxel %i face %i atlas undefined", voxel_id0, face_id);
continue;
}
const AtlasSegmentCache *aseg = atlas_reg->get_texture(seg_ref);

View File

@ -20,7 +20,7 @@ void PacketStream::input(std::deque<char> &socket_buffer,
(socket_buffer[3] & 0xff)<<8 |
(socket_buffer[4] & 0xff)<<16 |
(socket_buffer[5] & 0xff)<<24;
log_d(MODULE, "size=%zu", size);
//log_d(MODULE, "size=%zu", size);
if(socket_buffer.size() < 6 + size)
return;
log_d(MODULE, "Received full packet; type=%zu, "

View File

@ -24,6 +24,8 @@
namespace interface
{
// NOTE: Event has no copy constructor due to up_<Private> p; just pass it
// by non-const value if it will be placed in a container by the receiver.
struct Event
{
typedef size_t Type;

View File

@ -46,7 +46,7 @@ static int l_mkdir(lua_State *L)
// pcall(function) -> status, error
static int l_pcall(lua_State *L)
{
log_d(MODULE, "l_pcall()");
log_t(MODULE, "l_pcall() begin");
lua_pushcfunction(L, handle_error);
int handle_error_stack_i = lua_gettop(L);
@ -54,7 +54,7 @@ static int l_pcall(lua_State *L)
int r = lua_pcall(L, 0, 0, handle_error_stack_i);
int error_stack_i = lua_gettop(L);
if(r == 0){
log_d(MODULE, "l_pcall() returned 0 (no error)");
log_t(MODULE, "l_pcall() returned 0 (no error)");
lua_pushboolean(L, true);
return 1;
}

View File

@ -32,7 +32,7 @@ namespace lua_bindings {
struct SpatialUpdateQueue
{
struct Value { // Describes an update
struct Value {
ss_ type;
uint32_t node_id = -1;
};
@ -50,12 +50,75 @@ struct SpatialUpdateQueue
bool operator>(const Item &other) const;
};
// Set of values with iterators for fast access of items, so that items
// can be removed quickly by value
struct ValueSet
{
struct Entry
{
Value value;
std::list<Item>::iterator it;
Entry(const Value &value, std::list<Item>::iterator it =
std::list<Item>::iterator()):
value(value), it(it)
{}
bool operator>(const Entry &other) const {
if(value.node_id > other.value.node_id)
return true;
if(value.node_id < other.value.node_id)
return false;
return value.type > other.value.type;
}
};
// sorted list in descending node_id and type order
std::list<Entry> m_set;
void clear(){
m_set.clear();
}
void insert(const Value &value, std::list<Item>::iterator queue_it){
Entry entry(value, queue_it);
auto it = std::lower_bound(m_set.begin(), m_set.end(), entry,
std::greater<Entry>());
if(it == m_set.end())
m_set.insert(it, entry);
else if(it->value.node_id != value.node_id ||
it->value.type != value.type)
m_set.insert(it, entry);
else
*it = entry;
}
void remove(const Value &value){
Entry entry(value);
auto it = std::lower_bound(m_set.begin(), m_set.end(), entry,
std::greater<Entry>());
if(it == m_set.end())
return;
m_set.erase(it);
}
std::list<Item>::iterator* find(const Value &value){
Entry entry(value);
auto it = std::lower_bound(m_set.begin(), m_set.end(), entry,
std::greater<Entry>());
if(it == m_set.end())
return nullptr;
if(it->value.node_id != value.node_id ||
it->value.type != value.type)
return nullptr;
return &(it->it);
}
};
Vector3 m_p;
Vector3 m_queue_oldest_p;
size_t m_queue_length = 0; // GCC std::list's size() is O(n)
std::list<Item> m_queue;
std::list<Item> m_old_queue;
ValueSet m_value_set;
void update(int max_operations)
{
if(m_old_queue.empty())
@ -76,6 +139,7 @@ struct SpatialUpdateQueue
m_p = p;
if(m_old_queue.empty() && (m_p - m_queue_oldest_p).Length() > 20){
m_queue_length = 0;
m_value_set.clear();
m_old_queue.swap(m_queue);
m_queue_oldest_p = m_p;
}
@ -109,10 +173,27 @@ struct SpatialUpdateQueue
if(item.f == -1.0f || item.fw == -1.0f)
throw Exception("item.f == -1.0f || item.fw == -1.0f");
// Find old entry; if the old entry is more important, discard the new
// one; if the old entry is less important, remove the old entry
std::list<Item>::iterator *itp = m_value_set.find(item.value);
if(itp != nullptr){
Item &old_item = **itp;
if(old_item.fw < item.fw){
// Old item is more important
return;
} else {
// New item is more important
m_value_set.remove(item.value);
m_queue.erase(*itp);
m_queue_length--;
}
}
auto it = std::lower_bound(m_queue.begin(), m_queue.end(),
item, std::greater<Item>()); // position in descending order
m_queue.insert(it, item);
auto inserted_it = m_queue.insert(it, item);
m_queue_length++;
m_value_set.insert(item.value, inserted_it);
}
void put(const Vector3 &p, float near_weight, float near_trigger_d,
@ -135,6 +216,10 @@ struct SpatialUpdateQueue
void pop()
{
if(m_queue.empty())
throw Exception("SpatialUpdateQueue::pop(): Empty");
Item &item = m_queue.back();
m_value_set.remove(item.value);
m_queue.pop_back();
m_queue_length--;
}