Browse Source

Optimized network server operation by using HashMap instead of Map where applicable and by querying Serializable attributes once per network frame, instead of per user.

Lasse Öörni 13 years ago
parent
commit
1cf5d99d44

+ 22 - 20
Engine/Container/Map.h

@@ -331,37 +331,39 @@ private:
     /// Find the node with smallest key.
     Node* FindFirst() const
     {
-        if (!head_)
+        Node* node = Root();
+        if (!node)
             return 0;
         
-        // Check cached node first
-        if (head_->link_[0])
-            return reinterpret_cast<Node*>(head_->link_[0]);
-        
-        Node* node = reinterpret_cast<Node*>(head_->parent_);
-        while (node && node->link_[0])
-            node = node->Child(0);
+        // Search if not cached
+        Node*& first = reinterpret_cast<Node*&>(head_->link_[0]);
+        if (!first)
+        {
+            while (node && node->link_[0])
+                node = node->Child(0);
+            first = node;
+        }
         
-        head_->link_[0] = node;
-        return node;
+        return first;
     }
     
     /// Find the node with largest key.
     Node* FindLast() const
     {
-        if (!head_)
+        Node* node = Root();
+        if (!node)
             return 0;
         
-        // Check cached node first
-        if (head_->link_[1])
-            return reinterpret_cast<Node*>(head_->link_[1]);
-        
-        Node* node = reinterpret_cast<Node*>(head_->parent_);
-        while (node && node->link_[1])
-            node = node->Child(1);
+        // Search if not cached
+        Node*& last = reinterpret_cast<Node*&>(head_->link_[1]);
+        if (!last)
+        {
+            while (node && node->link_[1])
+                node = node->Child(1);
+            last = node;
+        }
         
-        head_->link_[1] = node;
-        return node;
+        return last;
     }
     
     /// Find a node with key. Return null if not found.

+ 22 - 20
Engine/Container/Set.h

@@ -282,37 +282,39 @@ private:
     /// Find the node with smallest key.
     Node* FindFirst() const
     {
-        if (!head_)
+        Node* node = Root();
+        if (!node)
             return 0;
         
-        // Check cached node first
-        if (head_->link_[0])
-            return reinterpret_cast<Node*>(head_->link_[0]);
-        
-        Node* node = reinterpret_cast<Node*>(head_->parent_);
-        while (node && node->link_[0])
-            node = node->Child(0);
+        // Search if not cached
+        Node*& first = reinterpret_cast<Node*&>(head_->link_[0]);
+        if (!first)
+        {
+            while (node && node->link_[0])
+                node = node->Child(0);
+            first = node;
+        }
         
-        head_->link_[0] = node;
-        return node;
+        return first;
     }
     
     /// Find the node with largest key.
     Node* FindLast() const
     {
-        if (!head_)
+        Node* node = Root();
+        if (!node)
             return 0;
         
-        // Check cached node first
-        if (head_->link_[1])
-            return reinterpret_cast<Node*>(head_->link_[1]);
-        
-        Node* node = reinterpret_cast<Node*>(head_->parent_);
-        while (node && node->link_[1])
-            node = node->Child(1);
+        // Search if not cached
+        Node*& last = reinterpret_cast<Node*&>(head_->link_[1]);
+        if (!last)
+        {
+            while (node && node->link_[1])
+                node = node->Child(1);
+            last = node;
+        }
         
-        head_->link_[1] = node;
-        return node;
+        return last;
     }
     
     /// Find a node with key. Return null if not found.

+ 1 - 1
Engine/Graphics/AnimatedModel.cpp

@@ -564,7 +564,7 @@ void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones)
 {
     if (!node_ && createBones)
     {
-        LOGWARNING("AnimatedModel not attached to a scene node, can not create bone nodes");
+        LOGERROR("AnimatedModel not attached to a scene node, can not create bone nodes");
         return;
     }
     

+ 24 - 25
Engine/Network/Connection.cpp

@@ -216,7 +216,7 @@ void Connection::Disconnect(int waitMSec)
     connection_->Disconnect(waitMSec);
 }
 
