/*
Copyright (c) 2019 yvt
This file is part of OpenSpades.
OpenSpades is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see .
*/
#include
#include "GameMap.h"
#include "GameMapLoader.h"
#include
#include
#include
#include
#include
#include
namespace spades {
namespace client {
struct GameMapLoader::Result {
// The following fields are mutually exclusive.
std::exception_ptr exceptionThrown;
Handle gameMap;
};
struct GameMapLoader::Decoder : public IRunnable {
GameMapLoader &parent;
std::unique_ptr rawDataReader;
Decoder(GameMapLoader &parent, std::unique_ptr rawDataReader)
: parent{parent}, rawDataReader{std::move(rawDataReader)} {}
void Run() override {
SPADES_MARK_FUNCTION();
auto result = stmp::make_unique();
try {
DeflateStream inflate(rawDataReader.get(), CompressModeDecompress, false);
GameMap *gameMapPtr =
GameMap::Load(&inflate, [this](int x) { HandleProgress(x); });
result->gameMap = Handle{gameMapPtr, false};
} catch (...) {
// Capture the current exception
result->exceptionThrown = std::current_exception();
}
// Send back the result
parent.resultCell.store(std::move(result));
}
void HandleProgress(int numColumnsLoaded) {
parent.progressCell.store(numColumnsLoaded);
}
};
GameMapLoader::GameMapLoader() : progressCell{0} {
SPADES_MARK_FUNCTION();
auto pipe = CreatePipeStream();
rawDataWriter = std::move(std::get<0>(pipe));
auto rawDataReader = std::move(std::get<1>(pipe));
decodingThreadRunnable = stmp::make_unique(*this, std::move(rawDataReader));
decodingThread = stmp::make_unique(&*decodingThreadRunnable);
decodingThread->Start();
}
GameMapLoader::~GameMapLoader() {
SPADES_MARK_FUNCTION();
// Hang up the writer. This causes the decoder thread to exit gracefully.
rawDataWriter.reset();
decodingThread->Join();
decodingThread.reset();
}
void GameMapLoader::AddRawChunk(const char *bytes, std::size_t numBytes) {
SPADES_MARK_FUNCTION();
if (!rawDataWriter) {
SPRaise("The raw data channel is already closed.");
}
rawDataWriter->Write(bytes, numBytes);
}
void GameMapLoader::MarkEOF() {
SPADES_MARK_FUNCTION();
if (!rawDataWriter) {
SPRaise("The raw data channel is already closed.");
}
rawDataWriter.reset();
}
bool GameMapLoader::IsComplete() const { return resultCell.operator bool(); }
void GameMapLoader::WaitComplete() {
SPADES_MARK_FUNCTION();
decodingThread->Join();
SPAssert(IsComplete());
}
float GameMapLoader::GetProgress() {
return static_cast(progressCell.load(std::memory_order_relaxed)) / (512 * 512);
}
Handle GameMapLoader::TakeGameMap() {
SPADES_MARK_FUNCTION();
SPAssert(IsComplete());
std::unique_ptr result = resultCell.take();
SPAssert(result);
if (result->gameMap) {
return std::move(result->gameMap);
} else {
std::rethrow_exception(result->exceptionThrown);
}
}
} // namespace client
} // namespace spades