diff --git a/doomsday/apps/client/src/network/serverlink.cpp b/doomsday/apps/client/src/network/serverlink.cpp index d2f6f882a0..f3ebcae26b 100644 --- a/doomsday/apps/client/src/network/serverlink.cpp +++ b/doomsday/apps/client/src/network/serverlink.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,8 @@ DENG2_PIMPL(ServerLink) std::unique_ptr serverProfile; ///< Profile used when joining. std::function profileResultCallback; std::function profileResultCallbackWithAddress; - LoopCallback mainCall; + String fileRepository; + LoopCallback mainCall; // for deferred actions Impl(Public *i, Flags flags) : Base(i) @@ -335,6 +337,25 @@ DENG2_PIMPL(ServerLink) dlg->open(MessageDialog::Modal); } + void mountFileRepository(shell::ServerInfo const &info) + { + fileRepository = info.address().asText(); + FS::get().makeFolderWithFeed + ("/remote/server", + RemoteFeedRelay::get().addServerRepository(fileRepository), + Folder::PopulateAsyncFullTree); + } + + void unmountFileRepository() + { + if (Folder *remoteFiles = FS::tryLocate("/remote/server")) + { + trash(remoteFiles); + } + RemoteFeedRelay::get().removeRepository(fileRepository); + fileRepository.clear(); + } + DENG2_PIMPL_AUDIENCE(DiscoveryUpdate) DENG2_PIMPL_AUDIENCE(PingResponse) DENG2_PIMPL_AUDIENCE(MapOutline) @@ -415,10 +436,7 @@ void ServerLink::connectToServerAndChangeGame(shell::ServerInfo info) joinProfile = &adhoc; } - Folder &remotePacks = FS::get().makeFolderWithFeed - ("/remote/server", - RemoteFeedRelay::get().addServerRepository(info.address().asText()), - Folder::PopulateAsyncFullTree); + d->mountFileRepository(info); if (!joinProfile->isPlayable()) { @@ -534,7 +552,7 @@ void ServerLink::disconnect() DENG2_FOR_AUDIENCE2(Leave, i) i->networkGameLeft(); LOG_NET_NOTE("Link to server %s disconnected") << address(); - + d->unmountFileRepository(); AbstractLink::disconnect(); Net_StopGame(); diff --git a/doomsday/sdk/libcore/src/filesys/remotefeedrelay.cpp b/doomsday/sdk/libcore/src/filesys/remotefeedrelay.cpp index 7631e132dc..af1f6c9942 100644 --- a/doomsday/sdk/libcore/src/filesys/remotefeedrelay.cpp +++ b/doomsday/sdk/libcore/src/filesys/remotefeedrelay.cpp @@ -47,7 +47,7 @@ DENG2_PIMPL(RemoteFeedRelay) String path; FileListRequest fileList; FileContentsRequest fileContents; - Block receivedData; + //Block receivedData; duint64 receivedBytes = 0; duint64 fileSize = 0; @@ -81,7 +81,17 @@ DENG2_PIMPL(RemoteFeedRelay) {} virtual ~RepositoryLink() - {} + { + // All queries will be cancelled. + for (auto i = deferredQueries.begin(); i != deferredQueries.end(); ++i) + { + i->cancel(); + } + for (auto i = pendingQueries.begin(); i != pendingQueries.end(); ++i) + { + i.value().cancel(); + } + } virtual void wasConnected() { @@ -164,16 +174,16 @@ DENG2_PIMPL(RemoteFeedRelay) pendingQueries.erase(found); return; } - if (query.receivedData.size() != fileSize) + /*if (query.receivedData.size() != fileSize) { query.receivedData.resize(fileSize); - } + }*/ if (!query.fileSize) { // Before the first chunk, notify about the total size. query.fileContents->call(0, Block(), fileSize); } - query.receivedData.set(startOffset, chunk.data(), chunk.size()); + //query.receivedData.set(startOffset, chunk.data(), chunk.size()); query.fileSize = fileSize; query.receivedBytes += chunk.size(); @@ -243,7 +253,7 @@ DENG2_PIMPL(RemoteFeedRelay) { packet.setQuery(RemoteFeedQueryPacket::ListFiles); } - else + else if (query.fileContents) { packet.setQuery(RemoteFeedQueryPacket::FileContents); } @@ -265,19 +275,15 @@ DENG2_PIMPL(RemoteFeedRelay) switch (d->protocol.recognize(*packet)) { - case RemoteFeedProtocol::Metadata: - { - auto const &md = packet->as(); - metadataReceived(md.id(), md.metadata()); - } - break; + case RemoteFeedProtocol::Metadata: { + auto const &md = packet->as(); + metadataReceived(md.id(), md.metadata()); + break; } - case RemoteFeedProtocol::FileContents: - { - auto const &fc = packet->as(); - chunkReceived(fc.id(), fc.startOffset(), fc.data(), fc.fileSize()); - } - break; + case RemoteFeedProtocol::FileContents: { + auto const &fc = packet->as(); + chunkReceived(fc.id(), fc.startOffset(), fc.data(), fc.fileSize()); + break; } default: break; @@ -327,6 +333,14 @@ RemoteFeed *RemoteFeedRelay::addRepository(String const &address) return nullptr; } +void RemoteFeedRelay::removeRepository(const de::String &address) +{ + if (auto *repo = d->repositories.take(address)) + { + delete repo; + } +} + StringList RemoteFeedRelay::repositories() const { StringList repos; @@ -361,9 +375,17 @@ RemoteFeedRelay::fetchFileContents(String const &repository, String filePath, Da { DENG2_ASSERT(d->repositories.contains(repository)); - auto *repo = d->repositories[repository]; - FileContentsRequest request(new FileContentsRequest::element_type(dataReceived)); - repo->sendQuery(Impl::RepositoryLink::Query(request, filePath)); + Waitable done; + FileContentsRequest request; + Loop::mainCall([&] () + { + // The repository sockets are handled in the main thread. + auto *repo = d->repositories[repository]; + FileContentsRequest request(new FileContentsRequest::element_type(dataReceived)); + repo->sendQuery(Impl::RepositoryLink::Query(request, filePath)); + done.post(); + }); + done.wait(); return request; } diff --git a/doomsday/sdk/libcore/src/filesys/remotefile.cpp b/doomsday/sdk/libcore/src/filesys/remotefile.cpp index e07bf3238e..2987da097e 100644 --- a/doomsday/sdk/libcore/src/filesys/remotefile.cpp +++ b/doomsday/sdk/libcore/src/filesys/remotefile.cpp @@ -44,22 +44,27 @@ DENG2_PIMPL(RemoteFile) } } - void checkCache() + String cachePath() const { - if (self().state() != Ready) + String const hex = remoteMetaId.asHexadecimalText(); + return CACHE_PATH / String(hex.last()) / hex; + } + + void checkCache(bool requireExists = true) + { + if (self().state() == NotReady) { throw UnfetchedError("RemoteFile::operator >>", self().description() + " not downloaded"); } if (!cachedFile) { - cachedFile.reset(FS::tryLocate - (CACHE_PATH / remoteMetaId.asHexadecimalText())); - if (!cachedFile) - { - throw InputError("RemoteFile::operator >>", - self().description() + " has no locally cached data although marked Ready"); - } + cachedFile.reset(FS::tryLocate(cachePath())); + } + if (requireExists && !cachedFile) + { + throw InputError("RemoteFile::operator >>", + self().description() + " has no locally cached data although marked Ready"); } } }; @@ -82,6 +87,15 @@ void RemoteFile::fetchContents() setState(Recovering); + d->checkCache(false /* doesn't have to exist */); + if (d->cachedFile) + { + // There is a cached copy already. + setState(Ready); + reinterpret(); + return; + } + d->fetching = RemoteFeedRelay::get().fetchFileContents (originFeed()->as().repository(), d->remotePath, @@ -105,8 +119,9 @@ void RemoteFile::fetchContents() qDebug() << "[RemoteFile] Complete contents received" << d->buffer.size(); d->fetching = nullptr; - Folder &cacheFolder = FS::get().makeFolder(CACHE_PATH); - File &data = cacheFolder.replaceFile(d->remoteMetaId.asHexadecimalText()); + String const fn = d->cachePath(); + Folder &cacheFolder = FS::get().makeFolder(fn.fileNamePath()); + File &data = cacheFolder.replaceFile(fn); data << d->buffer; data.flush(); @@ -144,6 +159,10 @@ IIStream const &RemoteFile::operator >> (IByteArray &bytes) const String RemoteFile::describe() const { + if (isReady()) + { + return String("\"%1\"").arg(name()); + } return String("remote file \"%1\" (%2)") .arg(name()) .arg( state() == NotReady ? "not ready"