Skip to content

Commit

Permalink
FS|Network: Disconnecting from a remote repository
Browse files Browse the repository at this point in the history
The server's folder is deleted and pending queries are cancelled.

RemoteFile will use an existing cached copy of a remote file is
one is available (according to metaId).
  • Loading branch information
skyjake committed Oct 15, 2017
1 parent 0736e2d commit ff17605
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 38 deletions.
30 changes: 24 additions & 6 deletions doomsday/apps/client/src/network/serverlink.cpp
Expand Up @@ -34,6 +34,7 @@
#include <de/BlockPacket>
#include <de/ByteRefArray>
#include <de/ByteSubArray>
#include <de/Garbage>
#include <de/GuiApp>
#include <de/Message>
#include <de/MessageDialog>
Expand Down Expand Up @@ -81,7 +82,8 @@ DENG2_PIMPL(ServerLink)
std::unique_ptr<GameProfile> serverProfile; ///< Profile used when joining.
std::function<void (GameProfile const *)> profileResultCallback;
std::function<void (Address, GameProfile const *)> profileResultCallbackWithAddress;
LoopCallback mainCall;
String fileRepository;
LoopCallback mainCall; // for deferred actions

Impl(Public *i, Flags flags)
: Base(i)
Expand Down Expand Up @@ -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<Folder>("/remote/server"))
{
trash(remoteFiles);
}
RemoteFeedRelay::get().removeRepository(fileRepository);
fileRepository.clear();
}

DENG2_PIMPL_AUDIENCE(DiscoveryUpdate)
DENG2_PIMPL_AUDIENCE(PingResponse)
DENG2_PIMPL_AUDIENCE(MapOutline)
Expand Down Expand Up @@ -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())
{
Expand Down Expand Up @@ -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();
Expand Down
64 changes: 43 additions & 21 deletions doomsday/sdk/libcore/src/filesys/remotefeedrelay.cpp
Expand Up @@ -47,7 +47,7 @@ DENG2_PIMPL(RemoteFeedRelay)
String path;
FileListRequest fileList;
FileContentsRequest fileContents;
Block receivedData;
//Block receivedData;
duint64 receivedBytes = 0;
duint64 fileSize = 0;

Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -243,7 +253,7 @@ DENG2_PIMPL(RemoteFeedRelay)
{
packet.setQuery(RemoteFeedQueryPacket::ListFiles);
}
else
else if (query.fileContents)
{
packet.setQuery(RemoteFeedQueryPacket::FileContents);
}
Expand All @@ -265,19 +275,15 @@ DENG2_PIMPL(RemoteFeedRelay)

switch (d->protocol.recognize(*packet))
{
case RemoteFeedProtocol::Metadata:
{
auto const &md = packet->as<RemoteFeedMetadataPacket>();
metadataReceived(md.id(), md.metadata());
}
break;
case RemoteFeedProtocol::Metadata: {
auto const &md = packet->as<RemoteFeedMetadataPacket>();
metadataReceived(md.id(), md.metadata());
break; }

case RemoteFeedProtocol::FileContents:
{
auto const &fc = packet->as<RemoteFeedFileContentsPacket>();
chunkReceived(fc.id(), fc.startOffset(), fc.data(), fc.fileSize());
}
break;
case RemoteFeedProtocol::FileContents: {
auto const &fc = packet->as<RemoteFeedFileContentsPacket>();
chunkReceived(fc.id(), fc.startOffset(), fc.data(), fc.fileSize());
break; }

default:
break;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
41 changes: 30 additions & 11 deletions doomsday/sdk/libcore/src/filesys/remotefile.cpp
Expand Up @@ -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<File const>
(CACHE_PATH / remoteMetaId.asHexadecimalText()));
if (!cachedFile)
{
throw InputError("RemoteFile::operator >>",
self().description() + " has no locally cached data although marked Ready");
}
cachedFile.reset(FS::tryLocate<File const>(cachePath()));
}
if (requireExists && !cachedFile)
{
throw InputError("RemoteFile::operator >>",
self().description() + " has no locally cached data although marked Ready");
}
}
};
Expand All @@ -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<RemoteFeed>().repository(),
d->remotePath,
Expand All @@ -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();

Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit ff17605

Please sign in to comment.