| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955 |
- //
- // 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 "Client.h"
- #include "Connection.h"
- #include "FileSystem.h"
- #include "Log.h"
- #include "Network.h"
- #include "NetworkEvents.h"
- #include "PackageFile.h"
- #include "ProcessUtils.h"
- #include "Profiler.h"
- #include "Protocol.h"
- #include "ProtocolEvents.h"
- #include "ResourceCache.h"
- #include "Scene.h"
- #include "SceneEvents.h"
- #include "StringUtils.h"
- #include "DebugNew.h"
- static const Controls noControls;
- static const int MIN_FILE_FRAGMENT_COUNT = 16;
- static const int MAX_FILE_FRAGMENT_COUNT = 1024;
- static const int FILE_FRAGMENT_COUNT_DELTA = 8;
- OBJECTTYPESTATIC(Client);
- Client::Client(Context* context) :
- Object(context),
- netFps_(30),
- timeAcc_(0.0f),
- frameNumber_(1)
- {
- SubscribeToEvent(E_PEERDISCONNECTED, HANDLER(Client, HandlePeerDisconnected));
- SubscribeToEvent(E_FILETRANSFERCOMPLETED, HANDLER(Client, HandleFileTransferCompleted));
- SubscribeToEvent(E_FILETRANSFERFAILED, HANDLER(Client, HandleFileTransferFailed));
- SubscribeToEvent(E_ASYNCLOADFINISHED, HANDLER(Client, HandleAsyncLoadFinished));
- }
- Client::~Client()
- {
- }
- void Client::SetScene(Scene* scene)
- {
- scene_ = scene;
- }
- void Client::SetDownloadDirectory(const String& path)
- {
- downloadDirectory_ = AddTrailingSlash(path);
- }
- bool Client::Connect(const String& address, unsigned short port, const VariantMap& loginData)
- {
- if (!scene_)
- return false;
-
- // Make sure any previous async loading is stopped
- scene_->StopAsyncLoading();
-
- Disconnect();
-
- Peer* peer = GetSubsystem<Network>()->Connect(address, port);
- if (!peer)
- return false;
-
- serverConnection_ = new Connection(context_, peer);
- frameNumber_ = 1;
- pendingLoginData_ = loginData;
- return true;
- }
- void Client::Disconnect()
- {
- if (serverConnection_)
- {
- serverConnection_->Disconnect();
- serverConnection_.Reset();
- }
-
- // Stop async loading if was in progress
- if (!scene_)
- scene_->StopAsyncLoading();
-
- pendingDownloads_.Clear();
- fileTransfers_.Clear();
- sceneInfo_ = SceneInfo();
- }
- void Client::SetControls(const Controls& controls)
- {
- if (serverConnection_)
- serverConnection_->SetControls(controls);
- }
- void Client::SetPosition(const Vector3& position)
- {
- if (serverConnection_)
- serverConnection_->SetPosition(position);
- }
- void Client::Update(float timeStep)
- {
- PROFILE(UpdateClient);
-
- // Process connection (assume that Engine has updated Network, so we do not do that here)
- if (serverConnection_)
- {
- VectorBuffer packet;
-
- // Process reliable packets first, then unreliable
- while (serverConnection_->ReceiveReliable(packet))
- HandleReliablePacket(packet);
-
- while (serverConnection_->ReceiveUnreliable(packet))
- HandleServerUpdate(packet);
-
- // Update own simulation of scene if connected
- if (serverConnection_->GetScene() == scene_)
- scene_->Update(timeStep);
- }
-
- // Send update if enough time passed
- float netPeriod = 1.0f / netFps_;
- timeAcc_ += timeStep;
- if (timeAcc_ >= 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_;
- SendClientUpdate();
- }
- }
- bool Client::IsConnected() const
- {
- return (serverConnection_) && (serverConnection_->IsConnected()) && (serverConnection_->HasChallenge());
- }
- bool Client::IsJoinPending() const
- {
- return ((IsConnected()) && (serverConnection_->GetJoinState() == JS_PREPARESCENE));
- }
- bool Client::IsJoined() const
- {
- return ((IsConnected()) && (serverConnection_->GetJoinState() > JS_PREPARESCENE));
- }
- const Controls& Client::GetControls() const
- {
- if (!serverConnection_)
- return noControls;
- return serverConnection_->GetControls();
- }
- const Vector3& Client::GetPosition() const
- {
- if (!serverConnection_)
- return Vector3::ZERO;
- return serverConnection_->GetPosition();
- }
- String Client::GetFileTransferStatus() const
- {
- String ret;
-
- for (Map<StringHash, FileTransfer>::ConstIterator i = fileTransfers_.Begin(); i != fileTransfers_.End(); ++i)
- {
- String line = i->second_.fileName_ + " " + ToString(i->second_.bytesReceived_) + "/" + ToString(i->second_.size_)
- + " (" + ToString((int)(((float)i->second_.bytesReceived_ / (float)i->second_.size_) * 100.0f + 0.5f)) + "%)\n";
- ret += line;
- }
-
- return ret;
- }
- void Client::HandlePeerDisconnected(StringHash eventType, VariantMap& eventData)
- {
- using namespace PeerDisconnected;
-
- Peer* peerPtr = static_cast<Peer*>(eventData[P_PEER].GetPtr());
- if (peerPtr->GetType() != PEER_SERVER)
- return;
-
- if (serverConnection_)
- {
- if (serverConnection_->GetPeer() == peerPtr)
- {
- serverConnection_->LeftScene();
- serverConnection_.Reset();
- pendingDownloads_.Clear();
- fileTransfers_.Clear();
- sceneInfo_ = SceneInfo();
- }
- }
- }
- void Client::HandleFileTransferCompleted(StringHash eventType, VariantMap& eventData)
- {
- using namespace FileTransferCompleted;
-
- String fileName = eventData[P_FILENAME].GetString();
- if (pendingDownloads_.Find(fileName) != pendingDownloads_.End())
- {
- pendingDownloads_.Erase(fileName);
-
- // Add the package to the resource cache
- SharedPtr<PackageFile> package(new PackageFile(context_));
- if (!package->Open(eventData[P_FULLPATH].GetString()))
- {
- JoinFailed("Could not open downloaded package file " + fileName);
- return;
- }
- // Add the package as first in case it overrides something in the default files
- GetSubsystem<ResourceCache>()->AddPackageFile(package, true);
-
- // If this was the last required download, can now join scene
- if ((pendingDownloads_.Empty()) && (IsJoinPending()))
- SetupScene();
- }
- }
- void Client::HandleFileTransferFailed(StringHash eventType, VariantMap& eventData)
- {
- using namespace FileTransferFailed;
-
- String fileName = eventData[P_FILENAME].GetString();
- if (pendingDownloads_.Find(fileName) != pendingDownloads_.End())
- JoinFailed("Failed to transfer file " + fileName);
- }
- void Client::HandleAsyncLoadFinished(StringHash eventType, VariantMap& eventData)
- {
- if ((!scene_) || (!serverConnection_))
- return;
-
- using namespace AsyncLoadFinished;
-
- // If it is the scene used for networking, send join packet now
- if ((eventData[P_SCENE].GetPtr() == (void*)scene_) && (serverConnection_->GetJoinState() == JS_LOADSCENE))
- SendJoinScene();
- }
- void Client::HandleReliablePacket(VectorBuffer& packet)
- {
- unsigned char msgID = 0;
-
- msgID = packet.ReadUByte();
-
- switch (msgID)
- {
- case MSG_CHALLENGE:
- HandleChallenge(packet);
- break;
-
- case MSG_SCENEINFO:
- HandleSceneInfo(packet);
- break;
-
- case MSG_TRANSFERDATA:
- HandleTransferData(packet);
- break;
-
- case MSG_TRANSFERFAILED:
- HandleTransferFailed(packet);
- break;
-
- case MSG_JOINREPLY:
- HandleJoinReply(packet);
- break;
-
- case MSG_FULLUPDATE:
- HandleFullUpdate(packet);
- break;
-
- default:
- LOGERROR("Unknown message ID " + ToString((int)msgID) + " received from server");
- break;
- }
- }
- void Client::HandleChallenge(VectorBuffer& packet)
- {
- serverConnection_->SetChallenge(packet.ReadUInt());
-
- // Send login packet as a response
- VectorBuffer replyPacket;
- replyPacket.WriteUByte(MSG_LOGIN);
- replyPacket.WriteVariantMap(pendingLoginData_);
- serverConnection_->SendReliable(replyPacket);
- }
- void Client::HandleSceneInfo(VectorBuffer& packet)
- {
- if (!scene_)
- return;
-
- // Stop all previous loading, associate the scene with the connection
- scene_->StopAsyncLoading();
- serverConnection_->SetScene(scene_);
-
- // Read scene name, number of users and update rate
- String sceneName = packet.ReadString();
- sceneInfo_.name_ = sceneName;
- sceneInfo_.numUsers_ = packet.ReadVLE();
- sceneInfo_.netFps_ = packet.ReadInt();
-
- // Read source file name & required packages
- sceneInfo_.fileName_ = packet.ReadString();
- unsigned numPackages = packet.ReadVLE();
- sceneInfo_.requiredPackages_.Clear();
- for (unsigned i = 0; i < numPackages; ++i)
- {
- PackageInfo package;
- package.name_ = packet.ReadString();
- package.size_ = packet.ReadUInt();
- package.checksum_ = packet.ReadUInt();
- sceneInfo_.requiredPackages_.Push(package);
- }
-
- // Check need for downloads: if none, can join immediately
- if (!CheckPackages())
- SetupScene();
- }
- void Client::HandleTransferData(VectorBuffer& packet)
- {
- StringHash nameHash = packet.ReadStringHash();
- Map<StringHash, FileTransfer>::Iterator i = fileTransfers_.Find(nameHash);
- if (i == fileTransfers_.End())
- {
- LOGDEBUG("Received fragment for nonexisting file transfer " + ToString(nameHash));
- return;
- }
- FileTransfer& transfer = i->second_;
-
- unsigned index = packet.ReadVLE();
- if (transfer.fragmentsReceived_ != index)
- {
- LOGERROR("Received unexpected fragment for file " + ToString(nameHash) + ", stopping transfer");
-
- using namespace FileTransferFailed;
-
- VariantMap eventData;
- eventData[P_FILENAME] = transfer.fileName_;
- eventData[P_REASON] = "Unexpected fragment";
- SendEvent(E_FILETRANSFERFAILED, eventData);
-
- fileTransfers_.Erase(i);
- return;
- }
-
- transfer.fragmentsReceived_ = index + 1; // We receive fragments in order
- unsigned dataSize = packet.GetSize() - packet.GetPosition();
- transfer.file_->Write(packet.GetData() + packet.GetPosition(), dataSize);
- transfer.bytesReceived_ += dataSize;
-
- if (transfer.fragmentsReceived_ == transfer.numFragments_)
- {
- if (transfer.bytesReceived_ != transfer.size_)
- {
- LOGERROR("Transfer of file " + transfer.fileName_ + " finished, expected " + ToString(transfer.size_) +
- " bytes but got " + ToString(transfer.bytesReceived_));
-
- using namespace FileTransferFailed;
-
- VariantMap eventData;
- eventData[P_FILENAME] = transfer.fileName_;
- eventData[P_REASON] = "Unexpected file size";
- SendEvent(E_FILETRANSFERFAILED, eventData);
- }
- else
- {
- float totalTime = transfer.receiveTimer_.GetMSec(true) * 0.001f;
- LOGINFO("Transfer of file " + transfer.fileName_ + " completed in " +
- ToString(totalTime) + " seconds, speed " + ToString(transfer.size_ / totalTime) + " bytes/sec");
- using namespace FileTransferCompleted;
-
- VariantMap eventData;
- eventData[P_FILENAME] = transfer.fileName_;
- eventData[P_FULLPATH] = transfer.file_->GetName();
-
- // Others might try to use the file as a response to the event, so close it first
- transfer.file_.Reset();
-
- SendEvent(E_FILETRANSFERCOMPLETED, eventData);
- }
-
- fileTransfers_.Erase(i);
- return;
- }
-
- // If current batch was finished, fire off the next
- if (transfer.fragmentsReceived_ == transfer.batchStart_ + transfer.batchSize_)
- {
- transfer.batchStart_ = transfer.fragmentsReceived_;
- float batchTime = transfer.batchTimer_.GetMSec(true) * 0.001f;
- float newDataRate = transfer.batchSize_ * FILE_FRAGMENT_SIZE / batchTime;
- LOGDEBUG("Received " + ToString(transfer.batchSize_) + " fragments in " + ToString(batchTime) + " seconds");
-
- // If this was the first batch, can not yet estimate speed, so go up in batch size
- if (!transfer.lastBatchSize_)
- {
- transfer.lastBatchSize_ = transfer.batchSize_;
- transfer.lastDataRate_ = newDataRate;
- transfer.batchSize_ += FILE_FRAGMENT_COUNT_DELTA;
- }
- else
- {
- bool goUp = true;
- // Go down in batch size if last batch was smaller and had better data rate
- if ((transfer.lastBatchSize_ < transfer.batchSize_) && (transfer.lastDataRate_ > newDataRate))
- goUp = false;
-
- transfer.lastBatchSize_ = transfer.batchSize_;
- transfer.lastDataRate_ = newDataRate;
-
- if (goUp)
- transfer.batchSize_ += FILE_FRAGMENT_COUNT_DELTA;
- else
- transfer.batchSize_ -= FILE_FRAGMENT_COUNT_DELTA;
-
- transfer.batchSize_ = Clamp((int)transfer.batchSize_, MIN_FILE_FRAGMENT_COUNT, MAX_FILE_FRAGMENT_COUNT);
- }
-
- VectorBuffer packet;
- packet.WriteUByte(MSG_REQUESTFILE);
- packet.WriteStringHash(i->first_);
- packet.WriteVLE(transfer.batchStart_);
- packet.WriteVLE(transfer.batchSize_);
- serverConnection_->SendReliable(packet);
- }
- }
- void Client::HandleTransferFailed(VectorBuffer& packet)
- {
- StringHash nameHash = packet.ReadStringHash();
- String reason = packet.ReadString();
-
- Map<StringHash, FileTransfer>::Iterator i = fileTransfers_.Find(nameHash);
- if (i == fileTransfers_.End())
- {
- LOGDEBUG("Received fail for nonexisting file transfer " + ToString(nameHash));
- return;
- }
-
- String errorMsg = "Transfer of file " + ToString(nameHash) + " failed: " + reason;
- LOGINFO(errorMsg);
-
- using namespace FileTransferFailed;
-
- VariantMap eventData;
- eventData[P_FILENAME] = i->second_.fileName_;
- eventData[P_REASON] = reason;
- SendEvent(E_FILETRANSFERFAILED, eventData);
-
- fileTransfers_.Erase(i);
- }
- void Client::HandleJoinReply(VectorBuffer& packet)
- {
- if (!scene_)
- return;
-
- bool success = packet.ReadBool();
- if (success)
- {
- serverConnection_->JoinedScene();
-
- LOGINFO("Joined scene " + scene_->GetName());
-
- SendEvent(E_JOINEDSCENE);
- }
- else
- {
- String reason = packet.ReadString();
-
- serverConnection_->LeftScene();
- pendingDownloads_.Clear();
- fileTransfers_.Clear();
-
- JoinFailed(reason);
- }
- }
- void Client::HandleFullUpdate(VectorBuffer& packet)
- {
- HandleServerUpdate(packet, true);
- }
- void Client::HandleServerUpdate(VectorBuffer& packet, bool initial)
- {
- if (!IsJoined())
- return;
-
- // Read frame numbers
- unsigned short lastFrameNumber = packet.ReadUShort();
- unsigned short lastFrameAck = packet.ReadUShort();
-
- if (!initial)
- {
- // 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, serverConnection_->GetFrameNumber()))
- return;
- //LOGDEBUG("Delta: " + ToString(packet.GetSize()));
- }
- else
- {
- // If initial/full update, remove all old non-local nodes
- scene_->ClearNonLocal();
- LOGDEBUG("Initial scene: " + ToString(packet.GetSize()) + " bytes");
- }
-
- serverConnection_->SetFrameNumbers(lastFrameNumber, lastFrameAck);
- serverConnection_->UpdateRoundTripTime(netFps_, frameNumber_);
-
- unsigned short previousEventFrameNumber = serverConnection_->GetEventFrameNumber();
-
- Set<unsigned> updatedNodes;
- Set<unsigned> updatedComponents;
-
- // Read the actual scene update messages
- while (!packet.IsEof())
- {
- unsigned char msgID = packet.ReadUByte();
- switch (msgID & 0x0f)
- {
- case MSG_REMOTEEVENT:
- {
- RemoteEvent newEvent;
- newEvent.Read(packet, false);
- if (serverConnection_->CheckRemoteEventFrame(newEvent, previousEventFrameNumber))
- newEvent.Dispatch(serverConnection_, scene_);
- }
- break;
-
- case MSG_REMOTENODEEVENT:
- {
- RemoteEvent newEvent;
- newEvent.Read(packet, true);
- if (serverConnection_->CheckRemoteEventFrame(newEvent, previousEventFrameNumber))
- newEvent.Dispatch(serverConnection_, scene_);
- }
- break;
-
- default:
- LOGWARNING("Unknown message ID " + ToString((int)msgID) + " received from server");
- packet.Seek(packet.GetSize()); // Break loop
- break;
- }
- }
-
- // Perform post network update for nodes & components
-
- // If initial update, send back ack
- if (initial)
- {
- VectorBuffer replyPacket;
- replyPacket.WriteUByte(MSG_FULLUPDATEACK);
- replyPacket.WriteUShort(frameNumber_);
- replyPacket.WriteUShort(serverConnection_->GetFrameNumber());
- serverConnection_->SendReliable(replyPacket);
- serverConnection_->SetJoinState(JS_SENDDELTAS);
- }
-
- // Remove acked controls
- serverConnection_->PurgeAckedControls();
-
- // Send notification of the server update
- using namespace ServerUpdate;
-
- VariantMap eventData;
- eventData[P_SCENE] = (void*)scene_.GetPtr();
- SendEvent(E_SERVERUPDATE, eventData);
- }
- unsigned Client::CheckPackages()
- {
- pendingDownloads_.Clear();
-
- // To avoid resource version conflicts and to keep the amount of open packages reasonable, remove all existing
- // downloaded packages from the resource cache first
- Vector<String> downloadedPackages;
- Vector<SharedPtr<PackageFile> > registeredPackages = GetSubsystem<ResourceCache>()->GetPackageFiles();
- GetSubsystem<FileSystem>()->ScanDir(downloadedPackages, downloadDirectory_, "*.pak", SCAN_FILES, false);
-
- for (Vector<SharedPtr<PackageFile> >::Iterator i = registeredPackages.Begin(); i != registeredPackages.End();)
- {
- if ((*i)->GetName().Find(downloadDirectory_) != String::NPOS)
- {
- GetSubsystem<ResourceCache>()->RemovePackageFile(*i);
- i = registeredPackages.Erase(i);
- }
- else
- ++i;
- }
-
- for (unsigned i = 0; i < sceneInfo_.requiredPackages_.Size(); ++i)
- {
- const PackageInfo& required = sceneInfo_.requiredPackages_[i];
- String requiredName = GetFileName(required.name_);
- bool found = false;
-
- // Check both already registered packages, and existing downloads
- for (unsigned j = 0; j < registeredPackages.Size(); ++j)
- {
- PackageFile* package = registeredPackages[i];
- String name = GetFileName(package->GetName());
- if ((name.Find(requiredName) != String::NPOS) && (package->GetTotalSize() == required.size_) &&
- (package->GetChecksum() == required.checksum_))
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- for (unsigned j = 0; j < downloadedPackages.Size(); ++j)
- {
- // Downloaded packages are encoded as filename_checksum.pak, so check if the filename contains the required name
- String name = GetFileName(downloadedPackages[i]);
- if (name.Find(requiredName) != String::NPOS)
- {
- SharedPtr<PackageFile> file(new PackageFile(context_));
- file->Open(downloadDirectory_ + downloadedPackages[i]);
- if ((file->GetTotalSize() == required.size_) && (file->GetChecksum() == required.checksum_))
- {
- // Add the package as first in case it overrides something in the default files
- GetSubsystem<ResourceCache>()->AddPackageFile(file, true);
- found = true;
- break;
- }
- }
- }
- }
-
- if (!found)
- {
- // If not found, initiate the download
- if (!RequestFile(required.name_, required.size_, required.checksum_))
- {
- JoinFailed("Failed to initiate transfer for file " + required.name_);
- return M_MAX_UNSIGNED; // Return nonzero to prevent immediate join attempt
- }
- pendingDownloads_.Insert(required.name_);
- }
- }
-
- return pendingDownloads_.Size();
- }
- bool Client::RequestFile(const String& fileName, unsigned size, unsigned checksum)
- {
- StringHash nameHash(fileName);
- if (fileTransfers_.Find(nameHash) != fileTransfers_.End())
- return true; // Already initiated
-
- FileTransfer newTransfer;
- // Append checksum to download name, so that we can have several versions of a package
- String destName = GetFileName(fileName) + "_" + ToStringHex(checksum) + GetExtension(fileName);
- newTransfer.file_ = new File(context_, downloadDirectory_ + destName, FILE_WRITE);
- if (!newTransfer.file_->IsOpen())
- return false;
-
- newTransfer.fileName_ = fileName;
- newTransfer.size_ = size;
- newTransfer.checksum_ = checksum;
- newTransfer.numFragments_ = (size - 1) / FILE_FRAGMENT_SIZE + 1;
- newTransfer.batchTimer_.Reset();
- newTransfer.receiveTimer_.Reset();
- newTransfer.batchSize_ = MIN_FILE_FRAGMENT_COUNT;
-
- VectorBuffer packet;
- packet.WriteUByte(MSG_REQUESTFILE);
- packet.WriteStringHash(nameHash);
- packet.WriteVLE(newTransfer.batchStart_);
- packet.WriteVLE(newTransfer.batchSize_);
- serverConnection_->SendReliable(packet);
-
- fileTransfers_[nameHash] = newTransfer;
- LOGINFO("Started transfer of file " + fileName + ", " + ToString(size) + " bytes");
- return true;
- }
- void Client::SetupScene()
- {
- netFps_ = sceneInfo_.netFps_;
- timeAcc_ = 0.0f;
-
- // Setup the scene
- // If no filename, just empty it
- if (sceneInfo_.fileName_.Empty())
- {
- scene_->SetName(sceneInfo_.name_);
- scene_->Clear();
- SendJoinScene();
- }
- else
- {
- SharedPtr<File> sceneFile = GetSubsystem<ResourceCache>()->GetFile(sceneInfo_.fileName_);
- // Support either binary or XML format scene
- if (GetExtension(sceneInfo_.fileName_) == ".xml")
- scene_->LoadAsyncXML(sceneFile);
- else
- scene_->LoadAsync(sceneFile);
-
- // Check if scene started loading successfully
- if (scene_->IsAsyncLoading())
- serverConnection_->SetJoinState(JS_LOADSCENE);
- else
- JoinFailed("Failed to load scene " + sceneInfo_.fileName_);
- }
- }
- void Client::SendJoinScene()
- {
- if ((!scene_) || (!serverConnection_))
- return;
-
- VectorBuffer packet;
- packet.WriteUByte(MSG_JOINSCENE);
- packet.WriteUInt(scene_->GetChecksum());
- serverConnection_->SendReliable(packet);
- }
- void Client::JoinFailed(const String& reason)
- {
- LOGINFO("Failed to join scene, reason: " + reason);
-
- using namespace JoinSceneFailed;
-
- VariantMap eventData;
- eventData[P_REASON] = reason;
- SendEvent(E_JOINSCENEFAILED);
- }
- void Client::SendClientUpdate()
- {
- if (!IsJoined())
- {
- // If we are not connected but remote events have been queued, clear them
- serverConnection_->ClearRemoteEvents();
- return;
- }
-
- // Request updated controls from the application
- using namespace ControlsUpdate;
-
- VariantMap eventData;
- eventData[P_SCENE] = (void*)scene_.GetPtr();
- SendEvent(E_CONTROLSUPDATE, eventData);
-
- // Purge acked and expired remote events
- serverConnection_->PurgeAckedRemoteEvents(frameNumber_);
-
- VectorBuffer packet;
- packet.WriteUShort(frameNumber_);
- packet.WriteUShort(serverConnection_->GetFrameNumber());
-
- // Write controls and position
- const Controls& controls = serverConnection_->GetControls();
- packet.WriteUByte(MSG_CONTROLS);
- packet.WriteUInt(controls.buttons_);
- packet.WriteFloat(controls.yaw_);
- packet.WriteFloat(controls.pitch_);
- packet.WriteVariantMap(controls.extraData_);
- packet.WriteVector3(serverConnection_->GetPosition());
-
- // Remember the controls if prediction & replay is needed
- serverConnection_->AddUnackedControls(frameNumber_, controls);
-
- // Append unacked remote events
- const Vector<RemoteEvent>& unackedEvents = serverConnection_->GetUnackedRemoteEvents();
- for (Vector<RemoteEvent>::ConstIterator i = unackedEvents.Begin(); i != unackedEvents.End(); ++i)
- {
- packet.WriteUByte(i->nodeID_ ? MSG_REMOTENODEEVENT : MSG_REMOTEEVENT);
- i->Write(packet);
- }
-
- serverConnection_->SendUnreliable(packet);
- }
- void Client::ReadNetUpdateBlock(Deserializer& source, unsigned char msgID, Set<unsigned>& updatedNodes, Set<unsigned>& updatedComponents)
- {
- /*
- unsigned id = source.ReadUShort();
- Node* node = scene_->GetNodeByID(id);
-
- switch (msgID & 0x0f)
- {
- case MSG_CREATEENTITY:
- {
- // Node may already exist if server sends the create many times. But data may have changed
- String name = source.ReadString();
- if (!node)
- node = scene_->createNode(id, name);
-
- unsigned char netFlags = source.ReadUByte();
- node->setNetFlags(netFlags);
- if (netFlags & NET_OWNER)
- node->setOwner(serverConnection_);
- else
- node->setOwner(0);
- node->setGroupFlags(source.ReadVLE());
-
- node->setProperties(source.ReadVariantMap(), true);
-
- Set<Component*> extraComponents;
- extraComponents.Clear();
- const Vector<SharedPtr<Component> >& components = node->GetComponents();
- for (Vector<SharedPtr<Component> >::ConstIterator i = components.Begin(); i != components.End(); ++i)
- extraComponents.Insert(*i);
-
- unsigned nucomponents_ = source.ReadVLE();
- for (unsigned i = 0; i < nucomponents_; ++i)
- {
- ShortStringHash type = source.ReadShortStringHash();
- String name = source.ReadString();
- // We might apply the same update multiple times, so check if the component already exists
- Component* component = node->GetComponent(type, name);
- bool newComponent = false;
- if (!component)
- {
- component = node->createComponent(type, name);
- newComponent = true;
- }
- component->setNetFlags(source.ReadUByte());
- component->readNetUpdate(source, GetSubsystem<ResourceCache>(), info);
- // If component is new, finish interpolation immediately
- if (newComponent)
- component->interpolate(true);
-
- extraComponents.Erase(component);
- }
-
- // Now check if the node has extra components it should not, and remove them
- for (Set<Component*>::Iterator i = extraComponents.Begin(); i != extraComponents.End(); ++i)
- node->removeComponent(*i);
-
- updatedNodes.Insert(id);
- }
- break;
-
- case MSG_REMOVEENTITY:
- if (node)
- scene_->removeNode(id);
- break;
-
- case MSG_UPDATEENTITY:
- {
- // Node should exist, but if it does not, create it now to not desync the stream
- if (!node)
- {
- LOGERROR("Update to nonexisting node " + ToString(id));
- node = scene_->createNode(id);
- }
- if (msgID & UPD_PROPERTIES)
- {
- unsigned numProperties = source.ReadVLE();
- for (unsigned i = 0; i < numProperties; ++i)
- {
- ShortStringHash key = source.ReadShortStringHash();
- Variant value = source.ReadVariant();
- node->setProperty(key, value, true);
- }
- }
- if (msgID & UPD_NEWCOMPONENTS)
- {
- unsigned nucomponents_ = source.ReadVLE();
- for (unsigned i = 0; i < nucomponents_; ++i)
- {
- ShortStringHash type = source.ReadShortStringHash();
- String name = source.ReadString();
- // We might apply the same update multiple times, so check if the component already exists
- Component* component = node->GetComponent(type, name);
- bool newComponent = false;
- if (!component)
- {
- component = node->createComponent(type, name);
- newComponent = true;
- }
- component->setNetFlags(source.ReadUByte());
- component->readNetUpdate(source, GetSubsystem<ResourceCache>(), info);
- // If component is new, finish interpolation immediately
- if (newComponent)
- component->interpolate(true);
- }
- }
- if (msgID & UPD_REMOVECOMPONENTS)
- {
- unsigned nucomponents_ = source.ReadVLE();
- for (unsigned i = 0; i < nucomponents_; ++i)
- {
- Component* component = node->GetComponent(source.ReadShortStringHash().mData);
- if (component)
- node->removeComponent(component);
- }
- }
- if (msgID & UPD_UPDATECOMPONENTS)
- {
- unsigned nucomponents_ = source.ReadVLE();
- for (unsigned i = 0; i < nucomponents_; ++i)
- {
- ShortStringHash combinedHash = source.ReadShortStringHash();
- Component* component = node->GetComponent(combinedHash.mData);
- if (component)
- component->readNetUpdate(source, GetSubsystem<ResourceCache>(), info);
- else
- {
- // Component should exist, but if it does not, create it now to not desync the stream
- // Note that we only have the combined hash to go on with
- LOGERROR("Update to nonexisting component " + ToString(combinedHash) + " in node " +
- node->GetName());
- component = node->createComponent(combinedHash);
- component->readNetUpdate(source, GetSubsystem<ResourceCache>(), info);
- component->interpolate(true);
- }
- }
- }
- updatedNodes.Insert(id);
- }
- break;
- }
- */
- }
|