mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-03 22:21:59 +00:00 
			
		
		
		
	Merge branch 'main' into websockets
This commit is contained in:
		
							
								
								
									
										12
									
								
								.github/workflows/build-and-test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/build-and-test.yml
									
									
									
									
										vendored
									
									
								
							@@ -16,12 +16,12 @@ jobs:
 | 
			
		||||
        os: [ windows-2022, ubuntu-22.04, macos-13 ]
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - uses: actions/checkout@v4
 | 
			
		||||
    - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
 | 
			
		||||
      with:
 | 
			
		||||
        submodules: true
 | 
			
		||||
    - name: Add msbuild to PATH (Windows only)
 | 
			
		||||
      if: ${{ matrix.os == 'windows-2022' }}
 | 
			
		||||
      uses: microsoft/setup-msbuild@v2
 | 
			
		||||
      uses: microsoft/setup-msbuild@767f00a3f09872d96a0cb9fcd5e6a4ff33311330
 | 
			
		||||
      with:
 | 
			
		||||
        vs-version: '[17,18)'
 | 
			
		||||
        msbuild-architecture: x64
 | 
			
		||||
@@ -30,12 +30,16 @@ jobs:
 | 
			
		||||
      run: |
 | 
			
		||||
        brew install openssl@3
 | 
			
		||||
        sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
 | 
			
		||||
    - name: Get CMake 3.x
 | 
			
		||||
      uses: lukka/get-cmake@28983e0d3955dba2bb0a6810caae0c6cf268ec0c
 | 
			
		||||
      with:
 | 
			
		||||
        cmakeVersion: "~3.25.0"  # <--= optional, use most recent 3.25.x version
 | 
			
		||||
    - name: cmake
 | 
			
		||||
      uses: lukka/run-cmake@v10
 | 
			
		||||
      uses: lukka/run-cmake@67c73a83a46f86c4e0b96b741ac37ff495478c38
 | 
			
		||||
      with:
 | 
			
		||||
        workflowPreset: "ci-${{matrix.os}}"
 | 
			
		||||
    - name: artifacts
 | 
			
		||||
      uses: actions/upload-artifact@v4
 | 
			
		||||
      uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
 | 
			
		||||
      with:
 | 
			
		||||
        name: build-${{matrix.os}}
 | 
			
		||||
        path: |
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
 | 
			
		||||
 | 
			
		||||
### Windows packages
 | 
			
		||||
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
 | 
			
		||||
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
 | 
			
		||||
You'll also need to download and install [CMake](https://cmake.org/download/) (<font size="4">**version 3.25**</font> up to <font size="4">**version 3.31**</font>!).
 | 
			
		||||
 | 
			
		||||
### MacOS packages
 | 
			
		||||
