2014-10-01 19:00:59 +03:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
2014-10-03 11:08:58 +03:00
|
|
|
#include "replicate/api.h"
|
2014-10-01 19:00:59 +03:00
|
|
|
#include "core/log.h"
|
2014-10-02 10:20:44 +03:00
|
|
|
#include "network/api.h"
|
2014-10-01 19:00:59 +03:00
|
|
|
#include "interface/module.h"
|
|
|
|
#include "interface/server.h"
|
|
|
|
#include "interface/event.h"
|
|
|
|
#include "interface/tcpsocket.h"
|
|
|
|
#include "interface/packet_stream.h"
|
2014-10-01 20:11:09 +03:00
|
|
|
#include "interface/magic_event.h"
|
2014-10-01 19:00:59 +03:00
|
|
|
#include <Object.h>
|
|
|
|
#include <Context.h>
|
|
|
|
#include <Engine.h>
|
|
|
|
#include <Variant.h>
|
|
|
|
#include <Scene.h>
|
|
|
|
#include <ResourceCache.h>
|
|
|
|
#include <CoreEvents.h>
|
|
|
|
#include <SceneEvents.h>
|
2014-10-02 09:42:35 +03:00
|
|
|
#include <ReplicationState.h>
|
2014-10-02 10:28:58 +03:00
|
|
|
#include <Component.h>
|
2014-10-01 19:00:59 +03:00
|
|
|
#include <cereal/archives/portable_binary.hpp>
|
|
|
|
#include <cereal/types/vector.hpp>
|
|
|
|
#include <cereal/types/tuple.hpp>
|
|
|
|
|
|
|
|
using interface::Event;
|
|
|
|
namespace magic = Urho3D;
|
2014-10-02 10:20:44 +03:00
|
|
|
using magic::Node;
|
|
|
|
using magic::Scene;
|
2014-10-02 10:28:58 +03:00
|
|
|
using magic::Component;
|
2014-10-01 19:00:59 +03:00
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
namespace replicate {
|
2014-10-01 19:00:59 +03:00
|
|
|
|
2014-10-02 10:45:53 +03:00
|
|
|
ss_ dump(const magic::VectorBuffer &buf)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios::binary);
|
|
|
|
os<<"[";
|
2014-10-02 10:49:00 +03:00
|
|
|
for(size_t i = 0; i < buf.GetBuffer().Size(); i++){
|
|
|
|
if(i != 0)
|
2014-10-02 10:45:53 +03:00
|
|
|
os<<", ";
|
2014-10-02 10:49:00 +03:00
|
|
|
os<<cs(buf.GetBuffer().At(i));
|
2014-10-02 10:45:53 +03:00
|
|
|
}
|
|
|
|
os<<"]";
|
|
|
|
return os.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
ss_ buf_to_string(const magic::VectorBuffer &buf)
|
|
|
|
{
|
|
|
|
return ss_((const char*)&buf.GetBuffer().Front(), buf.GetBuffer().Size());
|
|
|
|
}
|
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
struct Module: public interface::Module, public replicate::Interface
|
2014-10-01 19:00:59 +03:00
|
|
|
{
|
|
|
|
interface::Server *m_server;
|
2014-10-02 12:09:38 +03:00
|
|
|
// 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)
|
2014-10-02 11:55:29 +03:00
|
|
|
sm_<network::PeerInfo::Id, magic::SceneReplicationState> m_scene_states;
|
2014-10-01 19:00:59 +03:00
|
|
|
|
|
|
|
Module(interface::Server *server):
|
2014-10-03 11:08:58 +03:00
|
|
|
interface::Module("replicate"),
|
2014-10-01 19:00:59 +03:00
|
|
|
m_server(server)
|
|
|
|
{
|
2014-10-03 11:08:58 +03:00
|
|
|
log_v(MODULE, "replicate construct");
|
2014-10-01 19:00:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
~Module()
|
|
|
|
{
|
2014-10-03 11:08:58 +03:00
|
|
|
log_v(MODULE, "replicate destruct");
|
2014-10-02 12:09:38 +03:00
|
|
|
m_server->access_scene([&](magic::Scene *scene){
|
|
|
|
for(auto &pair : m_scene_states){
|
|
|
|
magic::SceneReplicationState &scene_state = pair.second;
|
|
|
|
scene->CleanupConnection((magic::Connection*)&scene_state);
|
|
|
|
}
|
|
|
|
});
|
2014-10-01 19:00:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void init()
|
|
|
|
{
|
2014-10-03 11:08:58 +03:00
|
|
|
log_v(MODULE, "replicate init");
|
2014-10-01 19:00:59 +03:00
|
|
|
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:continue"));
|
2014-10-02 11:55:29 +03:00
|
|
|
m_server->sub_event(this, Event::t("network:new_client"));
|
2014-10-02 09:42:35 +03:00
|
|
|
m_server->sub_event(this, Event::t("core:tick"));
|
2014-10-01 19:00:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void event(const Event::Type &type, const Event::Private *p)
|
|
|
|
{
|
2014-10-02 11:55:29 +03:00
|
|
|
EVENT_VOIDN("core:start", on_start)
|
|
|
|
EVENT_VOIDN("core:unload", on_unload)
|
|
|
|
EVENT_VOIDN("core:continue", on_continue)
|
|
|
|
EVENT_TYPEN("network:new_client", on_new_client, network::NewClient)
|
|
|
|
EVENT_TYPEN("core:tick", on_tick, interface::TickEvent)
|
2014-10-01 19:00:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void on_start()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_unload()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_continue()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-10-02 11:55:29 +03:00
|
|
|
void on_new_client(const network::NewClient &new_client)
|
|
|
|
{
|
2014-10-03 13:21:24 +03:00
|
|
|
log_v(MODULE, "replicate::on_new_client: id=%zu", new_client.info.id);
|
2014-10-02 11:55:29 +03:00
|
|
|
}
|
|
|
|
|
2014-10-02 12:09:38 +03:00
|
|
|
void on_client_disconnected(const network::OldClient &old_client)
|
|
|
|
{
|
2014-10-03 13:21:24 +03:00
|
|
|
log_v(MODULE, "replicate::on_client_disconnected: id=%zu",
|
2014-10-02 12:09:38 +03:00
|
|
|
old_client.info.id);
|
|
|
|
auto peer = old_client.info.id;
|
|
|
|
auto it = m_scene_states.find(peer);
|
|
|
|
if(it == m_scene_states.end())
|
|
|
|
return;
|
|
|
|
magic::SceneReplicationState &scene_state = it->second;
|
|
|
|
m_server->access_scene([&](magic::Scene *scene){
|
|
|
|
// 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)
|
|
|
|
scene->CleanupConnection((magic::Connection*)&scene_state);
|
|
|
|
m_scene_states.erase(peer);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-10-02 09:42:35 +03:00
|
|
|
void on_tick(const interface::TickEvent &event)
|
|
|
|
{
|
2014-10-03 11:08:58 +03:00
|
|
|
log_d(MODULE, "replicate::on_tick");
|
2014-10-02 11:55:29 +03:00
|
|
|
|
|
|
|
sync_changes();
|
|
|
|
}
|
2014-10-02 10:20:44 +03:00
|
|
|
|
2014-10-02 11:55:29 +03:00
|
|
|
void sync_changes()
|
|
|
|
{
|
|
|
|
sv_<network::PeerInfo::Id> peers;
|
|
|
|
network::access(m_server, [&](network::Interface * inetwork){
|
|
|
|
peers = inetwork->list_peers();
|
|
|
|
});
|
2014-10-02 12:09:38 +03:00
|
|
|
m_server->access_scene([&](magic::Scene *scene){
|
2014-10-02 11:19:40 +03:00
|
|
|
// For a reference implementation of this kind of network
|
|
|
|
// synchronization, see Urho3D's Network/Connection.cpp
|
2014-10-02 12:37:48 +03:00
|
|
|
|
|
|
|
// Compare attributes and set replication states dirty as needed;
|
|
|
|
// this accesses every replication state for every node.
|
|
|
|
scene->PrepareNetworkUpdate();
|
|
|
|
|
|
|
|
// Send changes to each peer (each of which has its own replication
|
|
|
|
// state)
|
2014-10-02 11:55:29 +03:00
|
|
|
for(auto &peer : peers){
|
|
|
|
magic::SceneReplicationState &scene_state = m_scene_states[peer];
|
2014-10-02 11:09:29 +03:00
|
|
|
|
2014-10-02 11:55:29 +03:00
|
|
|
magic::HashSet<uint> nodes_to_process;
|
|
|
|
uint scene_id = scene->GetID();
|
|
|
|
nodes_to_process.Insert(scene_id);
|
2014-10-02 14:47:52 +03:00
|
|
|
sync_node(peer, scene_id, nodes_to_process, scene, scene_state);
|
2014-10-02 10:20:44 +03:00
|
|
|
|
2014-10-02 11:55:29 +03:00
|
|
|
nodes_to_process.Insert(scene_state.dirtyNodes_);
|
|
|
|
nodes_to_process.Erase(scene_id);
|
2014-10-02 10:20:44 +03:00
|
|
|
|
2014-10-02 11:55:29 +03:00
|
|
|
while(!nodes_to_process.Empty()){
|
|
|
|
uint node_id = nodes_to_process.Front();
|
2014-10-02 14:47:52 +03:00
|
|
|
sync_node(peer, node_id, nodes_to_process, scene, scene_state);
|
2014-10-02 11:55:29 +03:00
|
|
|
}
|
2014-10-02 10:20:44 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-10-02 14:47:52 +03:00
|
|
|
void sync_node(network::PeerInfo::Id peer,
|
2014-10-02 10:20:44 +03:00
|
|
|
uint node_id, magic::HashSet<uint> &nodes_to_process,
|
|
|
|
magic::Scene *scene, magic::SceneReplicationState &scene_state)
|
|
|
|
{
|
|
|
|
if(!nodes_to_process.Erase(node_id))
|
|
|
|
return;
|
2014-10-02 10:23:57 +03:00
|
|
|
log_d(MODULE, "sync_node(): node_id=%zu", node_id);
|
2014-10-02 10:20:44 +03:00
|
|
|
auto it = scene_state.nodeStates_.Find(node_id);
|
2014-10-02 10:45:53 +03:00
|
|
|
if(it == scene_state.nodeStates_.End()){
|
|
|
|
// New node
|
|
|
|
Node *n = scene->GetNode(node_id);
|
|
|
|
if(n){
|
2014-10-02 21:00:45 +03:00
|
|
|
sync_create_node(peer, n, nodes_to_process, scene, scene_state);
|
2014-10-02 10:45:53 +03:00
|
|
|
} else {
|
|
|
|
// Was already deleted
|
2014-10-02 11:09:29 +03:00
|
|
|
log_w(MODULE, "New node was already deleted: %zu", node_id);
|
2014-10-02 10:45:53 +03:00
|
|
|
scene_state.dirtyNodes_.Erase(node_id);
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-02 10:20:44 +03:00
|
|
|
// Existing node
|
|
|
|
magic::NodeReplicationState &node_state = it->second_;
|
|
|
|
Node *n = node_state.node_;
|
|
|
|
if(!n){
|
2014-10-02 14:47:52 +03:00
|
|
|
// Node was removed
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
buf.WriteNetID(node_id);
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:remove_node", buf);
|
2014-10-02 10:20:44 +03:00
|
|
|
} else {
|
2014-10-02 14:47:52 +03:00
|
|
|
sync_existing_node(peer, n, node_state, nodes_to_process,
|
2014-10-02 10:20:44 +03:00
|
|
|
scene, scene_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 21:00:45 +03:00
|
|
|
void sync_create_node(network::PeerInfo::Id peer, Node *node,
|
2014-10-02 10:20:44 +03:00
|
|
|
magic::HashSet<uint> &nodes_to_process,
|
|
|
|
Scene *scene, magic::SceneReplicationState &scene_state)
|
|
|
|
{
|
2014-10-03 13:21:24 +03:00
|
|
|
log_d(MODULE, "sync_create_node(): %zu", node->GetID());
|
2014-10-02 10:20:44 +03:00
|
|
|
auto &deps = node->GetDependencyNodes();
|
|
|
|
for(auto it = deps.Begin(); it != deps.End(); ++it){
|
|
|
|
uint node_id = (*it)->GetID();
|
|
|
|
if(scene_state.dirtyNodes_.Contains(node_id))
|
2014-10-02 14:47:52 +03:00
|
|
|
sync_node(peer, node_id, nodes_to_process, scene, scene_state);
|
2014-10-02 10:20:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
magic::NodeReplicationState &node_state =
|
|
|
|
scene_state.nodeStates_[node->GetID()];
|
2014-10-02 12:09:38 +03:00
|
|
|
node_state.connection_ = (magic::Connection*)&scene_state;
|
2014-10-02 10:20:44 +03:00
|
|
|
node_state.sceneState_ = &scene_state;
|
|
|
|
node_state.node_ = node;
|
|
|
|
node->AddReplicationState(&node_state);
|
|
|
|
|
2014-10-02 10:23:57 +03:00
|
|
|
magic::VectorBuffer buf;
|
|
|
|
buf.WriteNetID(node->GetID());
|
|
|
|
node->WriteInitialDeltaUpdate(buf);
|
|
|
|
|
2014-10-02 21:00:45 +03:00
|
|
|
// User variables
|
|
|
|
auto &vars = node->GetVars();
|
|
|
|
buf.WriteVLE(vars.Size());
|
|
|
|
for(auto it = vars.Begin(); it != vars.End(); ++it){
|
|
|
|
buf.WriteStringHash(it->first_);
|
|
|
|
buf.WriteVariant(it->second_);
|
|
|
|
};
|
2014-10-02 10:20:44 +03:00
|
|
|
|
2014-10-02 10:28:58 +03:00
|
|
|
// Components
|
|
|
|
buf.WriteVLE(node->GetNumNetworkComponents());
|
|
|
|
auto &components = node->GetComponents();
|
|
|
|
for(uint i = 0; i<components.Size(); i++){
|
|
|
|
Component *component = components[i];
|
|
|
|
if(component->GetID() >= magic::FIRST_LOCAL_ID)
|
|
|
|
continue;
|
|
|
|
magic::ComponentReplicationState &component_state =
|
|
|
|
node_state.componentStates_[component->GetID()];
|
2014-10-02 12:09:38 +03:00
|
|
|
component_state.connection_ = (magic::Connection*)&scene_state;
|
2014-10-02 10:28:58 +03:00
|
|
|
component_state.nodeState_ = &node_state;
|
|
|
|
component_state.component_ = component;
|
|
|
|
component->AddReplicationState(&component_state);
|
|
|
|
buf.WriteStringHash(component->GetType());
|
|
|
|
buf.WriteNetID(component->GetID());
|
|
|
|
component->WriteInitialDeltaUpdate(buf);
|
|
|
|
}
|
2014-10-02 10:20:44 +03:00
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:create_node", buf);
|
2014-10-02 10:20:44 +03:00
|
|
|
|
2014-10-02 10:45:53 +03:00
|
|
|
node_state.markedDirty_ = false;
|
|
|
|
scene_state.dirtyNodes_.Erase(node->GetID());
|
|
|
|
}
|
|
|
|
|
2014-10-02 14:47:52 +03:00
|
|
|
void sync_existing_node(network::PeerInfo::Id peer,
|
2014-10-02 10:45:53 +03:00
|
|
|
Node *node, magic::NodeReplicationState &node_state,
|
|
|
|
magic::HashSet<uint> &nodes_to_process,
|
|
|
|
Scene *scene, magic::SceneReplicationState &scene_state)
|
|
|
|
{
|
2014-10-03 13:21:24 +03:00
|
|
|
log_d(MODULE, "sync_existing_node(): %zu", node->GetID());
|
2014-10-02 10:45:53 +03:00
|
|
|
auto &deps = node->GetDependencyNodes();
|
|
|
|
for(auto it = deps.Begin(); it != deps.End(); ++it){
|
|
|
|
uint node_id = (*it)->GetID();
|
|
|
|
if(scene_state.dirtyNodes_.Contains(node_id))
|
2014-10-02 14:47:52 +03:00
|
|
|
sync_node(peer, node_id, nodes_to_process, scene, scene_state);
|
2014-10-02 10:45:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Handle changed attributes
|
|
|
|
if(node_state.dirtyAttributes_.Count()){
|
2014-10-03 13:21:24 +03:00
|
|
|
log_d(MODULE, "sync_existing_node(): %zu: Changed attributes",
|
2014-10-02 11:09:29 +03:00
|
|
|
node->GetID());
|
2014-10-02 10:45:53 +03:00
|
|
|
const magic::Vector<magic::AttributeInfo> &attributes =
|
|
|
|
*node->GetNetworkAttributes();
|
|
|
|
uint num = attributes.Size();
|
|
|
|
bool has_latest_data = false;
|
|
|
|
|
|
|
|
// ?
|
|
|
|
for(uint i = 0; i < num; i++){
|
|
|
|
if(node_state.dirtyAttributes_.IsSet(i) &&
|
|
|
|
(attributes.At(i).mode_ & magic::AM_LATESTDATA)){
|
|
|
|
has_latest_data = true;
|
|
|
|
node_state.dirtyAttributes_.Clear(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ?
|
|
|
|
if(has_latest_data){
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
|
|
|
|
buf.WriteNetID(node->GetID());
|
|
|
|
node->WriteLatestDataUpdate(buf);
|
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:latest_node_data", buf);
|
2014-10-02 10:45:53 +03:00
|
|
|
}
|
2014-10-02 10:49:00 +03:00
|
|
|
|
2014-10-02 11:09:29 +03:00
|
|
|
// ?
|
2014-10-02 10:49:00 +03:00
|
|
|
if(node_state.dirtyAttributes_.Count() || node_state.dirtyVars_.Size()){
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
|
|
|
|
buf.WriteNetID(node->GetID());
|
|
|
|
node->WriteDeltaUpdate(buf, node_state.dirtyAttributes_);
|
|
|
|
|
2014-10-02 11:19:40 +03:00
|
|
|
// Variables
|
2014-10-02 11:09:29 +03:00
|
|
|
buf.WriteVLE(node_state.dirtyVars_.Size());
|
|
|
|
const magic::VariantMap &vars = node->GetVars();
|
|
|
|
const magic::HashSet<magic::StringHash> &dirty_vars =
|
|
|
|
node_state.dirtyVars_;
|
|
|
|
for(auto it = dirty_vars.Begin(); it != dirty_vars.End(); ++it){
|
|
|
|
auto var_it = vars.Find(*it);
|
|
|
|
if(var_it != vars.End()){
|
|
|
|
buf.WriteStringHash(var_it->first_);
|
|
|
|
buf.WriteVariant(var_it->second_);
|
|
|
|
} else {
|
|
|
|
// Variable has been removed; send dummy data
|
|
|
|
buf.WriteStringHash(magic::StringHash());
|
|
|
|
buf.WriteVariant(magic::Variant::EMPTY);
|
|
|
|
}
|
|
|
|
}
|
2014-10-02 10:49:00 +03:00
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:node_delta_update", buf);
|
2014-10-02 10:49:00 +03:00
|
|
|
}
|
2014-10-02 11:09:29 +03:00
|
|
|
|
|
|
|
node_state.dirtyAttributes_.ClearAll();
|
|
|
|
node_state.dirtyVars_.Clear();
|
2014-10-02 10:45:53 +03:00
|
|
|
}
|
2014-10-02 11:09:29 +03:00
|
|
|
|
2014-10-02 11:19:40 +03:00
|
|
|
// Handle changed or removed components
|
|
|
|
magic::HashMap<unsigned, magic::ComponentReplicationState>
|
|
|
|
&component_states = node_state.componentStates_;
|
|
|
|
for(auto it = component_states.Begin(); it != component_states.End(); ){
|
|
|
|
auto current_it = it++;
|
|
|
|
uint component_id = current_it->first_;
|
|
|
|
auto &component_state = current_it->second_;
|
|
|
|
magic::Component *component = component_state.component_;
|
|
|
|
if(!component){
|
|
|
|
// Component was removed
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
buf.WriteNetID(component_id);
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:remove_component", buf);
|
2014-10-02 11:19:40 +03:00
|
|
|
component_states.Erase(current_it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Existing component
|
|
|
|
// Handle changed attributes
|
|
|
|
if(component_state.dirtyAttributes_.Count()){
|
2014-10-03 13:21:24 +03:00
|
|
|
log_d(MODULE, "sync_existing_node(): %zu: Changed attributes"
|
2014-10-02 11:19:40 +03:00
|
|
|
" in component %zu", node->GetID(), component_id);
|
|
|
|
const magic::Vector<magic::AttributeInfo> &attributes =
|
|
|
|
*component->GetNetworkAttributes();
|
|
|
|
uint num = attributes.Size();
|
|
|
|
bool has_latest_data = false;
|
|
|
|
|
|
|
|
// ?
|
|
|
|
for(uint i = 0; i < num; i++){
|
|
|
|
if(component_state.dirtyAttributes_.IsSet(i) &&
|
|
|
|
(attributes.At(i).mode_ & magic::AM_LATESTDATA)){
|
|
|
|
has_latest_data = true;
|
|
|
|
component_state.dirtyAttributes_.Clear(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ?
|
|
|
|
if(has_latest_data){
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
|
|
|
|
buf.WriteNetID(component->GetID());
|
|
|
|
component->WriteLatestDataUpdate(buf);
|
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:latest_component_data", buf);
|
2014-10-02 11:19:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ?
|
|
|
|
if(component_state.dirtyAttributes_.Count()){
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
|
|
|
|
buf.WriteNetID(component->GetID());
|
|
|
|
component->WriteDeltaUpdate(buf,
|
|
|
|
component_state.dirtyAttributes_);
|
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:component_delta_update", buf);
|
2014-10-02 11:19:40 +03:00
|
|
|
|
|
|
|
component_state.dirtyAttributes_.ClearAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 11:30:46 +03:00
|
|
|
// Handle new components
|
|
|
|
if(component_states.Size() != node->GetNumNetworkComponents()){
|
|
|
|
const magic::Vector<magic::SharedPtr<Component>>
|
|
|
|
&components = node->GetComponents();
|
|
|
|
for(uint i=0; i<components.Size(); i++){
|
|
|
|
Component *component = components[i];
|
|
|
|
if(component->GetID() >= magic::FIRST_LOCAL_ID)
|
|
|
|
continue;
|
|
|
|
auto it = component_states.Find(component->GetID());
|
|
|
|
if(it != component_states.End())
|
|
|
|
continue;
|
|
|
|
magic::ComponentReplicationState &component_state =
|
|
|
|
component_states[component->GetID()];
|
2014-10-02 12:09:38 +03:00
|
|
|
component_state.connection_ = (magic::Connection*)&scene_state;
|
2014-10-02 11:30:46 +03:00
|
|
|
component_state.nodeState_ = &node_state;
|
|
|
|
component_state.component_ = component;
|
|
|
|
component->AddReplicationState(&component_state);
|
|
|
|
|
|
|
|
magic::VectorBuffer buf;
|
|
|
|
buf.WriteNetID(component->GetID());
|
|
|
|
buf.WriteStringHash(component->GetType());
|
|
|
|
buf.WriteNetID(component->GetID());
|
|
|
|
component->WriteInitialDeltaUpdate(buf);
|
|
|
|
|
2014-10-03 11:08:58 +03:00
|
|
|
send_to_peer(peer, "replicate:create_component", buf);
|
2014-10-02 11:30:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 11:10:42 +03:00
|
|
|
node_state.markedDirty_ = false;
|
|
|
|
scene_state.dirtyNodes_.Erase(node->GetID());
|
2014-10-02 10:45:53 +03:00
|
|
|
}
|
|
|
|
|
2014-10-02 11:55:29 +03:00
|
|
|
void send_to_peer(network::PeerInfo::Id peer,
|
|
|
|
const ss_ &name, const magic::VectorBuffer &buf)
|
|
|
|
{
|
2014-10-03 13:21:24 +03:00
|
|
|
log_d(MODULE, "%s: Update size: %zu, data=%s",
|
2014-10-02 11:55:29 +03:00
|
|
|
cs(name), buf.GetBuffer().Size(), cs(dump(buf)));
|
|
|
|
ss_ data = buf_to_string(buf);
|
|
|
|
network::access(m_server, [&](network::Interface * inetwork){
|
|
|
|
inetwork->send(peer, name, data);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-10-02 14:47:52 +03:00
|
|
|
/*void send_to_all(const ss_ &name, const magic::VectorBuffer &buf)
|
2014-10-02 10:45:53 +03:00
|
|
|
{
|
|
|
|
log_i(MODULE, "%s: Update size: %zu, data=%s",
|
|
|
|
cs(name), buf.GetBuffer().Size(), cs(dump(buf)));
|
|
|
|
ss_ data = buf_to_string(buf);
|
2014-10-02 10:20:44 +03:00
|
|
|
network::access(m_server, [&](network::Interface * inetwork){
|
|
|
|
auto peers = inetwork->list_peers();
|
|
|
|
for(auto &peer : peers)
|
2014-10-02 10:45:53 +03:00
|
|
|
inetwork->send(peer, name, data);
|
2014-10-02 09:42:35 +03:00
|
|
|
});
|
2014-10-02 14:47:52 +03:00
|
|
|
}*/
|
2014-10-02 09:42:35 +03:00
|
|
|
|
2014-10-01 19:00:59 +03:00
|
|
|
// Interface
|
|
|
|
|
|
|
|
void* get_interface()
|
|
|
|
{
|
|
|
|
return dynamic_cast<Interface*>(this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
extern "C" {
|
2014-10-03 11:08:58 +03:00
|
|
|
EXPORT void* createModule_replicate(interface::Server *server){
|
2014-10-01 19:00:59 +03:00
|
|
|
return (void*)(new Module(server));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// vim: set noet ts=4 sw=4:
|