| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981 |
- //
- // Urho3D Engine
- // Copyright (c) 2008-2011 Lasse Öörni
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "Precompiled.h"
- #include "Connection.h"
- #include "File.h"
- #include "Log.h"
- #include "Network.h"
- #include "NetworkEvents.h"
- #include "PackageFile.h"
- #include "Profiler.h"
- #include "Protocol.h"
- #include "ProtocolEvents.h"
- #include "Scene.h"
- #include "Server.h"
- #include "DebugNew.h"
- // Timeout for closing transferred package files, in milliseconds
- static const int FILE_TIMEOUT = 15 * 1000;
- OBJECTTYPESTATIC(Server);
- Server::Server(Context* context) :
- Object(context),
- netFps_(30),
- timeAcc_(0),
- frameNumber_(1),
- maxSceneRevisions_(100),
- stayRelevantTime_(30)
- {
- SubscribeToEvent(E_PEERCONNECTED, HANDLER(Server, HandlePeerConnected));
- SubscribeToEvent(E_PEERDISCONNECTED, HANDLER(Server, HandlePeerDisconnected));
- }
- Server::~Server()
- {
- }
- void Server::SetNetFps(int fps)
- {
- netFps_ = Max(fps, 1);
- }
- void Server::SetMaxSceneRevisions(int revisions)
- {
- maxSceneRevisions_ = Max(revisions, 3);
- }
- void Server::SetStayRelevantTime(int time)
- {
- stayRelevantTime_ = Max(time, 1);
- }
- void Server::AddScene(Scene* scene)
- {
- if (!scene)
- return;
-
- if (HasScene(scene))
- {
- LOGWARNING("Scene " + scene->GetName() + " already added to server");
- return;
- }
-
- scene->SetNetworkMode(NM_SERVER);
- scenes_.Push(SharedPtr<Scene>(scene));
- }
- void Server::RemoveScene(Scene* scene)
- {
- if (!scene)
- return;
-
- for (unsigned i = 0; i < scenes_.Size(); ++i)
- {
- if (scenes_[i] == scene)
- {
- VectorBuffer packet;
- packet.WriteUByte(MSG_JOINREPLY);
- packet.WriteBool(false);
- packet.WriteString("The scene is shutting down");
-
- // If any clients are connected to this scene, they must leave forcibly
- for (unsigned j = 0; j < connections_.Size(); ++j)
- {
- Connection* connection = connections_[j];
- if (connection->GetScene() == scene)
- {
- connection->SendReliable(packet);
- connection->LeftScene();
- }
- }
-
- // Remove the network mode
- scene->SetNetworkMode(NM_NONETWORK);
- scenes_.Erase(scenes_.Begin() + i);
- return;
- }
- }
-
- LOGWARNING("Scene " + scene->GetName() + " not found on server");
- }
- bool Server::Start(unsigned short port)
- {
- timeAcc_ = 0.0f;
- frameNumber_ = 1;
- return GetSubsystem<Network>()->StartServer(port);
- }
- void Server::Stop()
- {
- GetSubsystem<Network>()->StopServer();
- }
- void Server::Update(float timeStep)
- {
- PROFILE(UpdateServer);
-
- // Process incoming packets from connections (assume that Engine has updated Network, so we do not do that here)
- for (unsigned i = 0; i < connections_.Size(); ++i)
- HandlePackets(connections_[i]);
-
- // Update scenes / send update if enough time passed
- float netPeriod = 1.0f / netFps_;
- timeAcc_ += timeStep;
- if (timeAcc_ >= netPeriod)
- {
- // Update simulation of scene(s)
- for (unsigned i = 0; i < scenes_.Size(); ++i)
- scenes_[i]->Update(netPeriod);
-
- // If multiple updates have accumulated because of a slow frame, send just one
- while (timeAcc_ >= netPeriod)
- timeAcc_ -= netPeriod;
- ++frameNumber_;
-
- // We have a policy that framenumber 0 means "frame never received", so loop back to 1
- if (!frameNumber_)
- ++frameNumber_;
-
- // Send update for each connection
- for (unsigned i = 0; i < connections_.Size(); ++i)
- SendServerUpdate(connections_[i]);
- }
-
- // Remove disconnected clients
- for (Vector<SharedPtr<Connection> >::Iterator i = connections_.Begin(); i != connections_.End();)
- {
- if (!(*i)->IsConnected())
- i = connections_.Erase(i);
- else
- ++i;
- }
-
- // Close file transfers that have been unused for some time
- for (Map<StringHash, ServerFileTransfer>::Iterator i = fileTransfers_.Begin(); i != fileTransfers_.End();)
- {
- Map<StringHash, ServerFileTransfer>::Iterator current = i++;
- if (current->second_.closeTimer_.GetMSec(false) > FILE_TIMEOUT)
- fileTransfers_.Erase(current);
- }
- }
- bool Server::SetClientScene(Connection* connection, Scene* scene)
- {
- // Check that the scene is under server control
- if (!HasScene(scene))
- return false;
-
- connection->SetScene(scene);
- SendSceneInfo(connection);
-
- return true;
- }
- bool Server::IsRunning() const
- {
- return GetSubsystem<Network>()->IsServer();
- }
- bool Server::HasScene(Scene* scene) const
- {
- for (unsigned i = 0; i < scenes_.Size(); ++i)
- {
- if (scenes_[i] == scene)
- return true;
- }
-
- return false;
- }
- unsigned Server::GetNumUsersInScene(Scene* scene) const
- {
- unsigned users = 0;
-
- for (unsigned i = 0; i < connections_.Size(); ++i)
- {
- if (connections_[i]->GetScene() == scene)
- ++users;
- }
-
- return users;
- }
- void Server::HandlePackets(Connection* connection)
- {
- VectorBuffer packet;
-
- // Process reliable packets first, then unreliable
- while (connection->ReceiveReliable(packet))
- {
- if (!HandleReliablePacket(connection, packet))
- return;
- }
- while (connection->ReceiveUnreliable(packet))
- {
- if (!HandleClientUpdate(connection, packet))
- return;
- }
- }
- void Server::HandlePeerConnected(StringHash eventType, VariantMap& eventData)
- {
- using namespace PeerConnected;
-
- Peer* peer = static_cast<Peer*>(eventData[P_PEER].GetPtr());
- if (peer->GetPeerType() != PEER_CLIENT)
- return;
-
- // Create a new connection, assign a challenge, then send the challenge message
- SharedPtr<Connection> connection(new Connection(context_, peer));
- connections_.Push(connection);
-
- unsigned challenge = GenerateChallenge();
- connection->SetChallenge(challenge);
-
- VectorBuffer packet;
- packet.WriteUByte(MSG_CHALLENGE);
- packet.WriteUInt(challenge);
- connection->SendReliable(packet);
- }
- void Server::HandlePeerDisconnected(StringHash eventType, VariantMap& eventData)
- {
- using namespace PeerDisconnected;
-
- Peer* peerPtr = static_cast<Peer*>(eventData[P_PEER].GetPtr());
- if (peerPtr->GetPeerType() != PEER_CLIENT)
- return;
-
- for (unsigned i = 0; i < connections_.Size(); ++i)
- {
- Connection* connection = connections_[i];
- if (connection->GetPeer() == peerPtr)
- {
- // Remove if was in a scene
- connection->LeftScene();
- return;
- }
- }
- }
- bool Server::HandleReliablePacket(Connection* connection, VectorBuffer& packet)
- {
- unsigned char msgID = 0;
-
- msgID = packet.ReadUByte();
-
- switch (msgID)
- {
- case MSG_LOGIN:
- if (!HandleLogin(connection, packet))
- return false;
- else
- break;
-
- case MSG_REQUESTFILE:
- HandleRequestFile(connection, packet);
- break;
-
- case MSG_JOINSCENE:
- HandleJoinScene(connection, packet);
- break;
-
- case MSG_FULLUPDATEACK:
- HandleFullUpdateAck(connection, packet);
- break;
-
- default:
- Disconnect(connection, false, "Unauthorized message ID " + String((int)msgID) + ", disconnecting client");
- return false;
- }
-
- return true;
- }
- bool Server::HandleLogin(Connection* connection, VectorBuffer& packet)
- {
- connection->SetLoginData(packet.ReadVariantMap());
-
- // Send login event and check if any event handler denies access
- using namespace ClientLogin;
-
- // Initialize eventdata with the logindata received
- VariantMap eventData = connection->GetLoginData();
- eventData[P_CONNECTION] = (void*)connection;
- eventData[P_AUTHORIZE] = true;
- SendEvent(E_CLIENTLOGIN, eventData);
-
- if (!eventData[P_AUTHORIZE].GetBool())
- {
- Disconnect(connection, false, "Disconnecting unauthorized client");
- return false;
- }
-
- return true;
- }
- void Server::HandleRequestFile(Connection* connection, VectorBuffer& packet)
- {
- StringHash nameHash = packet.ReadStringHash();
- int fragmentStart = packet.ReadVLE();
- int fragmentCount = packet.ReadVLE();
-
- // The only files we are willing to transmit are packages associated with scene(s)
- PackageFile* package = 0;
- for (unsigned i = 0; i < scenes_.Size(); ++i)
- {
- const Vector<SharedPtr<PackageFile> >& packages = scenes_[i]->GetRequiredPackageFiles();
- for (unsigned j = 0; j < packages.Size(); ++j)
- {
- if (packages[j]->GetNameHash() == nameHash)
- {
- package = packages[j];
- break;
- }
- }
- }
- if (!package)
- {
- LOGWARNING("Client " + connection->GetIdentity() + " requested unknown file " + String(nameHash));
- VectorBuffer replyPacket;
- replyPacket.WriteUByte(MSG_TRANSFERFAILED);
- replyPacket.WriteStringHash(nameHash);
- replyPacket.WriteString("File not found");
- connection->SendReliable(replyPacket);
- return;
- }
-
- // Open file if not already open
- File* file = fileTransfers_[nameHash].file_;
- if (!file)
- {
- file = fileTransfers_[nameHash].file_ = new File(context_, package->GetName());
- if (!file->IsOpen())
- {
- LOGERROR("Failed to open package file " + package->GetName() + " for file transfer");
- VectorBuffer replyPacket;
- replyPacket.WriteUByte(MSG_TRANSFERFAILED);
- replyPacket.WriteStringHash(nameHash);
- replyPacket.WriteString("Could not open file");
- connection->SendReliable(replyPacket);
- return;
- }
- }
-
- fileTransfers_[nameHash].closeTimer_.Reset();
-
- // Check that fragment range is valid
- unsigned fileSize = file->GetSize();
- int maxFragments = (fileSize - 1) / FILE_FRAGMENT_SIZE + 1;
- if (fragmentStart + fragmentCount > maxFragments)
- fragmentCount = maxFragments - fragmentStart;
- if (fragmentCount <= 0)
- return;
-
- if (!fragmentStart)
- LOGINFO("Client " + connection->GetIdentity() + " requested file " + String(nameHash));
-
- // Send the fragments
- unsigned fragmentOffset = fragmentStart * FILE_FRAGMENT_SIZE;
- file->Seek(fragmentOffset);
-
- for (int i = fragmentStart; i < fragmentStart + fragmentCount; ++i)
- {
- fragmentOffset = i * FILE_FRAGMENT_SIZE;
- unsigned fragmentSize = fileSize - fragmentOffset;
- if (fragmentSize > FILE_FRAGMENT_SIZE)
- fragmentSize = FILE_FRAGMENT_SIZE;
-
- VectorBuffer fragmentPacket;
- fragmentPacket.Seek(0);
- fragmentPacket.WriteUByte(MSG_TRANSFERDATA);
- fragmentPacket.WriteStringHash(nameHash);
- fragmentPacket.WriteVLE(i);
- fragmentPacket.Resize(fragmentPacket.GetPosition() + fragmentSize);
- file->Read(fragmentPacket.GetModifiableData() + fragmentPacket.GetPosition(), fragmentSize);
- connection->SendReliable(fragmentPacket);
- }
- }
- void Server::HandleJoinScene(Connection* connection, VectorBuffer& packet)
- {
- unsigned checksum = packet.ReadUInt();
- Scene* scene = connection->GetScene();
-
- VectorBuffer replyPacket;
- replyPacket.WriteUByte(MSG_JOINREPLY);
-
- if (!scene)
- {
- replyPacket.WriteBool(false);
- replyPacket.WriteString("No scene");
- LOGINFO("Client " + connection->GetIdentity() + " attempted to join without an assigned scene");
- }
- else if (checksum != scene->GetChecksum())
- {
- replyPacket.WriteBool(false);
- replyPacket.WriteString("Scene checksum mismatch");
- LOGINFO("Client " + connection->GetIdentity() + " checksum mismatch for scene " + scene->GetName());
- }
- else
- {
- replyPacket.WriteBool(true);
- connection->JoinedScene();
- }
-
- connection->SendReliable(replyPacket);
- }
- void Server::HandleFullUpdateAck(Connection* connection, VectorBuffer& packet)
- {
- if (connection->GetJoinState() == JS_WAITFORACK)
- {
- unsigned short lastFrameNumber = packet.ReadUShort();
- unsigned short lastFrameAck = packet.ReadUShort();
- connection->SetFrameNumbers(lastFrameNumber, lastFrameAck);
- connection->UpdateRoundTripTime(netFps_, frameNumber_);
- connection->SetJoinState(JS_SENDDELTAS);
- }
- else
- LOGWARNING("Received unexpected full update ack from client " + connection->GetIdentity());
- }
- bool Server::HandleClientUpdate(Connection* connection, VectorBuffer& packet)
- {
- // Disregard unreliable client updates while waiting for the initial scene ack
- if (connection->GetJoinState() != JS_SENDDELTAS)
- return true;
-
- unsigned short lastFrameNumber = packet.ReadUShort();
- unsigned short lastFrameAck = packet.ReadUShort();
-
- // Check that this packet is not older than the last received (overlap may occur when we transition
- // between a reliable full update and unreliable delta updates)
- if (!CheckFrameNumber(lastFrameNumber, connection->GetFrameNumber()))
- return true;
-
- connection->SetFrameNumbers(lastFrameNumber, lastFrameAck);
- connection->UpdateRoundTripTime(netFps_, frameNumber_);
-
- unsigned short previousEventFrameNumber = connection->GetEventFrameNumber();
-
- while (!packet.IsEof())
- {
- unsigned char msgID = packet.ReadUByte();
- // The client is only allowed to send a few specific messages in the client update
- switch (msgID)
- {
- case MSG_REMOTEEVENT:
- {
- RemoteEvent newEvent;
- newEvent.Read(packet, false);
- if (connection->CheckRemoteEventFrame(newEvent, previousEventFrameNumber))
- newEvent.Dispatch(connection, connection->GetScene());
- }
- break;
-
- case MSG_REMOTENODEEVENT:
- {
- RemoteEvent newEvent;
- newEvent.Read(packet, true);
- if (connection->CheckRemoteEventFrame(newEvent, previousEventFrameNumber))
- newEvent.Dispatch(connection, connection->GetScene());
- }
- break;
-
- case MSG_CONTROLS:
- {
- Controls newControls;
- newControls.buttons_ = packet.ReadUInt();
- newControls.yaw_ = packet.ReadFloat();
- newControls.pitch_ = packet.ReadFloat();
- newControls.extraData_ = packet.ReadVariantMap();
- connection->SetControls(newControls);
- connection->SetPosition(packet.ReadVector3());
-
- using namespace ClientControls;
-
- // Initialize event parameters with possible extra controls
- VariantMap eventData = newControls.extraData_;
- eventData[P_CONNECTION] = (void*)connection;
- eventData[P_FRAMENUMBER] = connection->GetFrameNumber();
- eventData[P_BUTTONS] = newControls.buttons_;
- eventData[P_YAW] = newControls.yaw_;
- eventData[P_PITCH] = newControls.pitch_;
- SendEvent(E_CLIENTCONTROLS, eventData);
- break;
- }
-
- default:
- Disconnect(connection, false, "Unauthorized message ID " + String((int)msgID) + ", disconnecting client");
- return false;
- }
- }
-
- return true;
- }
- void Server::SendSceneInfo(Connection* connection)
- {
- Scene* scene = connection->GetScene();
- if (!scene)
- return;
-
- VectorBuffer packet;
- packet.WriteUByte(MSG_SCENEINFO);
-
- // Write scene name, number of users and update rate
- packet.WriteString(scene->GetName());
- packet.WriteVLE(GetNumUsersInScene(scene));
- packet.WriteInt(netFps_);
-
- // Write source file name & required packages
- const Vector<SharedPtr<PackageFile> >& requiredPackages = scene->GetRequiredPackageFiles();
- packet.WriteString(scene->GetFileName());
- packet.WriteVLE(requiredPackages.Size());
- for (unsigned i = 0; i < requiredPackages.Size(); ++i)
- {
- PackageFile* package = requiredPackages[i];
- packet.WriteString(package->GetName());
- packet.WriteUInt(package->GetTotalSize());
- packet.WriteUInt(package->GetChecksum());
- }
-
- connection->SendReliable(packet);
- }
- void Server::SendFullUpdate(Connection* connection)
- {
- PROFILE(SendFullUpdate);
-
- Scene* scene = connection->GetScene();
- if (!scene)
- return;
-
- // Clear all scene revision data so that we write a full update
- connection->ClearSceneState();
-
- VectorBuffer packet;
- packet.WriteUByte(MSG_FULLUPDATE);
- WriteNetUpdate(connection, packet);
- connection->SendReliable(packet);
-
- // All unacked remote events were sent reliably, so clear them
- connection->ClearRemoteEvents();
- connection->SetJoinState(JS_WAITFORACK);
-
- LOGDEBUG("Initial scene: " + String(packet.GetSize()) + " bytes");
- }
- void Server::SendServerUpdate(Connection* connection)
- {
- PROFILE(SendServerUpdate);
-
- Scene* scene = connection->GetScene();
- JoinState state = connection->GetJoinState();
- if (!scene || state < JS_SENDFULLUPDATE || state == JS_WAITFORACK)
- return;
-
- // Purge states and events which are older than last acked, and expired remote events
- connection->PurgeAckedSceneState();
- connection->PurgeAckedRemoteEvents(frameNumber_);
-
- // If already have too many revisions stored, fall back to the initial scene sending (reliable)
- if (connection->GetSceneState().GetRevisionCount() >= maxSceneRevisions_)
- {
- LOGWARNING("Too many scene revisions for client " + connection->GetIdentity() + ", falling back to initial scene send");
- state = JS_SENDFULLUPDATE;
- }
-
- // Send initial scene as reliable
- if (state == JS_SENDFULLUPDATE)
- {
- SendFullUpdate(connection);
- return;
- }
-
- VectorBuffer packet;
- WriteNetUpdate(connection, packet);
- connection->SendUnreliable(packet);
-
- //LOGDEBUG("Delta: " + ToString(packet.GetSize()) + " Revisions: " +
- // ToString(connection->GetSceneState().GetRevisionCount()) + " Events: " +
- // ToString(connection->GetUnackedRemoteEvents().Size()));
- }
- unsigned Server::GenerateChallenge() const
- {
- return (rand() & 32767) | ((rand() & 32767) << 15) | ((rand() & 32767) << 30);
- }
- void Server::Disconnect(Connection* connection, bool forced, const String& logMessage)
- {
- LOGERROR(logMessage + " " + connection->GetIdentity());
-
- if (forced)
- connection->forceDisconnect();
- else
- connection->Disconnect();
-
- // Send event
- using namespace ClientDisconnected;
-
- VariantMap eventData;
- eventData[P_CONNECTION] = (void*)connection;
- SendEvent(E_CLIENTDISCONNECTED, eventData);
- }
- void Server::WriteNetUpdate(Connection* connection, Serializer& dest)
- {
- PROFILE(WriteNetUpdate);
-
- Scene* scene = connection->GetScene();
- SceneReplicationState& sceneState = connection->GetSceneState();
-
- // Write frame numbers
- dest.WriteUShort(frameNumber_);
- dest.WriteUShort(connection->GetFrameNumber());
-
- VectorBuffer emptyBaseRevision;
- VectorBuffer propertyBuffer;
- VectorBuffer newBuffer;
- VectorBuffer removeBuffer;
- VectorBuffer updateBuffer;
- VectorBuffer newRevision;
-
- /*
-
- // Find relevant nodes for this client
- Set<unsigned> relevantNodes;
- GetRelevantNodes(connection, relevantNodes);
-
- {
- // Go through the scene and see which nodes are new and which have been removed
- const Map<unsigned, SharedPtr<Node> >& nodes = scene->GetAllNodes();
- Set<unsigned> processedNodes;
- for (Map<unsigned, SharedPtr<Node> >::ConstIterator i = nodes.Begin(); i != nodes.End(); ++i)
- {
- // If we reach the local node ID's, break
- if (i->first_ >= FIRST_LOCAL_ID)
- break;
-
- processedNodes.Insert(i->first_);
-
- bool relevant = relevantNodes.Find(i->first_) != relevantNodes.End();
-
- Node* node = i->second_;
- NodeReplicationState* nodeState = sceneState.findNode(i->first_);
- if (!nodeState)
- {
- // If client does not have this node and it is not relevant, skip
- if (!relevant)
- continue;
-
- nodeState = &sceneState.nodes_[i->first_];
- nodeState->Created(frameNumber_);
- nodeState->stayRelevantTime_ = stayRelevantTime_;
- }
- else
- {
- // If nodestate exists, check relevancy timer and refresh it if necessary
- if (relevant)
- nodeState->stayRelevantTime_ = stayRelevantTime_;
- else if (nodeState->stayRelevantTime_ > 0)
- {
- --nodeState->stayRelevantTime_;
- relevant = true;
- }
- else
- // If node is not relevant, and relevancy timer has expired, do not check for changes
- continue;
-
- if (!nodeState->exists_)
- nodeState->Created(frameNumber_);
- }
-
- // Check components of this node
- const Vector<SharedPtr<Component> >& components = node->GetComponents();
- Set<ShortStringHash> processedComponents;
- for (Vector<SharedPtr<Component> >::ConstIterator j = components.Begin(); j != components.End(); ++j)
- {
- Component* component = *j;
- if (!component->checkSync(connection))
- continue;
-
- ShortStringHash combinedHash = component->GetCombinedHash();
- ComponentReplicationState* componentState = nodeState->findComponent(combinedHash);
-
- if (!componentState)
- {
- componentState = &nodeState->components_[combinedHash];
- componentState->Created(frameNumber_);
- }
- else if (!componentState->exists_)
- componentState->Created(frameNumber_);
-
- processedComponents.Insert(combinedHash);
- }
-
- // Check components that have been removed
- for (Map<ShortStringHash, ComponentReplicationState>::Iterator j = nodeState->components_.Begin();
- j != nodeState->components_.End(); ++j)
- {
- if (j->second_.exists_)
- {
- if (processedComponents.Find(j->first_) == processedComponents.End())
- j->second_.Removed(frameNumber_);
- }
- }
- }
-
- // Check nodes that have been removed
- for (Map<unsigned, NodeReplicationState>::Iterator i = sceneState.nodes_.Begin();
- i != sceneState.nodes_.End(); ++i)
- {
- if (i->second_.exists_)
- {
- if (processedNodes.Find(i->first_) == processedNodes.End())
- i->second_.Removed(frameNumber_);
- }
- }
- }
-
- {
- // Now go through the replication state again and build commands
- for (Map<unsigned, NodeReplicationState>::Iterator i = sceneState.nodes_.Begin();
- i != sceneState.nodes_.End(); ++i)
- {
- Node* node = scene->GetNode(i->first_);
- NodeReplicationState& nodeState = i->second_;
- // Create
- if (nodeState.createdFrame_ && node)
- {
- dest.WriteUByte(MSG_CREATEENTITY);
- dest.WriteUShort(node->GetID());
- dest.WriteString(node->GetName());
- dest.WriteUByte(GetClientNetFlags(connection, node, node->GetNetFlags()));
- dest.WriteVLE(node->GetGroupFlags());
-
- // Write a full update of node properties
- newRevision.Seek(0);
- if (node->WriteNetUpdate(dest, newRevision, emptyBaseRevision, info))
- nodeState.revisions_.Commit(frameNumber_, newRevision);
-
- // Write a full update of all components that should be synced
- const Vector<SharedPtr<Component> >& components = node->GetComponents();
- unsigned newComponents = 0;
- newBuffer.Seek(0);
-
- for (Vector<SharedPtr<Component> >::ConstIterator j = components.Begin(); j != components.End(); ++j)
- {
- Component* component = *j;
- if (!component->checkSync(connection))
- continue;
-
- ComponentReplicationState& componentState = nodeState.components_[component->GetCombinedHash()];
- newBuffer.WriteShortStringHash(component->GetType());
- newBuffer.WriteString(component->GetName());
- newBuffer.WriteUByte(GetClientNetFlags(connection, node, component->GetNetFlags()));
- newRevision.Seek(0);
- component->WriteNetUpdate(newBuffer, newRevision, emptyBaseRevision, info);
- componentState.revisions_.Commit(frameNumber_, newRevision);
- ++newComponents;
- }
-
- dest.WriteVLE(newComponents);
- // Check if any components were actually written
- if (newComponents)
- dest.Write(newBuffer.GetData(), newBuffer.GetPosition());
- }
- // Remove
- else if (nodeState.removedFrame_)
- {
- dest.WriteUByte(MSG_REMOVEENTITY);
- dest.WriteUShort(i->first_);
- }
- // Update
- else if (node)
- {
- // If node's update timer has expired (it is not relevant), do not write updates
- if (nodeState.stayRelevantTime_ <= 0)
- {
- // However, we must be careful to see what to do when the node becomes relevant again
- // If node has unacked property or component revisions, must forget all of them
- if (nodeState.HasUnAcked(connection->GetFrameAck()))
- {
- nodeState.revisions_.Clear();
- for (Map<ShortStringHash, ComponentReplicationState>::Iterator j = nodeState.components_.Begin();
- j != nodeState.components_.End(); ++j)
- j->second_.revisions_.Clear();
- }
- continue;
- }
-
- // Divide update types into separate buffers, then see which of them got data
- // (if there are no updates, then this node does not need to write anything into the final stream)
- unsigned newComponents = 0;
- unsigned removedComponents = 0;
- unsigned updatedComponents = 0;
-
- propertyBuffer.Seek(0);
- newBuffer.Seek(0);
- removeBuffer.Seek(0);
- updateBuffer.Seek(0);
-
- unsigned char msgID = MSG_UPDATEENTITY;
-
- // Property update
- Deserializer& baseRevision = nodeState.revisions_.GetBase();
- newRevision.Seek(0);
- if (node->WriteNetUpdate(propertyBuffer, newRevision, baseRevision, info))
- {
- nodeState.revisions_.Commit(frameNumber_, newRevision);
- msgID |= UPD_PROPERTIES;
- }
-
- // Component create/remove/update
- for (Map<ShortStringHash, ComponentReplicationState>::Iterator j = nodeState.components_.Begin();
- j != nodeState.components_.End(); ++j)
- {
- Component* component = node->GetComponent(j->first_.mData);
- ComponentReplicationState& componentState = j->second_;
- // Create
- if (componentState.createdFrame_ && component)
- {
- newBuffer.WriteShortStringHash(component->GetType());
- newBuffer.WriteString(component->GetName());
- newBuffer.WriteUByte(GetClientNetFlags(connection, node, component->GetNetFlags()));
- newRevision.Seek(0);
- component->WriteNetUpdate(newBuffer, newRevision, emptyBaseRevision, info);
- componentState.revisions_.Commit(frameNumber_, newRevision);
- msgID |= UPD_NEWCOMPONENTS;
- ++newComponents;
- }
- // Remove
- else if (componentState.removedFrame_)
- {
- msgID |= UPD_REMOVECOMPONENTS;
- ++removedComponents;
- removeBuffer.WriteShortStringHash(j->first_);
- }
- // Update
- else if (component)
- {
- // Prepare to rewind buffer in case component writes nothing meaningful
- unsigned oldPos = updateBuffer.GetPosition();
- updateBuffer.WriteShortStringHash(component->GetCombinedHash());
- newRevision.Seek(0);
- Deserializer& baseRevision = componentState.revisions_.GetBase();
- if (component->WriteNetUpdate(updateBuffer, newRevision, baseRevision, info))
- {
- componentState.revisions_.Commit(frameNumber_, newRevision);
- msgID |= UPD_UPDATECOMPONENTS;
- ++updatedComponents;
- }
- else
- updateBuffer.Seek(oldPos);
- }
- }
-
- // Now write each buffer if there was some data
- if (msgID != MSG_UPDATEENTITY)
- {
- dest.WriteUByte(msgID);
- dest.WriteUShort(node->GetID());
-
- if (msgID & UPD_PROPERTIES)
- dest.Write(propertyBuffer.GetData(), propertyBuffer.GetPosition());
- if (msgID & UPD_NEWCOMPONENTS)
- {
- dest.WriteVLE(newComponents);
- dest.Write(newBuffer.GetData(), newBuffer.GetPosition());
- }
- if (msgID & UPD_REMOVECOMPONENTS)
- {
- dest.WriteVLE(removedComponents);
- dest.Write(removeBuffer.GetData(), removeBuffer.GetPosition());
- }
- if (msgID & UPD_UPDATECOMPONENTS)
- {
- dest.WriteVLE(updatedComponents);
- dest.Write(updateBuffer.GetData(), updateBuffer.GetPosition());
- }
- }
- }
- }
- }
-
- */
-
- // Append unacked remote events
- const Vector<RemoteEvent>& unackedEvents = connection->GetUnackedRemoteEvents();
- for (Vector<RemoteEvent>::ConstIterator i = unackedEvents.Begin(); i != unackedEvents.End(); ++i)
- {
- dest.WriteUByte(i->nodeID_ ? MSG_REMOTENODEEVENT : MSG_REMOTEEVENT);
- i->Write(dest);
- }
- }
- void Server::GetRelevantNodes(Connection* connection, Set<unsigned>& dest) const
- {
- // Generate just the raw set of relevant nodes based on their owner, distance and references. A node might need
- // to stay relevant because it has unacked changes, or has time left in its relevancy timer, but that is checked in
- // WriteNetUpdate()
- PROFILE(GetRelevantNodes);
-
- dest.Clear();
-
- Scene* scene = connection->GetScene();
- const Map<unsigned, Node*>& nodes = scene->GetAllNodes();
- const Vector3& clientPos = connection->GetPosition();
-
- for (Map<unsigned, Node*>::ConstIterator i = nodes.Begin(); i != nodes.End(); ++i)
- {
- // Stop when local node ID range reached
- if (i->first_ >= FIRST_LOCAL_ID)
- break;
-
- Node* node = i->second_;
-
- // If node is not owned by client and max. update distance has been defined, check it
- if (node->GetOwner() != connection)
- {
- //float maxDistance = node->GetNetUpdateDistance();
- float maxDistance = 100.0f; /// \todo Add max. update distance to Node
- if (maxDistance > 0.0f)
- {
- if ((node->GetWorldPosition() - clientPos).LengthSquared() > maxDistance * maxDistance)
- continue;
- }
- }
-
- // Node is relevant. Now also find its dependencies
- dest.Insert(i->first_);
- /// \todo Implement getting the dependencies from a node
- //GetNodeDependencies(connection, node, dest);
- }
- }
|