Ensure you have [brew](https://brew.sh) installed.
 | 
			
		||||
@@ -100,7 +100,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Required CMake version
 | 
			
		||||
This project uses <font size="4">**CMake version 3.25**</font> or higher and as such you will need to ensure you have this version installed.
 | 
			
		||||
This project uses <font size="4">**CMake version 3.25**</font> up to <font size="4">**version 3.31**</font> and as such you will need to ensure you have this version installed.
 | 
			
		||||
You can check your CMake version by using the following command in a terminal.
 | 
			
		||||
```bash
 | 
			
		||||
cmake --version
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,9 @@
 | 
			
		||||
 | 
			
		||||
#include "zlib.h"
 | 
			
		||||
 | 
			
		||||
constexpr uint32_t CRC32_INIT = 0xFFFFFFFF;
 | 
			
		||||
constexpr auto NULL_TERMINATOR = std::string_view{"\0\0\0", 4};
 | 
			
		||||
 | 
			
		||||
AssetManager::AssetManager(const std::filesystem::path& path) {
 | 
			
		||||
	if (!std::filesystem::is_directory(path)) {
 | 
			
		||||
		throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
 | 
			
		||||
@@ -18,12 +21,20 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
 | 
			
		||||
 | 
			
		||||
		m_RootPath = m_Path;
 | 
			
		||||
		m_ResPath = (m_Path / "client" / "res");
 | 
			
		||||
	} else if (std::filesystem::exists(m_Path / ".." / "versions") && std::filesystem::exists(m_Path / "res")) {
 | 
			
		||||
	} else if (std::filesystem::exists(m_Path / "res" / "pack")) {
 | 
			
		||||
		if (!std::filesystem::exists(m_Path / ".." / "versions")) {
 | 
			
		||||
			throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		m_AssetBundleType = eAssetBundleType::Packed;
 | 
			
		||||
 | 
			
		||||
		m_RootPath = (m_Path / "..");
 | 
			
		||||
		m_ResPath = (m_Path / "res");
 | 
			
		||||
	} else if (std::filesystem::exists(m_Path / "pack") && std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
 | 
			
		||||
	} else if (std::filesystem::exists(m_Path / "pack")) {
 | 
			
		||||
		if (!std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
 | 
			
		||||
			throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		m_AssetBundleType = eAssetBundleType::Packed;
 | 
			
		||||
 | 
			
		||||
		m_RootPath = (m_Path / ".." / "..");
 | 
			
		||||
@@ -48,6 +59,7 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case eAssetBundleType::None:
 | 
			
		||||
			[[fallthrough]];
 | 
			
		||||
		case eAssetBundleType::Unpacked: {
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
@@ -55,19 +67,10 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AssetManager::LoadPackIndex() {
 | 
			
		||||
	m_PackIndex = new PackIndex(m_RootPath);
 | 
			
		||||
	m_PackIndex = PackIndex(m_RootPath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::filesystem::path AssetManager::GetResPath() {
 | 
			
		||||
	return m_ResPath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
eAssetBundleType AssetManager::GetAssetBundleType() {
 | 
			
		||||
	return m_AssetBundleType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AssetManager::HasFile(const char* name) {
 | 
			
		||||
	auto fixedName = std::string(name);
 | 
			
		||||
bool AssetManager::HasFile(std::string fixedName) const {
 | 
			
		||||
	std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
 | 
			
		||||
 | 
			
		||||
	// Special case for unpacked client have BrickModels in upper case
 | 
			
		||||
@@ -81,8 +84,7 @@ bool AssetManager::HasFile(const char* name) {
 | 
			
		||||
	std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
 | 
			
		||||
	if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
 | 
			
		||||
 | 
			
		||||
	uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
 | 
			
		||||
	crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
 | 
			
		||||
	const auto crc = crc32b(crc32b(CRC32_INIT, fixedName), NULL_TERMINATOR);
 | 
			
		||||
 | 
			
		||||
	for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
 | 
			
		||||
		if (item.m_Crc == crc) {
 | 
			
		||||
@@ -93,8 +95,7 @@ bool AssetManager::HasFile(const char* name) {
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
 | 
			
		||||
	auto fixedName = std::string(name);
 | 
			
		||||
bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) const {
 | 
			
		||||
	std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
 | 
			
		||||
	std::replace(fixedName.begin(), fixedName.end(), '\\', '/'); // On the off chance someone has the wrong slashes, force forward slashes
 | 
			
		||||
 | 
			
		||||
@@ -129,8 +130,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
 | 
			
		||||
		fixedName = "client\\res\\" + fixedName;
 | 
			
		||||
	}
 | 
			
		||||
	int32_t packIndex = -1;
 | 
			
		||||
	uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
 | 
			
		||||
	crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
 | 
			
		||||
	auto crc = crc32b(crc32b(CRC32_INIT, fixedName), NULL_TERMINATOR);
 | 
			
		||||
 | 
			
		||||
	for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
 | 
			
		||||
		if (item.m_Crc == crc) {
 | 
			
		||||
@@ -144,15 +144,13 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto packs = this->m_PackIndex->GetPacks();
 | 
			
		||||
	auto* pack = packs.at(packIndex);
 | 
			
		||||
 | 
			
		||||
	bool success = pack->ReadFileFromPack(crc, data, len);
 | 
			
		||||
	const auto& pack = this->m_PackIndex->GetPacks().at(packIndex);
 | 
			
		||||
	const bool success = pack.ReadFileFromPack(crc, data, len);
 | 
			
		||||
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AssetStream AssetManager::GetFile(const char* name) {
 | 
			
		||||
AssetStream AssetManager::GetFile(const char* name) const {
 | 
			
		||||
	char* buf; uint32_t len;
 | 
			
		||||
 | 
			
		||||
	bool success = this->GetFile(name, &buf, &len);
 | 
			
		||||
@@ -160,23 +158,15 @@ AssetStream AssetManager::GetFile(const char* name) {
 | 
			
		||||
	return AssetStream(buf, len, success);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
 | 
			
		||||
	size_t i, j;
 | 
			
		||||
	uint32_t crc, msb;
 | 
			
		||||
 | 
			
		||||
	crc = base;
 | 
			
		||||
	for (i = 0; i < l; i++) {
 | 
			
		||||
uint32_t AssetManager::crc32b(uint32_t crc, const std::string_view message) {
 | 
			
		||||
	for (const auto byte : message) {
 | 
			
		||||
		// xor next byte to upper bits of crc
 | 
			
		||||
		crc ^= (static_cast<unsigned int>(message[i]) << 24);
 | 
			
		||||
		for (j = 0; j < 8; j++) { // Do eight times.
 | 
			
		||||
			msb = crc >> 31;
 | 
			
		||||
		crc ^= (static_cast<uint32_t>(std::bit_cast<uint8_t>(byte)) << 24);
 | 
			
		||||
		for (size_t _ = 0; _ < 8; _++) { // Do eight times.
 | 
			
		||||
			const uint32_t msb = crc >> 31;
 | 
			
		||||
			crc <<= 1;
 | 
			
		||||
			crc ^= (0 - msb) & 0x04C11DB7;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return crc; // don't complement crc on output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AssetManager::~AssetManager() {
 | 
			
		||||
	delete m_PackIndex;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -61,23 +61,32 @@ struct AssetStream : std::istream {
 | 
			
		||||
class AssetManager {
 | 
			
		||||
public:
 | 
			
		||||
	AssetManager(const std::filesystem::path& path);
 | 
			
		||||
	~AssetManager();
 | 
			
		||||
 | 
			
		||||
	std::filesystem::path GetResPath();
 | 
			
		||||
	eAssetBundleType GetAssetBundleType();
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	const std::filesystem::path& GetResPath() const {
 | 
			
		||||
		return m_ResPath;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	eAssetBundleType GetAssetBundleType() const {
 | 
			
		||||
		return m_AssetBundleType;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool HasFile(const char* name);
 | 
			
		||||
	bool GetFile(const char* name, char** data, uint32_t* len);
 | 
			
		||||
	AssetStream GetFile(const char* name);
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	bool HasFile(std::string name) const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	bool GetFile(std::string name, char** data, uint32_t* len) const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	AssetStream GetFile(const char* name) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void LoadPackIndex();
 | 
			
		||||
 | 
			
		||||
	// Modified crc algorithm (mpeg2)
 | 
			
		||||
	// Reference: https://stackoverflow.com/questions/54339800/how-to-modify-crc-32-to-crc-32-mpeg-2
 | 
			
		||||
	inline uint32_t crc32b(uint32_t base, uint8_t* message, size_t l);
 | 
			
		||||
 | 
			
		||||
	bool m_SuccessfullyLoaded;
 | 
			
		||||
	static inline uint32_t crc32b(uint32_t crc, std::string_view message);
 | 
			
		||||
 | 
			
		||||
	std::filesystem::path m_Path;
 | 
			
		||||
	std::filesystem::path m_RootPath;
 | 
			
		||||
@@ -85,5 +94,5 @@ private:
 | 
			
		||||
 | 
			
		||||
	eAssetBundleType m_AssetBundleType = eAssetBundleType::None;
 | 
			
		||||
 | 
			
		||||
	PackIndex* m_PackIndex;
 | 
			
		||||
	std::optional<PackIndex> m_PackIndex;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -21,19 +21,20 @@ Pack::Pack(const std::filesystem::path& filePath) {
 | 
			
		||||
 | 
			
		||||
	m_FileStream.seekg(recordCountPos, std::ios::beg);
 | 
			
		||||
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_RecordCount);
 | 
			
		||||
	uint32_t recordCount = 0;
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, recordCount);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < m_RecordCount; i++) {
 | 
			
		||||
	m_Records.reserve(recordCount);
 | 
			
		||||
	std::generate_n(std::back_inserter(m_Records), recordCount, [&] {
 | 
			
		||||
		PackRecord record;
 | 
			
		||||
		BinaryIO::BinaryRead<PackRecord>(m_FileStream, record);
 | 
			
		||||
 | 
			
		||||
		m_Records.push_back(record);
 | 
			
		||||
	}
 | 
			
		||||
		return record;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	m_FileStream.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Pack::HasFile(uint32_t crc) {
 | 
			
		||||
bool Pack::HasFile(const uint32_t crc) const {
 | 
			
		||||
	for (const auto& record : m_Records) {
 | 
			
		||||
		if (record.m_Crc == crc) {
 | 
			
		||||
			return true;
 | 
			
		||||
@@ -43,7 +44,7 @@ bool Pack::HasFile(uint32_t crc) {
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
 | 
			
		||||
bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const {
 | 
			
		||||
	// Time for some wacky C file reading for speed reasons
 | 
			
		||||
 | 
			
		||||
	PackRecord pkRecord{};
 | 
			
		||||
 
 | 
			
		||||
@@ -24,16 +24,17 @@ struct PackRecord {
 | 
			
		||||
class Pack {
 | 
			
		||||
public:
 | 
			
		||||
	Pack(const std::filesystem::path& filePath);
 | 
			
		||||
	~Pack() = default;
 | 
			
		||||
 | 
			
		||||
	bool HasFile(uint32_t crc);
 | 
			
		||||
	bool ReadFileFromPack(uint32_t crc, char** data, uint32_t* len);
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	bool HasFile(uint32_t crc) const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	bool ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) const;
 | 
			
		||||
private:
 | 
			
		||||
	std::ifstream m_FileStream;
 | 
			
		||||
	std::filesystem::path m_FilePath;
 | 
			
		||||
 | 
			
		||||
	char m_Version[7];
 | 
			
		||||
 | 
			
		||||
	uint32_t m_RecordCount;
 | 
			
		||||
	std::vector<PackRecord> m_Records;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,38 +6,32 @@
 | 
			
		||||
PackIndex::PackIndex(const std::filesystem::path& filePath) {
 | 
			
		||||
	m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
 | 
			
		||||
 | 
			
		||||
	uint32_t packPathCount = 0;
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, packPathCount);
 | 
			
		||||
	
 | 
			
		||||
	m_PackPaths.resize(m_PackPathCount);
 | 
			
		||||
	m_PackPaths.resize(packPathCount);
 | 
			
		||||
	for (auto& item : m_PackPaths) {
 | 
			
		||||
		BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
 | 
			
		||||
	uint32_t packFileIndexCount = 0;
 | 
			
		||||
	BinaryIO::BinaryRead<uint32_t>(m_FileStream, packFileIndexCount);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < m_PackFileIndexCount; i++) {
 | 
			
		||||
	m_PackFileIndices.reserve(packFileIndexCount);
 | 
			
		||||
	std::generate_n(std::back_inserter(m_PackFileIndices), packFileIndexCount, [&] {
 | 
			
		||||
		PackFileIndex packFileIndex;
 | 
			
		||||
		BinaryIO::BinaryRead<PackFileIndex>(m_FileStream, packFileIndex);
 | 
			
		||||
 | 
			
		||||
		m_PackFileIndices.push_back(packFileIndex);
 | 
			
		||||
	}
 | 
			
		||||
		return packFileIndex;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
 | 
			
		||||
 | 
			
		||||
	m_Packs.reserve(m_PackPaths.size());
 | 
			
		||||
	for (auto& item : m_PackPaths) {
 | 
			
		||||
		std::replace(item.begin(), item.end(), '\\', '/');
 | 
			
		||||
 | 
			
		||||
		auto* pack = new Pack(filePath / item);
 | 
			
		||||
 | 
			
		||||
		m_Packs.push_back(pack);
 | 
			
		||||
		m_Packs.emplace_back(filePath / item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_FileStream.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PackIndex::~PackIndex() {
 | 
			
		||||
	for (const auto* item : m_Packs) {
 | 
			
		||||
		delete item;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,20 +21,23 @@ struct PackFileIndex {
 | 
			
		||||
class PackIndex {
 | 
			
		||||
public:
 | 
			
		||||
	PackIndex(const std::filesystem::path& filePath);
 | 
			
		||||
	~PackIndex();
 | 
			
		||||
 | 
			
		||||
	const std::vector<std::string>& GetPackPaths() { return m_PackPaths; }
 | 
			
		||||
	const std::vector<PackFileIndex>& GetPackFileIndices() { return m_PackFileIndices; }
 | 
			
		||||
	const std::vector<Pack*>& GetPacks() { return m_Packs; }
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	const std::vector<std::string>& GetPackPaths() const { return m_PackPaths; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	const std::vector<PackFileIndex>& GetPackFileIndices() const { return m_PackFileIndices; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	const std::vector<Pack>& GetPacks() const { return m_Packs; }
 | 
			
		||||
private:
 | 
			
		||||
	std::ifstream m_FileStream;
 | 
			
		||||
 | 
			
		||||
	uint32_t m_Version;
 | 
			
		||||
 | 
			
		||||
	uint32_t m_PackPathCount;
 | 
			
		||||
	std::vector<std::string> m_PackPaths;
 | 
			
		||||
	uint32_t m_PackFileIndexCount;
 | 
			
		||||
 | 
			
		||||
	std::vector<PackFileIndex> m_PackFileIndices;
 | 
			
		||||
 | 
			
		||||
	std::vector<Pack*> m_Packs;
 | 
			
		||||
	std::vector<Pack> m_Packs;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -512,7 +512,7 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
 | 
			
		||||
 | 
			
		||||
		auto* mission = new Mission(this, missionId);
 | 
			
		||||
 | 
			
		||||
		mission->LoadFromXml(*doneM);
 | 
			
		||||
		mission->LoadFromXmlDone(*doneM);
 | 
			
		||||
 | 
			
		||||
		doneM = doneM->NextSiblingElement();
 | 
			
		||||
 | 
			
		||||
@@ -527,9 +527,9 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
 | 
			
		||||
 | 
			
		||||
		currentM->QueryAttribute("id", &missionId);
 | 
			
		||||
 | 
			
		||||
		auto* mission = new Mission(this, missionId);
 | 
			
		||||
		auto* mission = m_Missions.contains(missionId) ? m_Missions[missionId] : new Mission(this, missionId);
 | 
			
		||||
 | 
			
		||||
		mission->LoadFromXml(*currentM);
 | 
			
		||||
		mission->LoadFromXmlCur(*currentM);
 | 
			
		||||
 | 
			
		||||
		if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
 | 
			
		||||
			mission->SetUniqueMissionOrderID(missionOrder);
 | 
			
		||||
@@ -565,20 +565,23 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
 | 
			
		||||
		auto* mission = pair.second;
 | 
			
		||||
 | 
			
		||||
		if (mission) {
 | 
			
		||||
			const auto complete = mission->IsComplete();
 | 
			
		||||
			const auto completions = mission->GetCompletions();
 | 
			
		||||
 | 
			
		||||
			auto* m = doc.NewElement("m");
 | 
			
		||||
 | 
			
		||||
			if (complete) {
 | 
			
		||||
				mission->UpdateXml(*m);
 | 
			
		||||
			if (completions > 0) {
 | 
			
		||||
				mission->UpdateXmlDone(*m);
 | 
			
		||||
 | 
			
		||||
				done->LinkEndChild(m);
 | 
			
		||||
 | 
			
		||||
				continue;
 | 
			
		||||
				if (mission->IsComplete()) continue;
 | 
			
		||||
 | 
			
		||||
				m = doc.NewElement("m");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
 | 
			
		||||
 | 
			
		||||
			mission->UpdateXml(*m);
 | 
			
		||||
			mission->UpdateXmlCur(*m);
 | 
			
		||||
 | 
			
		||||
			cur->LinkEndChild(m);
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
 | 
			
		||||
void Mission::LoadFromXmlDone(const tinyxml2::XMLElement& element) {
 | 
			
		||||
	// Start custom XML
 | 
			
		||||
	if (element.Attribute("state") != nullptr) {
 | 
			
		||||
		m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
 | 
			
		||||
@@ -76,11 +76,15 @@ void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
 | 
			
		||||
		m_Completions = std::stoul(element.Attribute("cct"));
 | 
			
		||||
 | 
			
		||||
		m_Timestamp = std::stoul(element.Attribute("cts"));
 | 
			
		||||
 | 
			
		||||
		if (IsComplete()) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
 | 
			
		||||
	// Start custom XML
 | 
			
		||||
	if (element.Attribute("state") != nullptr) {
 | 
			
		||||
		m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
 | 
			
		||||
	}
 | 
			
		||||
	// End custom XML
 | 
			
		||||
 | 
			
		||||
	auto* task = element.FirstChildElement();
 | 
			
		||||
 | 
			
		||||
@@ -132,7 +136,7 @@ void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Mission::UpdateXml(tinyxml2::XMLElement& element) {
 | 
			
		||||
void Mission::UpdateXmlDone(tinyxml2::XMLElement& element) {
 | 
			
		||||
	// Start custom XML
 | 
			
		||||
	element.SetAttribute("state", static_cast<unsigned int>(m_State));
 | 
			
		||||
	// End custom XML
 | 
			
		||||
@@ -141,15 +145,21 @@ void Mission::UpdateXml(tinyxml2::XMLElement& element) {
 | 
			
		||||
 | 
			
		||||
	element.SetAttribute("id", static_cast<unsigned int>(info.id));
 | 
			
		||||
 | 
			
		||||
	if (m_Completions > 0) {
 | 
			
		||||
		element.SetAttribute("cct", static_cast<unsigned int>(m_Completions));
 | 
			
		||||
	element.SetAttribute("cct", static_cast<unsigned int>(m_Completions));
 | 
			
		||||
 | 
			
		||||
		element.SetAttribute("cts", static_cast<unsigned int>(m_Timestamp));
 | 
			
		||||
	element.SetAttribute("cts", static_cast<unsigned int>(m_Timestamp));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		if (IsComplete()) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
void Mission::UpdateXmlCur(tinyxml2::XMLElement& element) {
 | 
			
		||||
	// Start custom XML
 | 
			
		||||
	element.SetAttribute("state", static_cast<unsigned int>(m_State));
 | 
			
		||||
	// End custom XML
 | 
			
		||||
 | 
			
		||||
	element.DeleteChildren();
 | 
			
		||||
 | 
			
		||||
	element.SetAttribute("id", static_cast<unsigned int>(info.id));
 | 
			
		||||
 | 
			
		||||
	if (IsComplete()) return;
 | 
			
		||||
 | 
			
		||||
	for (auto* task : m_Tasks) {
 | 
			
		||||
		if (task->GetType() == eMissionTaskType::COLLECTION ||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,13 @@ public:
 | 
			
		||||
	Mission(MissionComponent* missionComponent, uint32_t missionId);
 | 
			
		||||
	~Mission();
 | 
			
		||||
 | 
			
		||||
	void LoadFromXml(const tinyxml2::XMLElement& element);
 | 
			
		||||
	void UpdateXml(tinyxml2::XMLElement& element);
 | 
			
		||||
	// XML functions to load and save completed mission state to xml
 | 
			
		||||
	void LoadFromXmlDone(const tinyxml2::XMLElement& element);
 | 
			
		||||
	void UpdateXmlDone(tinyxml2::XMLElement& element);
 | 
			
		||||
 | 
			
		||||
	// XML functions to load and save current mission state and task data to xml
 | 
			
		||||
	void LoadFromXmlCur(const tinyxml2::XMLElement& element);
 | 
			
		||||
	void UpdateXmlCur(tinyxml2::XMLElement& element);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Returns the ID of this mission
 | 
			
		||||
 
 | 
			
		||||
@@ -181,6 +181,7 @@ namespace Mail {
 | 
			
		||||
 | 
			
		||||
	void AttachmentCollectRequest::Handle() {
 | 
			
		||||
		AttachmentCollectResponse response;
 | 
			
		||||
		response.mailID = mailID;
 | 
			
		||||
		auto inv = player->GetComponent<InventoryComponent>();
 | 
			
		||||
 | 
			
		||||
		if (mailID > 0 && playerID == player->GetObjectID() && inv) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user