builtin/main_context, builtin/replicate: New multi-scene handling
This commit is contained in:
parent
702528b542
commit
3f6bcb6228
@ -18,6 +18,9 @@ namespace main_context
|
||||
namespace magic = Urho3D;
|
||||
using interface::Event;
|
||||
|
||||
struct OpaqueSceneReference;
|
||||
typedef OpaqueSceneReference* SceneReference;
|
||||
|
||||
struct Interface
|
||||
{
|
||||
// NOTE: Do not store Urho3D::SharedPtr<>s or any other kinds of pointers
|
||||
@ -25,7 +28,11 @@ namespace main_context
|
||||
// thread-safe.
|
||||
|
||||
virtual magic::Context* get_context() = 0;
|
||||
virtual magic::Scene* get_scene() = 0;
|
||||
virtual magic::Scene* find_scene(SceneReference ref) = 0;
|
||||
virtual magic::Scene* get_scene(SceneReference ref) = 0;
|
||||
|
||||
virtual SceneReference create_scene() = 0;
|
||||
virtual void delete_scene(SceneReference ref) = 0;
|
||||
|
||||
virtual void sub_magic_event(
|
||||
const magic::StringHash &event_type,
|
||||
|
@ -24,24 +24,26 @@
|
||||
#include <Log.h>
|
||||
#include <IOEvents.h> // E_LOGMESSAGE
|
||||
#include <Thread.h>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#define MODULE "main_context"
|
||||
|
||||
namespace main_context {
|
||||
|
||||
using interface::Event;
|
||||
using namespace Urho3D;
|
||||
|
||||
class BuildatResourceRouter: public magic::ResourceRouter
|
||||
class BuildatResourceRouter: public ResourceRouter
|
||||
{
|
||||
OBJECT(BuildatResourceRouter);
|
||||
|
||||
interface::Server *m_server;
|
||||
public:
|
||||
BuildatResourceRouter(magic::Context *context, interface::Server *server):
|
||||
magic::ResourceRouter(context),
|
||||
BuildatResourceRouter(Context *context, interface::Server *server):
|
||||
ResourceRouter(context),
|
||||
m_server(server)
|
||||
{}
|
||||
void Route(magic::String &name, magic::ResourceRequest requestType)
|
||||
void Route(String &name, ResourceRequest requestType)
|
||||
{
|
||||
ss_ path = m_server->get_file_path(name.CString());
|
||||
if(path == ""){
|
||||
@ -55,17 +57,37 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct SceneSetItem
|
||||
{
|
||||
SharedPtr<Scene> scene;
|
||||
void *raw_ptr = nullptr; // If scene is not set, this is used for searching
|
||||
|
||||
SceneSetItem() {}
|
||||
SceneSetItem(SharedPtr<Scene> scene): scene(scene) {}
|
||||
SceneSetItem(void *raw_ptr): raw_ptr(raw_ptr) {}
|
||||
void* get_raw_ptr() const {
|
||||
if(scene) return scene.Get();
|
||||
return raw_ptr;
|
||||
}
|
||||
bool operator>(const SceneSetItem &other) const {
|
||||
return get_raw_ptr() > other.get_raw_ptr();
|
||||
}
|
||||
};
|
||||
|
||||
struct Module: public interface::Module, public main_context::Interface
|
||||
{
|
||||
interface::Server *m_server;
|
||||
magic::SharedPtr<magic::Context> m_context;
|
||||
magic::SharedPtr<magic::Engine> m_engine;
|
||||
magic::SharedPtr<magic::Scene> m_scene;
|
||||
sm_<Event::Type, magic::SharedPtr<interface::MagicEventHandler>
|
||||
> m_magic_event_handlers;
|
||||
|
||||
uint64_t profiler_last_print_us = 0;
|
||||
|
||||
SharedPtr<Context> m_context;
|
||||
SharedPtr<Engine> m_engine;
|
||||
|
||||
// Set of scenes as a sorted array in descending address order
|
||||
sv_<SceneSetItem> m_scenes;
|
||||
|
||||
sm_<Event::Type, SharedPtr<
|
||||
interface::MagicEventHandler>> m_magic_event_handlers;
|
||||
|
||||
Module(interface::Server *server):
|
||||
interface::Module(MODULE),
|
||||
m_server(server),
|
||||
@ -89,11 +111,11 @@ struct Module: public interface::Module, public main_context::Interface
|
||||
|
||||
// Initialize Urho3D
|
||||
|
||||
m_context = new magic::Context();
|
||||
m_engine = new magic::Engine(m_context);
|
||||
m_context = new Context();
|
||||
m_engine = new Engine(m_context);
|
||||
|
||||
// Disable timestamps in Urho3D log message events
|
||||
magic::Log *magic_log = m_context->GetSubsystem<magic::Log>();
|
||||
Log *magic_log = m_context->GetSubsystem<Log>();
|
||||
magic_log->SetTimeStamp(false);
|
||||
|
||||
const interface::ServerConfig &server_config = m_server->get_config();
|
||||
@ -110,7 +132,7 @@ struct Module: public interface::Module, public main_context::Interface
|
||||
resource_paths_s += fs->get_absolute_path(path);
|
||||
}
|
||||
|
||||
magic::VariantMap params;
|
||||
VariantMap params;
|
||||
params["ResourcePaths"] = resource_paths_s.c_str();
|
||||
params["Headless"] = true;
|
||||
params["LogName"] = ""; // Don't log to file
|
||||
@ -118,24 +140,13 @@ struct Module: public interface::Module, public main_context::Interface
|
||||
if(!m_engine->Initialize(params))
|
||||
throw Exception("Urho3D engine initialization failed");
|
||||
|
||||
m_scene = new magic::Scene(m_context);
|
||||
|
||||
auto *physics = m_scene->CreateComponent<magic::PhysicsWorld>(
|
||||
magic::LOCAL);
|
||||
physics->SetFps(30);
|
||||
physics->SetInterpolation(false);
|
||||
|
||||
// Useless but gets rid of warnings like
|
||||
// "ERROR: No Octree component in scene, drawable will not render"
|
||||
m_scene->CreateComponent<magic::Octree>(magic::LOCAL);
|
||||
|
||||
magic::ResourceCache *magic_cache =
|
||||
m_context->GetSubsystem<magic::ResourceCache>();
|
||||
ResourceCache *magic_cache =
|
||||
m_context->GetSubsystem<ResourceCache>();
|
||||
//magic_cache->SetAutoReloadResources(true);
|
||||
magic_cache->SetResourceRouter(
|
||||
new BuildatResourceRouter(m_context, m_server));
|
||||
|
||||
sub_magic_event(magic::E_LOGMESSAGE,
|
||||
sub_magic_event(E_LOGMESSAGE,
|
||||
Event::t("urho3d_log_redirect:message"));
|
||||
m_server->sub_event(this, Event::t("urho3d_log_redirect:message"));
|
||||
}
|
||||
@ -171,10 +182,10 @@ struct Module: public interface::Module, public main_context::Interface
|
||||
m_engine->RunFrame();
|
||||
|
||||
uint64_t current_us = interface::os::get_timeofday_us();
|
||||
magic::Profiler *p = m_context->GetSubsystem<magic::Profiler>();
|
||||
Profiler *p = m_context->GetSubsystem<Profiler>();
|
||||
if(p && profiler_last_print_us < current_us - 10000000){
|
||||
profiler_last_print_us = current_us;
|
||||
magic::String s = p->GetData(false, false, UINT_MAX);
|
||||
String s = p->GetData(false, false, UINT_MAX);
|
||||
p->BeginInterval();
|
||||
log_v(MODULE, "Urho3D profiler:\n%s", s.CString());
|
||||
}
|
||||
@ -199,14 +210,60 @@ struct Module: public interface::Module, public main_context::Interface
|
||||
|
||||
// Interface
|
||||
|
||||
magic::Context* get_context()
|
||||
Context* get_context()
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
magic::Scene* get_scene()
|
||||
Scene* find_scene(SceneReference ref)
|
||||
{
|
||||
return m_scene;
|
||||
SceneSetItem item((void*)ref);
|
||||
auto it = std::lower_bound(m_scenes.begin(), m_scenes.end(), item,
|
||||
std::greater<SceneSetItem>());
|
||||
if(it == m_scenes.end())
|
||||
return nullptr;
|
||||
return it->scene.Get();
|
||||
}
|
||||
|
||||
Scene* get_scene(SceneReference ref)
|
||||
{
|
||||
Scene *scene = find_scene(ref);
|
||||
if(!scene)
|
||||
throw Exception("get_scene(): Scene not found");
|
||||
return scene;
|
||||
}
|
||||
|
||||
SceneReference create_scene()
|
||||
{
|
||||
SharedPtr<Scene> scene(new Scene(m_context));
|
||||
|
||||
auto *physics = scene->CreateComponent<PhysicsWorld>(LOCAL);
|
||||
physics->SetFps(30);
|
||||
physics->SetInterpolation(false);
|
||||
|
||||
// Useless but gets rid of warnings like
|
||||
// "ERROR: No Octree component in scene, drawable will not render"
|
||||
scene->CreateComponent<Octree>(LOCAL);
|
||||
|
||||
// Insert into m_scenes
|
||||
SceneSetItem item(scene);
|
||||
auto it = std::lower_bound(m_scenes.begin(), m_scenes.end(), item,
|
||||
std::greater<SceneSetItem>());
|
||||
if(it == m_scenes.end())
|
||||
m_scenes.insert(it, item);
|
||||
|
||||
return (SceneReference)scene.Get();
|
||||
}
|
||||
|
||||
void delete_scene(SceneReference ref)
|
||||
{
|
||||
// Erase from m_scenes
|
||||
SceneSetItem item((void*)ref);
|
||||
auto it = std::lower_bound(m_scenes.begin(), m_scenes.end(), item,
|
||||
std::greater<SceneSetItem>());
|
||||
if(it == m_scenes.end())
|
||||
throw Exception("delete_scene(): Scene not found");
|
||||
m_scenes.erase(it);
|
||||
}
|
||||
|
||||
void sub_magic_event(
|
||||
|
@ -12,20 +12,51 @@ namespace Urho3D
|
||||
class Scene;
|
||||
}
|
||||
|
||||
namespace main_context
|
||||
{
|
||||
struct OpaqueSceneReference;
|
||||
typedef OpaqueSceneReference* SceneReference;
|
||||
};
|
||||
|
||||
namespace replicate
|
||||
{
|
||||
namespace magic = Urho3D;
|
||||
using interface::Event;
|
||||
using main_context::SceneReference;
|
||||
|
||||
typedef size_t PeerId;
|
||||
|
||||
struct PeerJoinedScene: public Event::Private
|
||||
{
|
||||
PeerId peer;
|
||||
SceneReference scene;
|
||||
|
||||
PeerJoinedScene(PeerId peer, SceneReference scene):
|
||||
peer(peer), scene(scene){}
|
||||
};
|
||||
|
||||
struct PeerLeftScene: public Event::Private
|
||||
{
|
||||
PeerId peer;
|
||||
SceneReference scene;
|
||||
|
||||
PeerLeftScene(PeerId peer, SceneReference scene):
|
||||
peer(peer), scene(scene){}
|
||||
};
|
||||
|
||||
struct Interface
|
||||
{
|
||||
virtual sv_<PeerId> find_peers_that_know_node(uint node_id) = 0;
|
||||
// Use scene_ref=nullptr to deassign
|
||||
virtual void assign_scene_to_peer(
|
||||
main_context::SceneReference scene_ref, PeerId peer) = 0;
|
||||
|
||||
virtual sv_<PeerId> find_peers_that_know_node(
|
||||
main_context::SceneReference scene_ref, uint node_id) = 0;
|
||||
|
||||
virtual void emit_after_next_sync(Event event) = 0;
|
||||
|
||||
virtual void sync_node_immediate(uint node_id) = 0;
|
||||
virtual void sync_node_immediate(
|
||||
main_context::SceneReference scene_ref, uint node_id) = 0;
|
||||
};
|
||||
|
||||
inline bool access(interface::Server *server,
|
||||
|
@ -33,7 +33,7 @@ using magic::Component;
|
||||
|
||||
namespace replicate {
|
||||
|
||||
ss_ dump(const magic::VectorBuffer &buf)
|
||||
static ss_ dump(const magic::VectorBuffer &buf)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
os<<"[";
|
||||
@ -46,18 +46,25 @@ ss_ dump(const magic::VectorBuffer &buf)
|
||||
return os.str();
|
||||
}
|
||||
|
||||
ss_ buf_to_string(const magic::VectorBuffer &buf)
|
||||
static ss_ buf_to_string(const magic::VectorBuffer &buf)
|
||||
{
|
||||
return ss_((const char*)&buf.GetBuffer().Front(), buf.GetBuffer().Size());
|
||||
}
|
||||
|
||||
struct PeerState
|
||||
{
|
||||
PeerId peer_id = 0;
|
||||
main_context::SceneReference scene_ref = nullptr;
|
||||
magic::SceneReplicationState scene_state;
|
||||
};
|
||||
|
||||
struct Module: public interface::Module, public replicate::Interface
|
||||
{
|
||||
interface::Server *m_server;
|
||||
// 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_<PeerId, magic::SceneReplicationState> m_scene_states;
|
||||
sm_<PeerId, PeerState> m_peers;
|
||||
|
||||
sv_<Event> m_events_to_emit_after_next_sync;
|
||||
|
||||
@ -72,10 +79,16 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
{
|
||||
log_d(MODULE, "replicate destruct");
|
||||
main_context::access(m_server, [&](main_context::Interface *imc){
|
||||
magic::Scene *scene = imc->get_scene();
|
||||
for(auto &pair: m_scene_states){
|
||||
magic::SceneReplicationState &scene_state = pair.second;
|
||||
scene->CleanupConnection((magic::Connection*)&scene_state);
|
||||
for(auto &pair: m_peers){
|
||||
PeerState &ps = pair.second;
|
||||
if(ps.scene_ref == nullptr)
|
||||
continue;
|
||||
magic::Scene *scene = imc->find_scene(ps.scene_ref);
|
||||
if(!scene){
|
||||
log_w(MODULE, "~Module(): Scene %p not found", ps.scene_ref);
|
||||
continue;
|
||||
}
|
||||
scene->CleanupConnection((magic::Connection*)&ps.scene_state);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -87,6 +100,7 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
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("network:client_connected"));
|
||||
m_server->sub_event(this, Event::t("network:client_disconnected"));
|
||||
m_server->sub_event(this, Event::t("core:tick"));
|
||||
}
|
||||
|
||||
@ -97,6 +111,8 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
EVENT_VOIDN("core:continue", on_continue)
|
||||
EVENT_TYPEN("network:client_connected", on_client_connected,
|
||||
network::NewClient)
|
||||
EVENT_TYPEN("network:client_disconnected", on_client_disconnected,
|
||||
network::OldClient)
|
||||
EVENT_TYPEN("core:tick", on_tick, interface::TickEvent)
|
||||
}
|
||||
|
||||
@ -123,19 +139,28 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
log_v(MODULE, "replicate::on_client_disconnected: id=%zu",
|
||||
old_client.info.id);
|
||||
auto peer = old_client.info.id;
|
||||
auto it = m_scene_states.find(peer);
|
||||
if(it == m_scene_states.end())
|
||||
auto it = m_peers.find(peer);
|
||||
if(it == m_peers.end())
|
||||
return;
|
||||
magic::SceneReplicationState &scene_state = it->second;
|
||||
main_context::access(m_server, [&](main_context::Interface *imc){
|
||||
magic::Scene *scene = imc->get_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);
|
||||
});
|
||||
PeerState &ps = it->second;
|
||||
if(ps.scene_ref){
|
||||
main_context::access(m_server, [&](main_context::Interface *imc){
|
||||
magic::Scene *scene = imc->find_scene(ps.scene_ref);
|
||||
if(!scene){
|
||||
log_w(MODULE, "on_client_disconnected(): Scene %p not found",
|
||||
ps.scene_ref);
|
||||
return;
|
||||
}
|
||||
// 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*)&ps.scene_state);
|
||||
});
|
||||
m_server->emit_event("replicate:peer_left_scene",
|
||||
new PeerLeftScene(peer, ps.scene_ref));
|
||||
}
|
||||
m_peers.erase(peer);
|
||||
}
|
||||
|
||||
void on_tick(const interface::TickEvent &event)
|
||||
@ -152,36 +177,44 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
|
||||
void sync_changes()
|
||||
{
|
||||
sv_<PeerId> peers;
|
||||
network::access(m_server, [&](network::Interface *inetwork){
|
||||
peers = inetwork->list_peers();
|
||||
});
|
||||
main_context::access(m_server, [&](main_context::Interface *imc){
|
||||
magic::Scene *scene = imc->get_scene();
|
||||
// For a reference implementation of this kind of network
|
||||
// synchronization, see Urho3D's Network/Connection.cpp
|
||||
|
||||
// Compare attributes and set replication states dirty as needed;
|
||||
// this accesses every replication state for every node.
|
||||
scene->PrepareNetworkUpdate();
|
||||
// For a reference implementation of this kind of network
|
||||
// synchronization, see Urho3D's Network/Connection.cpp
|
||||
|
||||
main_context::access(m_server, [&](main_context::Interface *imc)
|
||||
{
|
||||
// Send changes to each peer (each of which has its own replication
|
||||
// state)
|
||||
for(auto &peer: peers){
|
||||
magic::SceneReplicationState &scene_state = m_scene_states[peer];
|
||||
for(auto &pair: m_peers){
|
||||
PeerState &ps = pair.second;
|
||||
if(ps.scene_ref == nullptr)
|
||||
continue;
|
||||
magic::Scene *scene = imc->find_scene(ps.scene_ref);
|
||||
if(!scene){
|
||||
log_w(MODULE, "sync_changes(): Scene %p not found",
|
||||
ps.scene_ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare attributes and set replication states dirty as needed;
|
||||
// this accesses every replication state for every node.
|
||||
// NOTE: This can be called multiple times per scene; only the
|
||||
// first call will do anything as it clears the
|
||||
// marked-for-update lists
|
||||
scene->PrepareNetworkUpdate();
|
||||
|
||||
magic::HashSet<uint> nodes_to_process;
|
||||
uint scene_id = scene->GetID();
|
||||
nodes_to_process.Insert(scene_id);
|
||||
sync_node(peer, scene_id, nodes_to_process, scene, scene_state);
|
||||
sync_node(ps.peer_id, scene_id, nodes_to_process, scene,
|
||||
ps.scene_state);
|
||||
|
||||
nodes_to_process.Insert(scene_state.dirtyNodes_);
|
||||
nodes_to_process.Insert(ps.scene_state.dirtyNodes_);
|
||||
nodes_to_process.Erase(scene_id);
|
||||
|
||||
while(!nodes_to_process.Empty()){
|
||||
uint node_id = nodes_to_process.Front();
|
||||
sync_node(peer, node_id, nodes_to_process, scene,
|
||||
scene_state);
|
||||
sync_node(ps.peer_id, node_id, nodes_to_process, scene,
|
||||
ps.scene_state);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -463,16 +496,44 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
|
||||
// Interface
|
||||
|
||||
sv_<PeerId> find_peers_that_know_node(uint node_id)
|
||||
void assign_scene_to_peer(
|
||||
main_context::SceneReference scene_ref, PeerId peer)
|
||||
{
|
||||
log_v(MODULE, "assign_scene_to_peer(): scene_ref=%p, peer=%i",
|
||||
scene_ref, peer);
|
||||
if(scene_ref == nullptr){
|
||||
auto it = m_peers.find(peer);
|
||||
if(it == m_peers.end())
|
||||
return; // Peer wasn't in any scene
|
||||
PeerState &ps = it->second;
|
||||
m_server->emit_event("replicate:peer_left_scene",
|
||||
new PeerLeftScene(peer, ps.scene_ref));
|
||||
m_peers.erase(peer);
|
||||
|
||||
log_w(MODULE, "assign_scene_to_peer(): Deassign not properly"
|
||||
" implemented");
|
||||
// TODO: Tell client to remove replicated nodes and components
|
||||
} else {
|
||||
m_server->emit_event("replicate:peer_joined_scene",
|
||||
new PeerJoinedScene(peer, scene_ref));
|
||||
PeerState &ps = m_peers[peer];
|
||||
ps.peer_id = peer;
|
||||
ps.scene_ref = scene_ref;
|
||||
}
|
||||
}
|
||||
|
||||
sv_<PeerId> find_peers_that_know_node(
|
||||
main_context::SceneReference scene_ref, 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_;
|
||||
for(auto &pair: m_peers){
|
||||
PeerState &ps = pair.second;
|
||||
if(ps.scene_ref == nullptr || ps.scene_ref != scene_ref)
|
||||
continue;
|
||||
auto &node_states = ps.scene_state.nodeStates_;
|
||||
auto it = node_states.Find(node_id);
|
||||
if(it != node_states.End()){
|
||||
result.push_back(peer_id);
|
||||
result.push_back(ps.peer_id);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -483,27 +544,39 @@ struct Module: public interface::Module, public replicate::Interface
|
||||
m_events_to_emit_after_next_sync.push_back(std::move(event));
|
||||
}
|
||||
|
||||
// TODO: Check if this is correctly implemented
|
||||
void sync_node_immediate(uint node_id)
|
||||
void sync_node_immediate(
|
||||
main_context::SceneReference scene_ref, uint node_id)
|
||||
{
|
||||
sv_<PeerId> peers;
|
||||
network::access(m_server, [&](network::Interface *inetwork){
|
||||
peers = inetwork->list_peers();
|
||||
});
|
||||
main_context::access(m_server, [&](main_context::Interface *imc){
|
||||
magic::Scene *scene = imc->get_scene();
|
||||
Node *n = scene->GetNode(node_id);
|
||||
n->PrepareNetworkUpdate();
|
||||
for(auto &peer: peers){
|
||||
magic::SceneReplicationState &scene_state = m_scene_states[peer];
|
||||
for(auto &pair: m_peers){
|
||||
PeerState &ps = pair.second;
|
||||
if(ps.scene_ref == nullptr || ps.scene_ref != scene_ref)
|
||||
continue;
|
||||
magic::Scene *scene = imc->find_scene(ps.scene_ref);
|
||||
if(!scene){
|
||||
log_w(MODULE, "sync_node_immdiate(): Scene %p not found",
|
||||
ps.scene_ref);
|
||||
continue;
|
||||
}
|
||||
Node *n = scene->GetNode(node_id);
|
||||
if(!n){
|
||||
log_w(MODULE, "sync_node_immediate(): Node %i not found",
|
||||
node_id);
|
||||
continue;
|
||||
}
|
||||
// Compare attributes and set replication states dirty as needed;
|
||||
// this accesses every replication state for every child node.
|
||||
// NOTE: This can be called multiple times; only the first call
|
||||
// will do anything as it clears the marked-for-update lists
|
||||
n->PrepareNetworkUpdate();
|
||||
|
||||
magic::HashSet<uint> nodes_to_process;
|
||||
nodes_to_process.Insert(node_id);
|
||||
|
||||
while(!nodes_to_process.Empty()){
|
||||
uint node_id = nodes_to_process.Front();
|
||||
sync_node(peer, node_id, nodes_to_process, scene,
|
||||
scene_state);
|
||||
sync_node(ps.peer_id, node_id, nodes_to_process, scene,
|
||||
ps.scene_state);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -26,7 +26,6 @@ Buildat TODO
|
||||
- Precompiled mode
|
||||
- Singleplayer UI
|
||||
- Show all exceptions and errors on client using ui_utils.show_message_dialog
|
||||
- Allow creating and destroying arbitrary amounts of replicated scenes
|
||||
- magic.sub_sync_node_added -> replicate.sub_sync_node_added
|
||||
- Use tags for module dependencies (tags, require_tag, disallow_tag), and
|
||||
auto-tag each module at runtime with game_<current_game>
|
||||
@ -44,8 +43,6 @@ Buildat TODO
|
||||
woxelworld:update and ThreadPool::run_post() will not be run on the same frame
|
||||
- Pre (lod) geometry can take up to 2500us, and post can take 7200us, which
|
||||
means this will have a visible effect.
|
||||
- Multiple replicated scenes and voxelworlds, for servers with a lobby and
|
||||
multiple worlds
|
||||
- Don't emit all world generation events at once
|
||||
- Handle module load order properly in reloads (unload by popping from top,
|
||||
then reload everything until top)
|
||||
|
Loading…
x
Reference in New Issue
Block a user