/* 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