-void Connection::SendServerUpdate()
+void Connection::SendServerUpdate(unsigned serverFrameNumber)
 {
     if (!scene_ || !sceneLoaded_)
         return;
@@ -228,16 +228,16 @@ void Connection::SendServerUpdate()
     // Check for new or changed nodes
     // Start from the root node (scene) so that the scene-wide components get sent first
     processedNodes_.Clear();
-    ProcessNode(scene_);
+    ProcessNode(serverFrameNumber, scene_);
     
     // Then go through the rest of the nodes
     for (Map<unsigned, Node*>::ConstIterator i = nodes.Begin(); i != nodes.End() && i->first_ < FIRST_LOCAL_ID; ++i)
-        ProcessNode(i->second_);
+        ProcessNode(serverFrameNumber, i->second_);
     
     // Check for removed nodes
-    for (Map<unsigned, NodeReplicationState>::Iterator i = sceneState_.Begin(); i != sceneState_.End();)
+    for (HashMap<unsigned, NodeReplicationState>::Iterator i = sceneState_.Begin(); i != sceneState_.End();)
     {
-        Map<unsigned, NodeReplicationState>::Iterator current = i++;
+        HashMap<unsigned, NodeReplicationState>::Iterator current = i++;
         if (current->second_.frameNumber_ != frameNumber_)
         {
             msg_.Clear();
@@ -311,9 +311,9 @@ void Connection::SendPackages()
     {
         unsigned char buffer[PACKAGE_FRAGMENT_SIZE];
         
-        for (Map<StringHash, PackageUpload>::Iterator i = uploads_.Begin(); i != uploads_.End();)
+        for (HashMap<StringHash, PackageUpload>::Iterator i = uploads_.Begin(); i != uploads_.End();)
         {
-            Map<StringHash, PackageUpload>::Iterator current = i++;
+            HashMap<StringHash, PackageUpload>::Iterator current = i++;
             PackageUpload& upload = current->second_;
             unsigned fragmentSize = Min((int)(upload.file_->GetSize() - upload.file_->GetPosition()), (int)PACKAGE_FRAGMENT_SIZE);
             upload.file_->Read(buffer, fragmentSize);
@@ -758,7 +758,7 @@ void Connection::ProcessPackageDownload(int msgID, MemoryBuffer& msg)
         {
             StringHash nameHash = msg.ReadStringHash();
             
-            Map<StringHash, PackageDownload>::Iterator i = downloads_.Find(nameHash);
+            HashMap<StringHash, PackageDownload>::Iterator i = downloads_.Find(nameHash);
             // In case of being unable to create the package file into the cache, we will still receive all data from the server.
             // Simply disregard it
             if (i == downloads_.End())
@@ -979,7 +979,7 @@ unsigned Connection::GetNumDownloads() const
 
 const String& Connection::GetDownloadName() const
 {
-    for (Map<StringHash, PackageDownload>::ConstIterator i = downloads_.Begin(); i != downloads_.End(); ++i)
+    for (HashMap<StringHash, PackageDownload>::ConstIterator i = downloads_.Begin(); i != downloads_.End(); ++i)
     {
         if (i->second_.initiated_)
             return i->second_.name_;
@@ -989,7 +989,7 @@ const String& Connection::GetDownloadName() const
 
 float Connection::GetDownloadProgress() const
 {
-    for (Map<StringHash, PackageDownload>::ConstIterator i = downloads_.Begin(); i != downloads_.End(); ++i)
+    for (HashMap<StringHash, PackageDownload>::ConstIterator i = downloads_.Begin(); i != downloads_.End(); ++i)
     {
         if (i->second_.initiated_)
             return (float)i->second_.receivedFragments_.Size() / (float)i->second_.totalFragments_;
@@ -1006,7 +1006,7 @@ void Connection::HandleAsyncLoadFinished(StringHash eventType, VariantMap& event
     SendMessage(MSG_SCENELOADED, true, true, msg_, NET_HIGH_PRIORITY);
 }
 
-void Connection::ProcessNode(Node* node)
+void Connection::ProcessNode(unsigned serverFrameNumber, Node* node)
 {
     if (!node || processedNodes_.Contains(node))
         return;
@@ -1017,16 +1017,16 @@ void Connection::ProcessNode(Node* node)
     PODVector<Node*> dependencyNodes;
     node->GetDependencyNodes(dependencyNodes);
     for (PODVector<Node*>::ConstIterator i = dependencyNodes.Begin(); i != dependencyNodes.End(); ++i)
-        ProcessNode(*i);
+        ProcessNode(serverFrameNumber, *i);
     
     // Check if the client's replication state already has this node
     if (sceneState_.Contains(node->GetID()))
-        ProcessExistingNode(node);
+        ProcessExistingNode(serverFrameNumber, node);
     else
-        ProcessNewNode(node);
+        ProcessNewNode(serverFrameNumber, node);
 }
 
-void Connection::ProcessNewNode(Node* node)
+void Connection::ProcessNewNode(unsigned serverFrameNumber, Node* node)
 {
     msg_.Clear();
     msg_.WriteNetID(node->GetID());
@@ -1036,7 +1036,7 @@ void Connection::ProcessNewNode(Node* node)
     nodeState.frameNumber_ = frameNumber_;
     
     // Write node's attributes
-    node->WriteInitialDeltaUpdate(msg_, deltaUpdateBits_, nodeState.attributes_);
+    node->WriteInitialDeltaUpdate(serverFrameNumber, msg_, deltaUpdateBits_, nodeState.attributes_);
     
     // Write node's user variables
     const VariantMap& vars = node->GetVars();
@@ -1064,13 +1064,13 @@ void Connection::ProcessNewNode(Node* node)
         
         msg_.WriteShortStringHash(component->GetType());
         msg_.WriteNetID(component->GetID());
-        component->WriteInitialDeltaUpdate(msg_, deltaUpdateBits_, componentState.attributes_);
+        component->WriteInitialDeltaUpdate(serverFrameNumber, msg_, deltaUpdateBits_, componentState.attributes_);
     }
     
     SendMessage(MSG_CREATENODE, true, true, msg_, NET_HIGH_PRIORITY);
 }
 
-void Connection::ProcessExistingNode(Node* node)
+void Connection::ProcessExistingNode(unsigned serverFrameNumber, Node* node)
 {
     NodeReplicationState& nodeState = sceneState_[node->GetID()];
     nodeState.frameNumber_ = frameNumber_;
@@ -1086,7 +1086,7 @@ void Connection::ProcessExistingNode(Node* node)
     
     // Check if attributes have changed
     bool deltaUpdate, latestData;
-    node->PrepareUpdates(deltaUpdateBits_, classCurrentState_[node->GetType()], nodeState.attributes_, deltaUpdate, latestData);
+    node->PrepareUpdates(serverFrameNumber, deltaUpdateBits_, nodeState.attributes_, deltaUpdate, latestData);
     
     // Check if user variables have changed. Note: variable removal is not supported
     changedVars_.Clear();
@@ -1141,7 +1141,7 @@ void Connection::ProcessExistingNode(Node* node)
         if (component->GetID() >= FIRST_LOCAL_ID)
             continue;
         
-        Map<unsigned, ComponentReplicationState>::Iterator j = nodeState.components_.Find(component->GetID());
+        HashMap<unsigned, ComponentReplicationState>::Iterator j = nodeState.components_.Find(component->GetID());
         if (j == nodeState.components_.End())
         {
             // New component
@@ -1153,7 +1153,7 @@ void Connection::ProcessExistingNode(Node* node)
             msg_.WriteNetID(node->GetID());
             msg_.WriteShortStringHash(component->GetType());
             msg_.WriteNetID(component->GetID());
-            component->WriteInitialDeltaUpdate(msg_, deltaUpdateBits_, componentState.attributes_);
+            component->WriteInitialDeltaUpdate(serverFrameNumber, msg_, deltaUpdateBits_, componentState.attributes_);
             
             SendMessage(MSG_CREATECOMPONENT, true, true, msg_, NET_HIGH_PRIORITY);
         }
@@ -1163,8 +1163,7 @@ void Connection::ProcessExistingNode(Node* node)
             ComponentReplicationState& componentState = j->second_;
             componentState.frameNumber_ = frameNumber_;
             
-            component->PrepareUpdates(deltaUpdateBits_, classCurrentState_[component->GetType()], componentState.attributes_,
-                deltaUpdate, latestData);
+            component->PrepareUpdates(serverFrameNumber, deltaUpdateBits_, componentState.attributes_, deltaUpdate, latestData);
             
             // Send deltaupdate message if necessary
             if (deltaUpdate)
@@ -1190,9 +1189,9 @@ void Connection::ProcessExistingNode(Node* node)
     }
     
     // Check for removed components
-    for (Map<unsigned, ComponentReplicationState>::Iterator i = nodeState.components_.Begin(); i != nodeState.components_.End();)
+    for (HashMap<unsigned, ComponentReplicationState>::Iterator i = nodeState.components_.Begin(); i != nodeState.components_.End();)
     {
-        Map<unsigned, ComponentReplicationState>::Iterator current = i++;
+        HashMap<unsigned, ComponentReplicationState>::Iterator current = i++;
         if (current->second_.frameNumber_ != frameNumber_)
         {
             msg_.Clear();

+ 7 - 9
Engine/Network/Connection.h

@@ -124,7 +124,7 @@ public:
     /// Disconnect. If wait time is non-zero, will block while waiting for disconnect to finish.
     void Disconnect(int waitMSec = 0);
     /// Send scene update messages. Called by Network.
-    void SendServerUpdate();
+    void SendServerUpdate(unsigned serverFrameNumber);
     /// Send latest controls from the client. Called by Network.
     void SendClientUpdate();
     /// Send queued remote events. Called by Network.
@@ -194,11 +194,11 @@ private:
     /// Handle scene loaded event.
     void HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData);
     /// Process a node for sending a network update. Recurses to process depended on node(s) first.
-    void ProcessNode(Node* node);
+    void ProcessNode(unsigned serverFrameNumber, Node* node);
     /// Process a node that the client had not yet received.
-    void ProcessNewNode(Node* node);
+    void ProcessNewNode(unsigned serverFrameNumber, Node* node);
     /// Process a node that the client has already received.
-    void ProcessExistingNode(Node* node);
+    void ProcessExistingNode(unsigned serverFrameNumber, Node* node);
     /// Initiate a package download.
     void RequestPackage(const String& name, unsigned fileSize, unsigned checksum);
     /// Send an error reply for a package download.
@@ -215,13 +215,11 @@ private:
     /// Scene.
     WeakPtr<Scene> scene_;
     /// Last sent state of the scene for network replication.
-    Map<unsigned, NodeReplicationState> sceneState_;
-    /// Preallocated attribute variants per networked object class for sending updates.
-    Map<ShortStringHash, Vector<Variant> > classCurrentState_;
+    HashMap<unsigned, NodeReplicationState> sceneState_;
     /// Waiting or ongoing package file receive transfers.
-    Map<StringHash, PackageDownload> downloads_;
+    HashMap<StringHash, PackageDownload> downloads_;
     /// Ongoing package send transfers.
-    Map<StringHash, PackageUpload> uploads_;
+    HashMap<StringHash, PackageUpload> uploads_;
     /// Pending latest data for not yet received nodes.
     HashMap<unsigned, PODVector<unsigned char> > nodeLatestData_;
     /// Pending latest data for not yet received components.

+ 8 - 2
Engine/Network/Network.cpp

@@ -47,7 +47,8 @@ Network::Network(Context* context) :
     Object(context),
     updateFps_(DEFAULT_UPDATE_FPS),
     updateInterval_(1.0f / (float)DEFAULT_UPDATE_FPS),
-    updateAcc_(0.0f)
+    updateAcc_(0.0f),
+    frameNumber_(1)
 {
     network_ = new kNet::Network();
     
@@ -427,7 +428,7 @@ void Network::PostUpdate(float timeStep)
             for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::ConstIterator i = clientConnections_.Begin();
                 i != clientConnections_.End(); ++i)
             {
-                i->second_->SendServerUpdate();
+                i->second_->SendServerUpdate(frameNumber_);
                 i->second_->SendRemoteEvents();
                 i->second_->SendPackages();
             }
@@ -442,6 +443,11 @@ void Network::PostUpdate(float timeStep)
         
         // Notify that the update was sent
         SendEvent(E_NETWORKUPDATESENT);
+        
+        // Increment server frame number. Wrap to 1 as 0 means "update never sent" for Serializables
+        ++frameNumber_;
+        if (!frameNumber_)
+            ++frameNumber_;
     }
 }
 

+ 2 - 0
Engine/Network/Network.h

@@ -132,6 +132,8 @@ private:
     float updateInterval_;
     /// Update time accumulator.
     float updateAcc_;
+    /// Server frame number.
+    unsigned frameNumber_;
     /// Package cache directory.
     String packageCacheDir_;
 };

+ 1 - 1
Engine/Network/ReplicationState.h

@@ -44,7 +44,7 @@ struct NodeReplicationState
     /// User variables.
     VariantMap vars_;
     /// Components by ID.
-    Map<unsigned, ComponentReplicationState> components_;
+    HashMap<unsigned, ComponentReplicationState> components_;
     /// Interest management priority accumulator.
     float priorityAcc_;
     /// Update frame number.

+ 31 - 17
Engine/Scene/Serializable.cpp

@@ -35,6 +35,7 @@ OBJECTTYPESTATIC(Serializable);
 
 Serializable::Serializable(Context* context) :
     Object(context),
+    lastServerFrameNumber_(0),
     loading_(false)
 {
 }
@@ -419,7 +420,7 @@ bool Serializable::SetAttribute(const String& name, const Variant& value)
     return false;
 }
 
-void Serializable::WriteInitialDeltaUpdate(Serializer& dest, PODVector<unsigned char>& deltaUpdateBits,
+void Serializable::WriteInitialDeltaUpdate(unsigned serverFrameNumber, Serializer& dest, PODVector<unsigned char>& deltaUpdateBits,
     Vector<Variant>& replicationState)
 {
     const Vector<AttributeInfo>* attributes = GetNetworkAttributes();
@@ -427,17 +428,28 @@ void Serializable::WriteInitialDeltaUpdate(Serializer& dest, PODVector<unsigned
         return;
     unsigned numAttributes = attributes->Size();
     
+    // Get current attribute values from the component if necessary
+    if (serverFrameNumber != lastServerFrameNumber_ || serverAttributes_.Empty())
+    {
+        serverAttributes_.Resize(numAttributes);
+        for (unsigned i = 0; i < numAttributes; ++i)
+        {
+            const AttributeInfo& attr = attributes->At(i);
+            OnGetAttribute(attr, serverAttributes_[i]);
+        }
+        lastServerFrameNumber_ = serverFrameNumber;
+    }
+    
     replicationState.Resize(numAttributes);
     deltaUpdateBits.Resize((numAttributes + 7) >> 3);
     for (unsigned i = 0; i < deltaUpdateBits.Size(); ++i)
         deltaUpdateBits[i] = 0;
     
-    // Set initial attribute values and compare against defaults
+    // Copy attributes to replication state and compare against defaults
     for (unsigned i = 0; i < numAttributes; ++i)
     {
         const AttributeInfo& attr = attributes->At(i);
-        
-        OnGetAttribute(attr, replicationState[i]);
+        replicationState[i] = serverAttributes_[i];
         if (replicationState[i] != attr.defaultValue_)
             deltaUpdateBits[i >> 3] |= (1 << (i & 7));
     }
@@ -447,14 +459,12 @@ void Serializable::WriteInitialDeltaUpdate(Serializer& dest, PODVector<unsigned
     
     for (unsigned i = 0; i < numAttributes; ++i)
     {
-        const AttributeInfo& attr = attributes->At(i);
-        
         if (deltaUpdateBits[i >> 3] & (1 << (i & 7)))
             dest.WriteVariantData(replicationState[i]);
     }
 }
 
-void Serializable::PrepareUpdates(PODVector<unsigned char>& deltaUpdateBits, Vector<Variant>& classCurrentState,
+void Serializable::PrepareUpdates(unsigned serverFrameNumber, PODVector<unsigned char>& deltaUpdateBits,
     Vector<Variant>& replicationState, bool& deltaUpdate, bool& latestData)
 {
     deltaUpdate = false;
@@ -465,23 +475,27 @@ void Serializable::PrepareUpdates(PODVector<unsigned char>& deltaUpdateBits, Vec
         return;
     unsigned numAttributes = attributes->Size();
     
+    // Get current attribute values from the component if necessary
+    if (serverFrameNumber != lastServerFrameNumber_)
+    {
+        for (unsigned i = 0; i < numAttributes; ++i)
+        {
+            const AttributeInfo& attr = attributes->At(i);
+            OnGetAttribute(attr, serverAttributes_[i]);
+        }
+        lastServerFrameNumber_ = serverFrameNumber;
+    }
+    
     deltaUpdateBits.Resize((numAttributes + 7) >> 3);
     for (unsigned i = 0; i < deltaUpdateBits.Size(); ++i)
         deltaUpdateBits[i] = 0;
     
-    // If class-specific current state has not been previously used, resize it now
-    if (classCurrentState.Empty())
-        classCurrentState.Resize(numAttributes);
-    
     for (unsigned i = 0; i < numAttributes; ++i)
     {
-        const AttributeInfo& attr = attributes->At(i);
-        
-        // Check for attribute change
-        OnGetAttribute(attr, classCurrentState[i]);
-        if (classCurrentState[i] != replicationState[i])
+        if (serverAttributes_[i] != replicationState[i])
         {
-            replicationState[i] = classCurrentState[i];
+            const AttributeInfo& attr = attributes->At(i);
+            replicationState[i] = serverAttributes_[i];
             if (attr.mode_ & AM_LATESTDATA)
                 latestData = true;
             else

+ 6 - 2
Engine/Scene/Serializable.h

@@ -63,9 +63,9 @@ public:
     /// %Set attribute by name. Return true if successfully set.
     bool SetAttribute(const String& name, const Variant& value);
     /// Write initial delta network update (compared to default attribute values) and prepare the last sent state.
-    void WriteInitialDeltaUpdate(Serializer& dest, PODVector<unsigned char>& deltaUpdateBits, Vector<Variant>& replicationState);
+    void WriteInitialDeltaUpdate(unsigned serverFrameNumber, Serializer& dest, PODVector<unsigned char>& deltaUpdateBits, Vector<Variant>& replicationState);
     /// Prepare delta and latest data network updates. Needs a previously prepared last sent state from WriteInitialDeltaUpdate().
-    void PrepareUpdates(PODVector<unsigned char>& deltaUpdateBits, Vector<Variant>& classCurrentState, Vector<Variant>& replicationState, bool& deltaUpdate, bool& latestData);
+    void PrepareUpdates(unsigned serverFrameNumber, PODVector<unsigned char>& deltaUpdateBits, Vector<Variant>& replicationState, bool& deltaUpdate, bool& latestData);
     /// Write a delta network update prepared with PrepareUpdates().
     void WriteDeltaUpdate(Serializer& dest, PODVector<unsigned char>& deltaUpdateBits, Vector<Variant>& replicationState);
     /// Write a latestdata network update prepared with PrepareUpdates().
@@ -91,6 +91,10 @@ public:
     bool IsLoading() const { return loading_; }
     
 protected:
+    /// Server-side attributes for sending updates. Only updated once per network frame, not per user.
+    Vector<Variant> serverAttributes_;
+    /// Last network frame number.
+    unsigned lastServerFrameNumber_;
     /// Is loading flag.
     bool loading_;
 };