mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-03 22:21:59 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/main'
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -14,12 +14,6 @@
 | 
			
		||||
	path = thirdparty/mariadb-connector-cpp
 | 
			
		||||
	url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
 | 
			
		||||
	ignore = dirty
 | 
			
		||||
[submodule "thirdparty/docker-utils"]
 | 
			
		||||
	path = thirdparty/docker-utils
 | 
			
		||||
	url = https://github.com/lcdr/utils.git
 | 
			
		||||
[submodule "thirdparty/LUnpack"]
 | 
			
		||||
	path = thirdparty/LUnpack
 | 
			
		||||
	url = https://github.com/Xiphoseer/LUnpack.git
 | 
			
		||||
[submodule "thirdparty/AccountManager"]
 | 
			
		||||
	path = thirdparty/AccountManager
 | 
			
		||||
	url = https://github.com/DarkflameUniverse/AccountManager
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop or on Linux normal Docker)
 | 
			
		||||
- [Docker Compose](https://docs.docker.com/compose/install/) (Included in Docker Desktop)
 | 
			
		||||
- LEGO® Universe packed Client. Check the main [README](./README.md) for details on this.
 | 
			
		||||
- LEGO® Universe Client. Check the main [README](./README.md) for details on this.
 | 
			
		||||
 | 
			
		||||
## Run server inside Docker
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
11. Once the command has completed (you can see you path again and can enter commands), close the window.
 | 
			
		||||
12. Inside the downloaded folder, copy `.env.example` and name the copy `.env`
 | 
			
		||||
13. Open `.env` with Notepad by right-clicking it and selecting _Open With_ -> _More apps_ -> _Notepad_.
 | 
			
		||||
14. Change the text after `CLIENT_PATH=` to the location of your client. The folder you are pointing to must contain a folder called `client` which should contain the client files.
 | 
			
		||||
14. Change the text after `CLIENT_PATH=` to the location of your client. This folder must contain either a folder `client` or `legouniverse.exe`.
 | 
			
		||||
    > If you need the extra performance, place the client files in `\\wsl$\<your linux OS>\...` to avoid working across file systems, see [Docker Best Practices](https://docs.docker.com/desktop/windows/wsl/#best-practices) and [WSL documentation](https://docs.microsoft.com/en-us/windows/wsl/filesystems#file-storage-and-performance-across-file-systems).
 | 
			
		||||
 | 
			
		||||
15. Optionally, you can change the number after `BUILD_THREADS=` to the number of cores / threads your processor has. If your computer crashes while building, you can try to reduce this value.
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include "GeneralUtils.h"
 | 
			
		||||
#include "Game.h"
 | 
			
		||||
#include "dLogger.h"
 | 
			
		||||
#include "AssetManager.h"
 | 
			
		||||
 | 
			
		||||
#include "eSqliteDataType.h"
 | 
			
		||||
 | 
			
		||||
@@ -23,26 +24,23 @@ std::map<eSqliteDataType, std::string> FdbToSqlite::Convert::m_SqliteType = {
 | 
			
		||||
			{ eSqliteDataType::TEXT_8, "text_8"}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
FdbToSqlite::Convert::Convert(std::string basePath, std::string binaryOutPath) {
 | 
			
		||||
	this->m_BasePath = basePath;
 | 
			
		||||
FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
 | 
			
		||||
	this->m_BinaryOutPath = binaryOutPath;
 | 
			
		||||
	m_Fdb.open(m_BasePath + "/cdclient.fdb", std::ios::binary);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FdbToSqlite::Convert::~Convert() {
 | 
			
		||||
	this->m_Fdb.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FdbToSqlite::Convert::ConvertDatabase() {
 | 
			
		||||
bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) {
 | 
			
		||||
	if (m_ConversionStarted) return false;
 | 
			
		||||
 | 
			
		||||
	std::istream cdClientBuffer(&buffer);
 | 
			
		||||
 | 
			
		||||
	this->m_ConversionStarted = true;
 | 
			
		||||
	try {
 | 
			
		||||
		CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
 | 
			
		||||
 | 
			
		||||
		CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
 | 
			
		||||
 | 
			
		||||
		int32_t numberOfTables = ReadInt32();
 | 
			
		||||
		ReadTables(numberOfTables);
 | 
			
		||||
		int32_t numberOfTables = ReadInt32(cdClientBuffer);
 | 
			
		||||
		ReadTables(numberOfTables, cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		CDClientDatabase::ExecuteQuery("COMMIT;");
 | 
			
		||||
	} catch (CppSQLite3Exception& e) {
 | 
			
		||||
@@ -53,130 +51,130 @@ bool FdbToSqlite::Convert::ConvertDatabase() {
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t FdbToSqlite::Convert::ReadInt32() {
 | 
			
		||||
int32_t FdbToSqlite::Convert::ReadInt32(std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t nextInt{};
 | 
			
		||||
	BinaryIO::BinaryRead(m_Fdb, nextInt);
 | 
			
		||||
	BinaryIO::BinaryRead(cdClientBuffer, nextInt);
 | 
			
		||||
	return nextInt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t FdbToSqlite::Convert::ReadInt64() {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
int64_t FdbToSqlite::Convert::ReadInt64(std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	int64_t value{};
 | 
			
		||||
	BinaryIO::BinaryRead(m_Fdb, value);
 | 
			
		||||
	BinaryIO::BinaryRead(cdClientBuffer, value);
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
	return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string FdbToSqlite::Convert::ReadString() {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
std::string FdbToSqlite::Convert::ReadString(std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	auto readString = BinaryIO::ReadString(m_Fdb);
 | 
			
		||||
	auto readString = BinaryIO::ReadString(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
	return readString;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t FdbToSqlite::Convert::SeekPointer() {
 | 
			
		||||
int32_t FdbToSqlite::Convert::SeekPointer(std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t position{};
 | 
			
		||||
	BinaryIO::BinaryRead(m_Fdb, position);
 | 
			
		||||
	int32_t prevPosition = m_Fdb.tellg();
 | 
			
		||||
	m_Fdb.seekg(position);
 | 
			
		||||
	BinaryIO::BinaryRead(cdClientBuffer, position);
 | 
			
		||||
	int32_t prevPosition = cdClientBuffer.tellg();
 | 
			
		||||
	cdClientBuffer.seekg(position);
 | 
			
		||||
	return prevPosition;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string FdbToSqlite::Convert::ReadColumnHeader() {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
std::string FdbToSqlite::Convert::ReadColumnHeader(std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	int32_t numberOfColumns = ReadInt32();
 | 
			
		||||
	std::string tableName = ReadString();
 | 
			
		||||
	int32_t numberOfColumns = ReadInt32(cdClientBuffer);
 | 
			
		||||
	std::string tableName = ReadString(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	auto columns = ReadColumns(numberOfColumns);
 | 
			
		||||
	auto columns = ReadColumns(numberOfColumns, cdClientBuffer);
 | 
			
		||||
	std::string newTable = "CREATE TABLE IF NOT EXISTS '" + tableName + "' (" + columns + ");";
 | 
			
		||||
	CDClientDatabase::ExecuteDML(newTable);
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
 | 
			
		||||
	return tableName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FdbToSqlite::Convert::ReadTables(int32_t& numberOfTables) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
void FdbToSqlite::Convert::ReadTables(int32_t& numberOfTables, std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	for (int32_t i = 0; i < numberOfTables; i++) {
 | 
			
		||||
		auto columnHeader = ReadColumnHeader();
 | 
			
		||||
		ReadRowHeader(columnHeader);
 | 
			
		||||
		auto columnHeader = ReadColumnHeader(cdClientBuffer);
 | 
			
		||||
		ReadRowHeader(columnHeader, cdClientBuffer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string FdbToSqlite::Convert::ReadColumns(int32_t& numberOfColumns) {
 | 
			
		||||
std::string FdbToSqlite::Convert::ReadColumns(int32_t& numberOfColumns, std::istream& cdClientBuffer) {
 | 
			
		||||
	std::stringstream columnsToCreate;
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	std::string name{};
 | 
			
		||||
	eSqliteDataType dataType{};
 | 
			
		||||
	for (int32_t i = 0; i < numberOfColumns; i++) {
 | 
			
		||||
		if (i != 0) columnsToCreate << ", ";
 | 
			
		||||
		dataType = static_cast<eSqliteDataType>(ReadInt32());
 | 
			
		||||
		name = ReadString();
 | 
			
		||||
		dataType = static_cast<eSqliteDataType>(ReadInt32(cdClientBuffer));
 | 
			
		||||
		name = ReadString(cdClientBuffer);
 | 
			
		||||
		columnsToCreate << "'" << name << "' " << FdbToSqlite::Convert::m_SqliteType[dataType];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
	return columnsToCreate.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FdbToSqlite::Convert::ReadRowHeader(std::string& tableName) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
void FdbToSqlite::Convert::ReadRowHeader(std::string& tableName, std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	int32_t numberOfAllocatedRows = ReadInt32();
 | 
			
		||||
	int32_t numberOfAllocatedRows = ReadInt32(cdClientBuffer);
 | 
			
		||||
	if (numberOfAllocatedRows != 0) assert((numberOfAllocatedRows & (numberOfAllocatedRows - 1)) == 0);  // assert power of 2 allocation size
 | 
			
		||||
	ReadRows(numberOfAllocatedRows, tableName);
 | 
			
		||||
	ReadRows(numberOfAllocatedRows, tableName, cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FdbToSqlite::Convert::ReadRows(int32_t& numberOfAllocatedRows, std::string& tableName) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
void FdbToSqlite::Convert::ReadRows(int32_t& numberOfAllocatedRows, std::string& tableName, std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	int32_t rowid = 0;
 | 
			
		||||
	for (int32_t row = 0; row < numberOfAllocatedRows; row++) {
 | 
			
		||||
		int32_t rowPointer = ReadInt32();
 | 
			
		||||
		int32_t rowPointer = ReadInt32(cdClientBuffer);
 | 
			
		||||
		if (rowPointer == -1) rowid++;
 | 
			
		||||
		else ReadRow(rowPointer, tableName);
 | 
			
		||||
		else ReadRow(rowPointer, tableName, cdClientBuffer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FdbToSqlite::Convert::ReadRow(int32_t& position, std::string& tableName) {
 | 
			
		||||
	int32_t prevPosition = m_Fdb.tellg();
 | 
			
		||||
	m_Fdb.seekg(position);
 | 
			
		||||
void FdbToSqlite::Convert::ReadRow(int32_t& position, std::string& tableName, std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = cdClientBuffer.tellg();
 | 
			
		||||
	cdClientBuffer.seekg(position);
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		ReadRowInfo(tableName);
 | 
			
		||||
		int32_t linked = ReadInt32();
 | 
			
		||||
		ReadRowInfo(tableName, cdClientBuffer);
 | 
			
		||||
		int32_t linked = ReadInt32(cdClientBuffer);
 | 
			
		||||
		if (linked == -1) break;
 | 
			
		||||
		m_Fdb.seekg(linked);
 | 
			
		||||
		cdClientBuffer.seekg(linked);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FdbToSqlite::Convert::ReadRowInfo(std::string& tableName) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
void FdbToSqlite::Convert::ReadRowInfo(std::string& tableName, std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	int32_t numberOfColumns = ReadInt32();
 | 
			
		||||
	ReadRowValues(numberOfColumns, tableName);
 | 
			
		||||
	int32_t numberOfColumns = ReadInt32(cdClientBuffer);
 | 
			
		||||
	ReadRowValues(numberOfColumns, tableName, cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& tableName) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer();
 | 
			
		||||
void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& tableName, std::istream& cdClientBuffer) {
 | 
			
		||||
	int32_t prevPosition = SeekPointer(cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
	int32_t emptyValue{};
 | 
			
		||||
	int32_t intValue{};
 | 
			
		||||
@@ -190,26 +188,26 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string&
 | 
			
		||||
 | 
			
		||||
	for (int32_t i = 0; i < numberOfColumns; i++) {
 | 
			
		||||
		if (i != 0) insertedRow << ", "; // Only append comma and space after first entry in row.
 | 
			
		||||
		switch (static_cast<eSqliteDataType>(ReadInt32())) {
 | 
			
		||||
		switch (static_cast<eSqliteDataType>(ReadInt32(cdClientBuffer))) {
 | 
			
		||||
		case eSqliteDataType::NONE:
 | 
			
		||||
			BinaryIO::BinaryRead(m_Fdb, emptyValue);
 | 
			
		||||
			BinaryIO::BinaryRead(cdClientBuffer, emptyValue);
 | 
			
		||||
			assert(emptyValue == 0);
 | 
			
		||||
			insertedRow << "NULL";
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case eSqliteDataType::INT32:
 | 
			
		||||
			intValue = ReadInt32();
 | 
			
		||||
			intValue = ReadInt32(cdClientBuffer);
 | 
			
		||||
			insertedRow << intValue;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case eSqliteDataType::REAL:
 | 
			
		||||
			BinaryIO::BinaryRead(m_Fdb, floatValue);
 | 
			
		||||
			BinaryIO::BinaryRead(cdClientBuffer, floatValue);
 | 
			
		||||
			insertedRow << std::fixed << std::setprecision(34) << floatValue; // maximum precision of floating point number
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case eSqliteDataType::TEXT_4:
 | 
			
		||||
		case eSqliteDataType::TEXT_8: {
 | 
			
		||||
			stringValue = ReadString();
 | 
			
		||||
			stringValue = ReadString(cdClientBuffer);
 | 
			
		||||
			size_t position = 0;
 | 
			
		||||
 | 
			
		||||
			// Need to escape quote with a double of ".
 | 
			
		||||
@@ -225,12 +223,12 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string&
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		case eSqliteDataType::INT_BOOL:
 | 
			
		||||
			BinaryIO::BinaryRead(m_Fdb, boolValue);
 | 
			
		||||
			BinaryIO::BinaryRead(cdClientBuffer, boolValue);
 | 
			
		||||
			insertedRow << static_cast<bool>(boolValue);
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case eSqliteDataType::INT64:
 | 
			
		||||
			int64Value = ReadInt64();
 | 
			
		||||
			int64Value = ReadInt64(cdClientBuffer);
 | 
			
		||||
			insertedRow << std::to_string(int64Value);
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
@@ -245,5 +243,5 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string&
 | 
			
		||||
 | 
			
		||||
	auto copiedString = insertedRow.str();
 | 
			
		||||
	CDClientDatabase::ExecuteDML(copiedString);
 | 
			
		||||
	m_Fdb.seekg(prevPosition);
 | 
			
		||||
	cdClientBuffer.seekg(prevPosition);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@
 | 
			
		||||
#include <iosfwd>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
class AssetMemoryBuffer;
 | 
			
		||||
 | 
			
		||||
enum class eSqliteDataType : int32_t;
 | 
			
		||||
 | 
			
		||||
namespace FdbToSqlite {
 | 
			
		||||
@@ -18,33 +20,28 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * @param inputFile The file which ends in .fdb to be converted
 | 
			
		||||
		 * @param binaryPath The base path where the file will be saved
 | 
			
		||||
		 */
 | 
			
		||||
		Convert(std::string inputFile, std::string binaryOutPath);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Destroy the convert object and close the fdb file.
 | 
			
		||||
		 */
 | 
			
		||||
		~Convert();
 | 
			
		||||
		Convert(std::string binaryOutPath);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Converts the input file to sqlite.  Calling multiple times is safe.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @return true if the database was converted properly, false otherwise. 
 | 
			
		||||
		 */
 | 
			
		||||
		bool ConvertDatabase();
 | 
			
		||||
		bool ConvertDatabase(AssetMemoryBuffer& buffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads a 32 bit int from the fdb file.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @return The read value
 | 
			
		||||
		 */
 | 
			
		||||
		int32_t ReadInt32();
 | 
			
		||||
		int32_t ReadInt32(std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads a 64 bit integer from the fdb file.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @return The read value
 | 
			
		||||
		 */
 | 
			
		||||
		int64_t ReadInt64();
 | 
			
		||||
		int64_t ReadInt64(std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads a string from the fdb file.
 | 
			
		||||
@@ -53,28 +50,28 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * 
 | 
			
		||||
		 * TODO This needs to be translated to latin-1!
 | 
			
		||||
		 */
 | 
			
		||||
		std::string ReadString();
 | 
			
		||||
		std::string ReadString(std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Seeks to a pointer position.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @return The previous position before the seek
 | 
			
		||||
		 */
 | 
			
		||||
		int32_t SeekPointer();
 | 
			
		||||
		int32_t SeekPointer(std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads a column header from the fdb file and creates the table in the database
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @return The table name
 | 
			
		||||
		 */
 | 
			
		||||
		std::string ReadColumnHeader();
 | 
			
		||||
		std::string ReadColumnHeader(std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Read the tables from the fdb file.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @param numberOfTables The number of tables to read
 | 
			
		||||
		 */
 | 
			
		||||
		void ReadTables(int32_t& numberOfTables);
 | 
			
		||||
		void ReadTables(int32_t& numberOfTables, std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads the columns from the fdb file.
 | 
			
		||||
@@ -82,14 +79,14 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * @param numberOfColumns The number of columns to read
 | 
			
		||||
		 * @return All columns of the table formatted for a sql query
 | 
			
		||||
		 */
 | 
			
		||||
		std::string ReadColumns(int32_t& numberOfColumns);
 | 
			
		||||
		std::string ReadColumns(int32_t& numberOfColumns, std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads the row header from the fdb file.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @param tableName The tables name
 | 
			
		||||
		 */
 | 
			
		||||
		void ReadRowHeader(std::string& tableName);
 | 
			
		||||
		void ReadRowHeader(std::string& tableName, std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Read the rows from the fdb file.,
 | 
			
		||||
@@ -97,7 +94,7 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * @param numberOfAllocatedRows The number of rows that were allocated.  Always a power of 2! 
 | 
			
		||||
		 * @param tableName The tables name.
 | 
			
		||||
		 */
 | 
			
		||||
		void ReadRows(int32_t& numberOfAllocatedRows, std::string& tableName);
 | 
			
		||||
		void ReadRows(int32_t& numberOfAllocatedRows, std::string& tableName, std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads a row from the fdb file.
 | 
			
		||||
@@ -105,14 +102,14 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * @param position The position to seek in the fdb to
 | 
			
		||||
		 * @param tableName The tables name
 | 
			
		||||
		 */
 | 
			
		||||
		void ReadRow(int32_t& position, std::string& tableName);
 | 
			
		||||
		void ReadRow(int32_t& position, std::string& tableName, std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads the row info from the fdb file.
 | 
			
		||||
		 * 
 | 
			
		||||
		 * @param tableName The tables name
 | 
			
		||||
		 */
 | 
			
		||||
		void ReadRowInfo(std::string& tableName);
 | 
			
		||||
		void ReadRowInfo(std::string& tableName, std::istream& cdClientBuffer);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Reads each row and its values from the fdb file and inserts them into the database
 | 
			
		||||
@@ -120,7 +117,7 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * @param numberOfColumns The number of columns to read in
 | 
			
		||||
		 * @param tableName The tables name
 | 
			
		||||
		 */
 | 
			
		||||
		void ReadRowValues(int32_t& numberOfColumns, std::string& tableName);
 | 
			
		||||
		void ReadRowValues(int32_t& numberOfColumns, std::string& tableName, std::istream& cdClientBuffer);
 | 
			
		||||
	private:
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
@@ -132,11 +129,6 @@ namespace FdbToSqlite {
 | 
			
		||||
		 * Base path of the folder containing the fdb file
 | 
			
		||||
		 */
 | 
			
		||||
		std::string m_BasePath{};
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * ifstream containing the fdb file
 | 
			
		||||
		 */
 | 
			
		||||
		std::ifstream m_Fdb{};
 | 
			
		||||
		
 | 
			
		||||
		/**
 | 
			
		||||
		 * Whether or not a conversion was started.  If one was started, do not attempt to convert the file again.
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,6 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
 | 
			
		||||
	switch (m_AssetBundleType) {
 | 
			
		||||
		case eAssetBundleType::Packed: {
 | 
			
		||||
			this->LoadPackIndex();
 | 
			
		||||
 | 
			
		||||
			this->UnpackRequiredAssets();
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -160,31 +157,6 @@ AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) {
 | 
			
		||||
	return AssetMemoryBuffer(buf, len, success);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AssetManager::UnpackRequiredAssets() {
 | 
			
		||||
	if (std::filesystem::exists(m_ResPath / "cdclient.fdb")) return;
 | 
			
		||||
 | 
			
		||||
	char* data;
 | 
			
		||||
	uint32_t size;
 | 
			
		||||
 | 
			
		||||
	bool success = this->GetFile("cdclient.fdb", &data, &size);
 | 
			
		||||
 | 
			
		||||
	if (!success) {
 | 
			
		||||
		Game::logger->Log("AssetManager", "Failed to extract required files from the packs.");
 | 
			
		||||
 | 
			
		||||
		delete data;
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::ofstream cdclientOutput(m_ResPath / "cdclient.fdb", std::ios::out | std::ios::binary);
 | 
			
		||||
	cdclientOutput.write(data, size);
 | 
			
		||||
	cdclientOutput.close();
 | 
			
		||||
 | 
			
		||||
	delete data;
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
 | 
			
		||||
	size_t i, j;
 | 
			
		||||
	uint32_t crc, msb;
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,6 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void LoadPackIndex();
 | 
			
		||||
	void UnpackRequiredAssets();
 | 
			
		||||
 | 
			
		||||
	// Modified crc algorithm (mpeg2)
 | 
			
		||||
	// Reference: https://stackoverflow.com/questions/54339800/how-to-modify-crc-32-to-crc-32-mpeg-2
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ void dLogger::Log(const std::string& className, const std::string& message) {
 | 
			
		||||
void dLogger::LogDebug(const char* className, const char* format, ...) {
 | 
			
		||||
	if (!m_logDebugStatements) return;
 | 
			
		||||
	va_list args;
 | 
			
		||||
	std::string log = "[" + std::string(className) + "] " + std::string(format);
 | 
			
		||||
	std::string log = "[" + std::string(className) + "] " + std::string(format) + "\n";
 | 
			
		||||
	va_start(args, format);
 | 
			
		||||
	vLog(log.c_str(), args);
 | 
			
		||||
	va_end(args);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@
 | 
			
		||||
#include "RebuildComponent.h"
 | 
			
		||||
#include "DestroyableComponent.h"
 | 
			
		||||
 | 
			
		||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) : Component(parent) {
 | 
			
		||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
 | 
			
		||||
	m_Target = LWOOBJID_EMPTY;
 | 
			
		||||
	m_State = AiState::spawn;
 | 
			
		||||
	SetAiState(AiState::spawn);
 | 
			
		||||
	m_Timer = 1.0f;
 | 
			
		||||
	m_StartPosition = parent->GetPosition();
 | 
			
		||||
	m_MovementAI = nullptr;
 | 
			
		||||
@@ -179,7 +179,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
 | 
			
		||||
 | 
			
		||||
	if (m_Disabled || m_Parent->GetIsDead())
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	bool stunnedThisFrame = m_Stunned;
 | 
			
		||||
	CalculateCombat(deltaTime); // Putting this here for now
 | 
			
		||||
 | 
			
		||||
	if (m_StartPosition == NiPoint3::ZERO) {
 | 
			
		||||
@@ -192,7 +192,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (m_Stunned) {
 | 
			
		||||
	if (stunnedThisFrame) {
 | 
			
		||||
		m_MovementAI->Stop();
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
@@ -206,7 +206,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
 | 
			
		||||
	switch (m_State) {
 | 
			
		||||
	case AiState::spawn:
 | 
			
		||||
		Stun(2.0f);
 | 
			
		||||
		m_State = AiState::idle;
 | 
			
		||||
		SetAiState(AiState::idle);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case AiState::idle:
 | 
			
		||||
@@ -248,13 +248,13 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
 | 
			
		||||
 | 
			
		||||
	if (m_Disabled) return;
 | 
			
		||||
 | 
			
		||||
	if (m_StunTime > 0.0f) {
 | 
			
		||||
	if (m_Stunned) {
 | 
			
		||||
		m_StunTime -= deltaTime;
 | 
			
		||||
 | 
			
		||||
		if (m_StunTime > 0.0f) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m_StunTime = 0.0f;
 | 
			
		||||
		m_Stunned = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -320,9 +320,9 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
 | 
			
		||||
			m_Timer = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m_State = AiState::aggro;
 | 
			
		||||
		SetAiState(AiState::aggro);
 | 
			
		||||
	} else {
 | 
			
		||||
		m_State = AiState::idle;
 | 
			
		||||
		SetAiState(AiState::idle);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (auto i = 0; i < m_SkillEntries.size(); ++i) {
 | 
			
		||||
@@ -348,7 +348,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (m_Target == LWOOBJID_EMPTY) {
 | 
			
		||||
		m_State = AiState::idle;
 | 
			
		||||
		SetAiState(AiState::idle);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
@@ -375,7 +375,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
 | 
			
		||||
				m_MovementAI->Stop();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			m_State = AiState::aggro;
 | 
			
		||||
			SetAiState(AiState::aggro);
 | 
			
		||||
 | 
			
		||||
			m_Timer = 0;
 | 
			
		||||
 | 
			
		||||
@@ -532,11 +532,20 @@ bool BaseCombatAIComponent::IsMech() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
 | 
			
		||||
	outBitStream->Write1();
 | 
			
		||||
	outBitStream->Write(uint32_t(m_State));
 | 
			
		||||
	outBitStream->Write(m_Target);
 | 
			
		||||
	outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
 | 
			
		||||
	if (m_DirtyStateOrTarget || bIsInitialUpdate) {
 | 
			
		||||
		outBitStream->Write(uint32_t(m_State));
 | 
			
		||||
		outBitStream->Write(m_Target);
 | 
			
		||||
		m_DirtyStateOrTarget = false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BaseCombatAIComponent::SetAiState(AiState newState) {
 | 
			
		||||
	if (newState == this->m_State) return;
 | 
			
		||||
	this->m_State = newState;
 | 
			
		||||
	m_DirtyStateOrTarget = true;
 | 
			
		||||
	EntityManager::Instance()->SerializeEntity(m_Parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
 | 
			
		||||
	auto* entity = EntityManager::Instance()->GetEntity(target);
 | 
			
		||||
@@ -585,7 +594,10 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BaseCombatAIComponent::SetTarget(const LWOOBJID target) {
 | 
			
		||||
	if (this->m_Target == target) return;
 | 
			
		||||
	m_Target = target;
 | 
			
		||||
	m_DirtyStateOrTarget = true;
 | 
			
		||||
	EntityManager::Instance()->SerializeEntity(m_Parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Entity* BaseCombatAIComponent::GetTargetEntity() const {
 | 
			
		||||
@@ -700,7 +712,7 @@ void BaseCombatAIComponent::OnAggro() {
 | 
			
		||||
 | 
			
		||||
		m_MovementAI->SetDestination(targetPos);
 | 
			
		||||
 | 
			
		||||
		m_State = AiState::tether;
 | 
			
		||||
		SetAiState(AiState::tether);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_Timer += 0.5f;
 | 
			
		||||
@@ -726,7 +738,7 @@ void BaseCombatAIComponent::OnTether() {
 | 
			
		||||
 | 
			
		||||
		m_MovementAI->SetDestination(m_StartPosition);
 | 
			
		||||
 | 
			
		||||
		m_State = AiState::aggro;
 | 
			
		||||
		SetAiState(AiState::aggro);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -243,6 +243,12 @@ private:
 | 
			
		||||
	 */
 | 
			
		||||
	std::vector<LWOOBJID> GetTargetWithinAggroRange() const;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @brief Sets the AiState and prepares the entity for serialization next frame.
 | 
			
		||||
	 * 
 | 
			
		||||
	 */
 | 
			
		||||
	void SetAiState(AiState newState);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The current state of the AI
 | 
			
		||||
	 */
 | 
			
		||||
@@ -374,6 +380,12 @@ private:
 | 
			
		||||
	 */
 | 
			
		||||
	bool m_DirtyThreat = false;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Whether or not the Component has dirty information and should update next frame
 | 
			
		||||
	 * 
 | 
			
		||||
	 */
 | 
			
		||||
	bool m_DirtyStateOrTarget = false;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Whether the current entity is a mech enemy, needed as mechs tether radius works differently
 | 
			
		||||
	 * @return whether this entity is a mech
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
#include "PossessorComponent.h"
 | 
			
		||||
#include "InventoryComponent.h"
 | 
			
		||||
#include "dZoneManager.h"
 | 
			
		||||
#include "WorldConfig.h"
 | 
			
		||||
 | 
			
		||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
 | 
			
		||||
	m_iArmor = 0;
 | 
			
		||||
@@ -700,7 +701,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
 | 
			
		||||
		auto* missions = owner->GetComponent<MissionComponent>();
 | 
			
		||||
 | 
			
		||||
		if (missions != nullptr) {
 | 
			
		||||
			if (team != nullptr && isEnemy) {
 | 
			
		||||
			if (team != nullptr) {
 | 
			
		||||
				for (const auto memberId : team->members) {
 | 
			
		||||
					auto* member = EntityManager::Instance()->GetEntity(memberId);
 | 
			
		||||
 | 
			
		||||
@@ -763,22 +764,17 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
 | 
			
		||||
		if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath()) {
 | 
			
		||||
			auto* character = m_Parent->GetCharacter();
 | 
			
		||||
			uint64_t coinsTotal = character->GetCoins();
 | 
			
		||||
			const uint64_t minCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMin;
 | 
			
		||||
			if (coinsTotal >= minCoinsToLose) {
 | 
			
		||||
				const uint64_t maxCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMax;
 | 
			
		||||
				const float coinPercentageToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathPercent;
 | 
			
		||||
 | 
			
		||||
			if (coinsTotal > 0) {
 | 
			
		||||
				uint64_t coinsToLoose = 1;
 | 
			
		||||
				uint64_t coinsToLose = std::max(static_cast<uint64_t>(coinsTotal * coinPercentageToLose), minCoinsToLose);
 | 
			
		||||
				coinsToLose = std::min(maxCoinsToLose, coinsToLose);
 | 
			
		||||
 | 
			
		||||
				if (coinsTotal >= 200) {
 | 
			
		||||
					float hundreth = (coinsTotal / 100.0f);
 | 
			
		||||
					coinsToLoose = static_cast<int>(hundreth);
 | 
			
		||||
				}
 | 
			
		||||
				coinsTotal -= coinsToLose;
 | 
			
		||||
 | 
			
		||||
				if (coinsToLoose > 10000) {
 | 
			
		||||
					coinsToLoose = 10000;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				coinsTotal -= coinsToLoose;
 | 
			
		||||
 | 
			
		||||
				LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose);
 | 
			
		||||
				LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
 | 
			
		||||
				character->SetCoins(coinsTotal, eLootSourceType::LOOT_SOURCE_PICKUP);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ std::map<LOT, uint32_t> PetComponent::petFlags = {
 | 
			
		||||
		{ 13067, 838 }, // Skeleton dragon
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(parent) {
 | 
			
		||||
PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(parent) {
 | 
			
		||||
	m_ComponentId = componentId;
 | 
			
		||||
 | 
			
		||||
	m_Interaction = LWOOBJID_EMPTY;
 | 
			
		||||
@@ -118,21 +118,23 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd
 | 
			
		||||
		outBitStream->Write(m_Owner);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	outBitStream->Write(tamed);
 | 
			
		||||
	if (tamed) {
 | 
			
		||||
		outBitStream->Write(m_ModerationStatus);
 | 
			
		||||
	if (bIsInitialUpdate) {
 | 
			
		||||
		outBitStream->Write(tamed);
 | 
			
		||||
		if (tamed) {
 | 
			
		||||
			outBitStream->Write(m_ModerationStatus);
 | 
			
		||||
 | 
			
		||||
		const auto nameData = GeneralUtils::UTF8ToUTF16(m_Name);
 | 
			
		||||
		const auto ownerNameData = GeneralUtils::UTF8ToUTF16(m_OwnerName);
 | 
			
		||||
			const auto nameData = GeneralUtils::UTF8ToUTF16(m_Name);
 | 
			
		||||
			const auto ownerNameData = GeneralUtils::UTF8ToUTF16(m_OwnerName);
 | 
			
		||||
 | 
			
		||||
		outBitStream->Write(static_cast<uint8_t>(nameData.size()));
 | 
			
		||||
		for (const auto c : nameData) {
 | 
			
		||||
			outBitStream->Write(c);
 | 
			
		||||
		}
 | 
			
		||||
			outBitStream->Write(static_cast<uint8_t>(nameData.size()));
 | 
			
		||||
			for (const auto c : nameData) {
 | 
			
		||||
				outBitStream->Write(c);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		outBitStream->Write(static_cast<uint8_t>(ownerNameData.size()));
 | 
			
		||||
		for (const auto c : ownerNameData) {
 | 
			
		||||
			outBitStream->Write(c);
 | 
			
		||||
			outBitStream->Write(static_cast<uint8_t>(ownerNameData.size()));
 | 
			
		||||
			for (const auto c : ownerNameData) {
 | 
			
		||||
				outBitStream->Write(c);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -916,16 +918,16 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If we are out of imagination despawn the pet.
 | 
			
		||||
		if (playerDestroyableComponent->GetImagination() == 0) {
 | 
			
		||||
			this->Deactivate();
 | 
			
		||||
			auto playerEntity = playerDestroyableComponent->GetParent();
 | 
			
		||||
			if (!playerEntity) return;
 | 
			
		||||
	// If we are out of imagination despawn the pet.
 | 
			
		||||
	if (playerDestroyableComponent->GetImagination() == 0) {
 | 
			
		||||
		this->Deactivate();
 | 
			
		||||
		auto playerEntity = playerDestroyableComponent->GetParent();
 | 
			
		||||
		if (!playerEntity) return;
 | 
			
		||||
 | 
			
		||||
			GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), UseItemResponse::NoImaginationForPet);
 | 
			
		||||
		}
 | 
			
		||||
		GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), UseItemResponse::NoImaginationForPet);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		this->AddDrainImaginationTimer(item);
 | 
			
		||||
	this->AddDrainImaginationTimer(item);
 | 
			
		||||
		});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,8 @@ PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent) : C
 | 
			
		||||
PlayerForcedMovementComponent::~PlayerForcedMovementComponent() {}
 | 
			
		||||
 | 
			
		||||
void PlayerForcedMovementComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
 | 
			
		||||
	outBitStream->Write(m_DirtyInfo);
 | 
			
		||||
	if (m_DirtyInfo) {
 | 
			
		||||
	outBitStream->Write(m_DirtyInfo || bIsInitialUpdate);
 | 
			
		||||
	if (m_DirtyInfo || bIsInitialUpdate) {
 | 
			
		||||
		outBitStream->Write(m_PlayerOnRail);
 | 
			
		||||
		outBitStream->Write(m_ShowBillboard);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
#include "dZoneManager.h"
 | 
			
		||||
#include "InventoryComponent.h"
 | 
			
		||||
#include "Database.h"
 | 
			
		||||
#include "WorldConfig.h"
 | 
			
		||||
 | 
			
		||||
Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
 | 
			
		||||
	m_MissionComponent = missionComponent;
 | 
			
		||||
@@ -435,9 +436,9 @@ void Mission::YieldRewards() {
 | 
			
		||||
	int32_t coinsToSend = 0;
 | 
			
		||||
	if (info->LegoScore > 0) {
 | 
			
		||||
		eLootSourceType lootSource = info->isMission ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT;
 | 
			
		||||
		if (levelComponent->GetLevel() >= dZoneManager::Instance()->GetMaxLevel()) {
 | 
			
		||||
		if (levelComponent->GetLevel() >= dZoneManager::Instance()->GetWorldConfig()->levelCap) {
 | 
			
		||||
			// Since the character is at the level cap we reward them with coins instead of UScore.
 | 
			
		||||
			coinsToSend += info->LegoScore * dZoneManager::Instance()->GetLevelCapCurrencyConversion();
 | 
			
		||||
			coinsToSend += info->LegoScore * dZoneManager::Instance()->GetWorldConfig()->levelCapCurrencyConversion;
 | 
			
		||||
		} else {
 | 
			
		||||
			characterComponent->SetUScore(characterComponent->GetUScore() + info->LegoScore);
 | 
			
		||||
			GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info->LegoScore, lootSource);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,8 @@
 | 
			
		||||
#include "MissionComponent.h"
 | 
			
		||||
#include "ChatPackets.h"
 | 
			
		||||
#include "Character.h"
 | 
			
		||||
#include "dZoneManager.h"
 | 
			
		||||
#include "WorldConfig.h"
 | 
			
		||||
 | 
			
		||||
void Mail::SendMail(const Entity* recipient, const std::string& subject, const std::string& body, const LOT attachment,
 | 
			
		||||
	const uint16_t attachmentCount) {
 | 
			
		||||
@@ -191,7 +193,7 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd
 | 
			
		||||
	uint32_t itemID = static_cast<uint32_t>(attachmentID);
 | 
			
		||||
	LOT itemLOT = 0;
 | 
			
		||||
	//Inventory::InventoryType itemType;
 | 
			
		||||
	int mailCost = 25;
 | 
			
		||||
	int mailCost = dZoneManager::Instance()->GetWorldConfig()->mailBaseFee;
 | 
			
		||||
	int stackSize = 0;
 | 
			
		||||
	auto inv = static_cast<InventoryComponent*>(entity->GetComponent(COMPONENT_TYPE_INVENTORY));
 | 
			
		||||
	Item* item = nullptr;
 | 
			
		||||
@@ -199,7 +201,7 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd
 | 
			
		||||
	if (itemID > 0 && attachmentCount > 0 && inv) {
 | 
			
		||||
		item = inv->FindItemById(attachmentID);
 | 
			
		||||
		if (item) {
 | 
			
		||||
			mailCost += (item->GetInfo().baseValue * 0.1f);
 | 
			
		||||
			mailCost += (item->GetInfo().baseValue * dZoneManager::Instance()->GetWorldConfig()->mailPercentAttachmentFee);
 | 
			
		||||
			stackSize = item->GetCount();
 | 
			
		||||
			itemLOT = item->GetLot();
 | 
			
		||||
		} else {
 | 
			
		||||
 
 | 
			
		||||
@@ -156,22 +156,25 @@ int main(int argc, char** argv) {
 | 
			
		||||
			Game::logger->Log("MasterServer", "CDServer.sqlite is not located at resServer, but is located at res path.  Copying file...");
 | 
			
		||||
			std::filesystem::copy_file(Game::assetManager->GetResPath() / "CDServer.sqlite", BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite");
 | 
			
		||||
		} else {
 | 
			
		||||
			Game::logger->Log("WorldServer",
 | 
			
		||||
			Game::logger->Log("MasterServer",
 | 
			
		||||
				"%s could not be found in resServer or res. Looking for %s to convert to sqlite.",
 | 
			
		||||
				(BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").c_str(),
 | 
			
		||||
				(Game::assetManager->GetResPath() / "cdclient.fdb").c_str());
 | 
			
		||||
			if (!fdbExists) {
 | 
			
		||||
				Game::logger->Log("WorldServer",
 | 
			
		||||
					"%s could not be opened. Please move cdclient.fdb to %s",
 | 
			
		||||
					(Game::assetManager->GetResPath() / "cdclient.fdb").c_str(),
 | 
			
		||||
					(Game::assetManager->GetResPath().c_str()));
 | 
			
		||||
				return FinalizeShutdown();
 | 
			
		||||
 | 
			
		||||
			AssetMemoryBuffer cdClientBuffer = Game::assetManager->GetFileAsBuffer("cdclient.fdb");
 | 
			
		||||
			if (!cdClientBuffer.m_Success) {
 | 
			
		||||
				Game::logger->Log("MasterServer", "Failed to load %s", (Game::assetManager->GetResPath() / "cdclient.fdb").c_str());
 | 
			
		||||
				throw std::runtime_error("Aborting initialization due to missing cdclient.fdb.");
 | 
			
		||||
			}
 | 
			
		||||
			Game::logger->Log("WorldServer", "Found cdclient.fdb.  Converting to SQLite");
 | 
			
		||||
			if (FdbToSqlite::Convert(Game::assetManager->GetResPath().string(), (BinaryPathFinder::GetBinaryDir() / "resServer").string()).ConvertDatabase() == false) {
 | 
			
		||||
 | 
			
		||||
			Game::logger->Log("MasterServer", "Found %s.  Converting to SQLite", (Game::assetManager->GetResPath() / "cdclient.fdb").c_str());
 | 
			
		||||
			Game::logger->Flush();
 | 
			
		||||
 | 
			
		||||
			if (FdbToSqlite::Convert((BinaryPathFinder::GetBinaryDir() / "resServer").string()).ConvertDatabase(cdClientBuffer) == false) {
 | 
			
		||||
				Game::logger->Log("MasterServer", "Failed to convert fdb to sqlite.");
 | 
			
		||||
				return FinalizeShutdown();
 | 
			
		||||
				return EXIT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
			cdClientBuffer.close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								dZoneManager/WorldConfig.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								dZoneManager/WorldConfig.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
#ifndef __WORLDCONFIG__H__
 | 
			
		||||
#define __WORLDCONFIG__H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
struct WorldConfig {
 | 
			
		||||
	int32_t worldConfigID{};						//! Primary key for WorlcConfig table
 | 
			
		||||
	float peGravityValue{};							//! Unknown
 | 
			
		||||
	float peBroadphaseWorldSize{};					//! Unknown
 | 
			
		||||
	float peGameObjScaleFactor{};					//! Unknown
 | 
			
		||||
	float characterRotationSpeed{};					//! The players' rotation speed
 | 
			
		||||
	float characterWalkForwardSpeed{};				//! The players' walk forward speed
 | 
			
		||||
	float characterWalkBackwardSpeed{};				//! The players' walk backwards speed
 | 
			
		||||
	float characterWalkStrafeSpeed{};				//! The players' strafe speed
 | 
			
		||||
	float characterWalkStrafeForwardSpeed{};		//! The players' walk strafe forward speed
 | 
			
		||||
	float characterWalkStrafeBackwardSpeed{};		//! The players' walk strage backwards speed
 | 
			
		||||
	float characterRunBackwardSpeed{};				//! The players' run backwards speed
 | 
			
		||||
	float characterRunStrafeSpeed{};				//! The players' run strafe speed
 | 
			
		||||
	float characterRunStrafeForwardSpeed{};			//! The players' run strafe forward speed
 | 
			
		||||
	float characterRunStrafeBackwardSpeed{};		//! The players' run strage backwards speed
 | 
			
		||||
	float globalCooldown{};							//! The global ability cooldown
 | 
			
		||||
	float characterGroundedTime{};					//! Unknown
 | 
			
		||||
	float characterGroundedSpeed{};					//! Unknown
 | 
			
		||||
	float globalImmunityTime{};						//! Unknown
 | 
			
		||||
	float characterMaxSlope{};						//! Unknown
 | 
			
		||||
	float defaultRespawnTime{};						//! Unknown
 | 
			
		||||
	float missionTooltipTimeout{};
 | 
			
		||||
	float vendorBuyMultiplier{};					//! The buy scalar for buying from vendors
 | 
			
		||||
	float petFollowRadius{};						//! The players' pet follow radius
 | 
			
		||||
	float characterEyeHeight{};						//! The players' eye height
 | 
			
		||||
	float flightVerticalVelocity{};					//! Unknown
 | 
			
		||||
	float flightAirspeed{};							//! Unknown
 | 
			
		||||
	float flightFuelRatio{};						//! Unknown
 | 
			
		||||
	float flightMaxAirspeed{};						//! Unknown
 | 
			
		||||
	float fReputationPerVote{};						//! Unknown
 | 
			
		||||
	int32_t propertyCloneLimit{};					//! Unknown
 | 
			
		||||
	int32_t defaultHomespaceTemplate{};				//! Unknown
 | 
			
		||||
	float coinsLostOnDeathPercent{};				//! The percentage of coins to lose on a player death
 | 
			
		||||
	int32_t coinsLostOnDeathMin{};					//! The minimum number of coins to lose on a player death
 | 
			
		||||
	int32_t coinsLostOnDeathMax{};					//! The maximum number of coins to lose on a player death
 | 
			
		||||
	int32_t characterVotesPerDay{};					//! Unknown
 | 
			
		||||
	int32_t propertyModerationRequestApprovalCost{};//! Unknown
 | 
			
		||||
	int32_t propertyModerationRequestReviewCost{};	//! Unknown
 | 
			
		||||
	int32_t propertyModRequestsAllowedSpike{};		//! Unknown
 | 
			
		||||
	int32_t propertyModRequestsAllowedInterval{};	//! Unknown
 | 
			
		||||
	int32_t propertyModRequestsAllowedTotal{};		//! Unknown
 | 
			
		||||
	int32_t propertyModRequestsSpikeDuration{};		//! Unknown
 | 
			
		||||
	int32_t propertyModRequestsIntervalDuration{};	//! Unknown
 | 
			
		||||
	bool modelModerateOnCreate{};					//! Unknown
 | 
			
		||||
	float defaultPropertyMaxHeight{};				//! Unknown
 | 
			
		||||
	float reputationPerVoteCast{};					//! Unknown
 | 
			
		||||
	float reputationPerVoteReceived{};				//! Unknown
 | 
			
		||||
	int32_t showcaseTopModelConsiderationBattles{};	//! Unknown
 | 
			
		||||
	float reputationPerBattlePromotion{};			//! Unknown
 | 
			
		||||
	float coinsLostOnDeathMinTimeout{};				//! Unknown
 | 
			
		||||
	float coinsLostOnDeathMaxTimeout{};				//! Unknown
 | 
			
		||||
	int32_t mailBaseFee{};							//! The base fee to take when a player sends mail
 | 
			
		||||
	float mailPercentAttachmentFee{};				//! The scalar multiplied by an items base cost to determine how much that item costs to be mailed
 | 
			
		||||
	int32_t propertyReputationDelay{};				//! Unknown
 | 
			
		||||
	int32_t levelCap{};								//! The maximum player level
 | 
			
		||||
	std::string levelUpBehaviorEffect{};			//! Unknown
 | 
			
		||||
	int32_t characterVersion{};						//! Unknown
 | 
			
		||||
	int32_t levelCapCurrencyConversion{};			//! The ratio of UScore (LEGO Score) to coins
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif //! __WORLDCONFIG__H__
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
#include "DestroyableComponent.h"
 | 
			
		||||
#include "GameMessages.h"
 | 
			
		||||
#include "VanityUtilities.h"
 | 
			
		||||
#include "WorldConfig.h"
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
#include "../dWorldServer/ObjectIDManager.h"
 | 
			
		||||
@@ -53,6 +54,8 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) {
 | 
			
		||||
 | 
			
		||||
	endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
 | 
			
		||||
 | 
			
		||||
	LoadWorldConfig();
 | 
			
		||||
 | 
			
		||||
	Game::logger->Log("dZoneManager", "Zone prepared in: %llu ms", (endTime - startTime));
 | 
			
		||||
 | 
			
		||||
	VanityUtilities::SpawnVanity();
 | 
			
		||||
@@ -69,6 +72,7 @@ dZoneManager::~dZoneManager() {
 | 
			
		||||
 | 
			
		||||
		m_Spawners.erase(p.first);
 | 
			
		||||
	}
 | 
			
		||||
	if (m_WorldConfig) delete m_WorldConfig;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Zone* dZoneManager::GetZone() {
 | 
			
		||||
@@ -117,24 +121,6 @@ LWOZONEID dZoneManager::GetZoneID() const {
 | 
			
		||||
	return m_ZoneID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t dZoneManager::GetMaxLevel() {
 | 
			
		||||
	if (m_MaxLevel == 0) {
 | 
			
		||||
		auto tableData = CDClientDatabase::ExecuteQuery("SELECT LevelCap FROM WorldConfig WHERE WorldConfigID = 1 LIMIT 1;");
 | 
			
		||||
		m_MaxLevel = tableData.getIntField(0, -1);
 | 
			
		||||
		tableData.finalize();
 | 
			
		||||
	}
 | 
			
		||||
	return m_MaxLevel;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t dZoneManager::GetLevelCapCurrencyConversion() {
 | 
			
		||||
	if (m_CurrencyConversionRate == 0) {
 | 
			
		||||
		auto tableData = CDClientDatabase::ExecuteQuery("SELECT LevelCapCurrencyConversion FROM WorldConfig WHERE WorldConfigID = 1 LIMIT 1;");
 | 
			
		||||
		m_CurrencyConversionRate = tableData.getIntField(0, -1);
 | 
			
		||||
		tableData.finalize();
 | 
			
		||||
	}
 | 
			
		||||
	return m_CurrencyConversionRate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dZoneManager::Update(float deltaTime) {
 | 
			
		||||
	for (auto spawner : m_Spawners) {
 | 
			
		||||
		spawner.second->Update(deltaTime);
 | 
			
		||||
@@ -249,3 +235,77 @@ uint32_t dZoneManager::GetUniqueMissionIdStartingValue() {
 | 
			
		||||
	}
 | 
			
		||||
	return m_UniqueMissionIdStart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dZoneManager::LoadWorldConfig() {
 | 
			
		||||
	Game::logger->Log("dZoneManager", "Loading WorldConfig into memory");
 | 
			
		||||
 | 
			
		||||
	auto worldConfig = CDClientDatabase::ExecuteQuery("SELECT * FROM WorldConfig;");
 | 
			
		||||
 | 
			
		||||
	if (!m_WorldConfig) m_WorldConfig = new WorldConfig();
 | 
			
		||||
 | 
			
		||||
	if (worldConfig.eof()) {
 | 
			
		||||
		Game::logger->Log("dZoneManager", "WorldConfig table is empty.  Is this intended?");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now read in the giant table
 | 
			
		||||
	m_WorldConfig->worldConfigID = worldConfig.getIntField("WorldConfigID");
 | 
			
		||||
	m_WorldConfig->peGravityValue = worldConfig.getFloatField("pegravityvalue");
 | 
			
		||||
	m_WorldConfig->peBroadphaseWorldSize = worldConfig.getFloatField("pebroadphaseworldsize");
 | 
			
		||||
	m_WorldConfig->peGameObjScaleFactor = worldConfig.getFloatField("pegameobjscalefactor");
 | 
			
		||||
	m_WorldConfig->characterRotationSpeed = worldConfig.getFloatField("character_rotation_speed");
 | 
			
		||||
	m_WorldConfig->characterWalkForwardSpeed = worldConfig.getFloatField("character_walk_forward_speed");
 | 
			
		||||
	m_WorldConfig->characterWalkBackwardSpeed = worldConfig.getFloatField("character_walk_backward_speed");
 | 
			
		||||
	m_WorldConfig->characterWalkStrafeSpeed = worldConfig.getFloatField("character_walk_strafe_speed");
 | 
			
		||||
	m_WorldConfig->characterWalkStrafeForwardSpeed = worldConfig.getFloatField("character_walk_strafe_forward_speed");
 | 
			
		||||
	m_WorldConfig->characterWalkStrafeBackwardSpeed = worldConfig.getFloatField("character_walk_strafe_backward_speed");
 | 
			
		||||
	m_WorldConfig->characterRunBackwardSpeed = worldConfig.getFloatField("character_run_backward_speed");
 | 
			
		||||
	m_WorldConfig->characterRunStrafeSpeed = worldConfig.getFloatField("character_run_strafe_speed");
 | 
			
		||||
	m_WorldConfig->characterRunStrafeForwardSpeed = worldConfig.getFloatField("character_run_strafe_forward_speed");
 | 
			
		||||
	m_WorldConfig->characterRunStrafeBackwardSpeed = worldConfig.getFloatField("character_run_strafe_backward_speed");
 | 
			
		||||
	m_WorldConfig->globalCooldown = worldConfig.getFloatField("global_cooldown");
 | 
			
		||||
	m_WorldConfig->characterGroundedTime = worldConfig.getFloatField("characterGroundedTime");
 | 
			
		||||
	m_WorldConfig->characterGroundedSpeed = worldConfig.getFloatField("characterGroundedSpeed");
 | 
			
		||||
	m_WorldConfig->globalImmunityTime = worldConfig.getFloatField("globalImmunityTime");
 | 
			
		||||
	m_WorldConfig->characterMaxSlope = worldConfig.getFloatField("character_max_slope");
 | 
			
		||||
	m_WorldConfig->defaultRespawnTime = worldConfig.getFloatField("defaultrespawntime");
 | 
			
		||||
	m_WorldConfig->missionTooltipTimeout = worldConfig.getFloatField("mission_tooltip_timeout");
 | 
			
		||||
	m_WorldConfig->vendorBuyMultiplier = worldConfig.getFloatField("vendor_buy_multiplier");
 | 
			
		||||
	m_WorldConfig->petFollowRadius = worldConfig.getFloatField("pet_follow_radius");
 | 
			
		||||
	m_WorldConfig->characterEyeHeight = worldConfig.getFloatField("character_eye_height");
 | 
			
		||||
	m_WorldConfig->flightVerticalVelocity = worldConfig.getFloatField("flight_vertical_velocity");
 | 
			
		||||
	m_WorldConfig->flightAirspeed = worldConfig.getFloatField("flight_airspeed");
 | 
			
		||||
	m_WorldConfig->flightFuelRatio = worldConfig.getFloatField("flight_fuel_ratio");
 | 
			
		||||
	m_WorldConfig->flightMaxAirspeed = worldConfig.getFloatField("flight_max_airspeed");
 | 
			
		||||
	m_WorldConfig->fReputationPerVote = worldConfig.getFloatField("fReputationPerVote");
 | 
			
		||||
	m_WorldConfig->propertyCloneLimit = worldConfig.getIntField("nPropertyCloneLimit");
 | 
			
		||||
	m_WorldConfig->defaultHomespaceTemplate = worldConfig.getIntField("defaultHomespaceTemplate");
 | 
			
		||||
	m_WorldConfig->coinsLostOnDeathPercent = worldConfig.getFloatField("coins_lost_on_death_percent");
 | 
			
		||||
	m_WorldConfig->coinsLostOnDeathMin = worldConfig.getIntField("coins_lost_on_death_min");
 | 
			
		||||
	m_WorldConfig->coinsLostOnDeathMax = worldConfig.getIntField("coins_lost_on_death_max");
 | 
			
		||||
	m_WorldConfig->characterVotesPerDay = worldConfig.getIntField("character_votes_per_day");
 | 
			
		||||
	m_WorldConfig->propertyModerationRequestApprovalCost = worldConfig.getIntField("property_moderation_request_approval_cost");
 | 
			
		||||
	m_WorldConfig->propertyModerationRequestReviewCost = worldConfig.getIntField("property_moderation_request_review_cost");
 | 
			
		||||
	m_WorldConfig->propertyModRequestsAllowedSpike = worldConfig.getIntField("propertyModRequestsAllowedSpike");
 | 
			
		||||
	m_WorldConfig->propertyModRequestsAllowedInterval = worldConfig.getIntField("propertyModRequestsAllowedInterval");
 | 
			
		||||
	m_WorldConfig->propertyModRequestsAllowedTotal = worldConfig.getIntField("propertyModRequestsAllowedTotal");
 | 
			
		||||
	m_WorldConfig->propertyModRequestsSpikeDuration = worldConfig.getIntField("propertyModRequestsSpikeDuration");
 | 
			
		||||
	m_WorldConfig->propertyModRequestsIntervalDuration = worldConfig.getIntField("propertyModRequestsIntervalDuration");
 | 
			
		||||
	m_WorldConfig->modelModerateOnCreate = worldConfig.getIntField("modelModerateOnCreate") != 0;
 | 
			
		||||
	m_WorldConfig->defaultPropertyMaxHeight = worldConfig.getFloatField("defaultPropertyMaxHeight");
 | 
			
		||||
	m_WorldConfig->reputationPerVoteCast = worldConfig.getFloatField("reputationPerVoteCast");
 | 
			
		||||
	m_WorldConfig->reputationPerVoteReceived = worldConfig.getFloatField("reputationPerVoteReceived");
 | 
			
		||||
	m_WorldConfig->showcaseTopModelConsiderationBattles = worldConfig.getIntField("showcaseTopModelConsiderationBattles");
 | 
			
		||||
	m_WorldConfig->reputationPerBattlePromotion = worldConfig.getFloatField("reputationPerBattlePromotion");
 | 
			
		||||
	m_WorldConfig->coinsLostOnDeathMinTimeout = worldConfig.getFloatField("coins_lost_on_death_min_timeout");
 | 
			
		||||
	m_WorldConfig->coinsLostOnDeathMaxTimeout = worldConfig.getFloatField("coins_lost_on_death_max_timeout");
 | 
			
		||||
	m_WorldConfig->mailBaseFee = worldConfig.getIntField("mail_base_fee");
 | 
			
		||||
	m_WorldConfig->mailPercentAttachmentFee = worldConfig.getFloatField("mail_percent_attachment_fee");
 | 
			
		||||
	m_WorldConfig->propertyReputationDelay = worldConfig.getIntField("propertyReputationDelay");
 | 
			
		||||
	m_WorldConfig->levelCap = worldConfig.getIntField("LevelCap");
 | 
			
		||||
	m_WorldConfig->levelUpBehaviorEffect = worldConfig.getStringField("LevelUpBehaviorEffect");
 | 
			
		||||
	m_WorldConfig->characterVersion = worldConfig.getIntField("CharacterVersion");
 | 
			
		||||
	m_WorldConfig->levelCapCurrencyConversion = worldConfig.getIntField("LevelCapCurrencyConversion");
 | 
			
		||||
	worldConfig.finalize();
 | 
			
		||||
	Game::logger->Log("dZoneManager", "Loaded WorldConfig into memory");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@
 | 
			
		||||
#include "Spawner.h"
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
class WorldConfig;
 | 
			
		||||
 | 
			
		||||
class dZoneManager {
 | 
			
		||||
public:
 | 
			
		||||
	enum class dZoneNotifier {
 | 
			
		||||
@@ -16,6 +18,12 @@ public:
 | 
			
		||||
		InvalidNotifier
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	/**
 | 
			
		||||
	 * Reads the WorldConfig from the CDClientDatabase into memory
 | 
			
		||||
	 */
 | 
			
		||||
	void LoadWorldConfig();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static dZoneManager* Instance() {
 | 
			
		||||
		if (!m_Address) {
 | 
			
		||||
@@ -33,8 +41,6 @@ public:
 | 
			
		||||
	void NotifyZone(const dZoneNotifier& notifier, const LWOOBJID& objectID); //Notifies the zone of a certain event or command.
 | 
			
		||||
	void AddSpawner(LWOOBJID id, Spawner* spawner);
 | 
			
		||||
	LWOZONEID GetZoneID() const;
 | 
			
		||||
	uint32_t GetMaxLevel();
 | 
			
		||||
	int32_t GetLevelCapCurrencyConversion();
 | 
			
		||||
	LWOOBJID MakeSpawner(SpawnerInfo info);
 | 
			
		||||
	Spawner* GetSpawner(LWOOBJID id);
 | 
			
		||||
	void RemoveSpawner(LWOOBJID id);
 | 
			
		||||
@@ -45,27 +51,24 @@ public:
 | 
			
		||||
	bool GetPlayerLoseCoinOnDeath() { return m_PlayerLoseCoinsOnDeath; }
 | 
			
		||||
	uint32_t GetUniqueMissionIdStartingValue();
 | 
			
		||||
 | 
			
		||||
	// The world config should not be modified by a caller.
 | 
			
		||||
	const WorldConfig* GetWorldConfig() {
 | 
			
		||||
		if (!m_WorldConfig) LoadWorldConfig();
 | 
			
		||||
		return m_WorldConfig;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	/**
 | 
			
		||||
	 * The maximum level of the world.
 | 
			
		||||
	 */
 | 
			
		||||
	uint32_t m_MaxLevel = 0;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The ratio of LEGO Score to currency when the character has hit the max level.
 | 
			
		||||
	 */
 | 
			
		||||
	int32_t m_CurrencyConversionRate = 0;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The starting unique mission ID.
 | 
			
		||||
	 */
 | 
			
		||||
	uint32_t m_UniqueMissionIdStart = 0;
 | 
			
		||||
 | 
			
		||||
	static dZoneManager* m_Address; //Singleton
 | 
			
		||||
	Zone* m_pZone;
 | 
			
		||||
	Zone* m_pZone = nullptr;
 | 
			
		||||
	LWOZONEID m_ZoneID;
 | 
			
		||||
	bool m_PlayerLoseCoinsOnDeath; //Do players drop coins in this zone when smashed
 | 
			
		||||
	std::map<LWOOBJID, Spawner*> m_Spawners;
 | 
			
		||||
	WorldConfig* m_WorldConfig = nullptr;
 | 
			
		||||
 | 
			
		||||
	Entity* m_ZoneControlObject;
 | 
			
		||||
	Entity* m_ZoneControlObject = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
 | 
			
		||||
    echo "Install build dependencies" && \
 | 
			
		||||
    apt update && \
 | 
			
		||||
    apt remove -y libmysqlcppconn7v5 libmysqlcppconn-dev && \
 | 
			
		||||
    apt install cmake zlib1g zlib1g-dev unzip -yqq --no-install-recommends && \
 | 
			
		||||
    apt install cmake zlib1g zlib1g-dev -yqq --no-install-recommends && \
 | 
			
		||||
    rm -rf /var/lib/apt/lists/*
 | 
			
		||||
 | 
			
		||||
COPY dAuthServer/ /build/dAuthServer
 | 
			
		||||
@@ -35,12 +35,11 @@ ARG BUILD_VERSION=171022
 | 
			
		||||
RUN echo "Build server" && \
 | 
			
		||||
    mkdir -p cmake_build && \
 | 
			
		||||
    cd cmake_build && \
 | 
			
		||||
    sed -i -e "s/171022/${BUILD_VERSION}/g" ../CMakeVariables.txt && \
 | 
			
		||||
    sed -i -e "s/NET_VERSION=.*/NET_VERSION=${BUILD_VERSION}/g" ../CMakeVariables.txt && \
 | 
			
		||||
    sed -i -e "s/__maria_db_connector_compile_jobs__=.*/__maria_db_connector_compile_jobs__=${BUILD_THREADS}/g" ../CMakeVariables.txt && \
 | 
			
		||||
    cmake .. -DCMAKE_BUILD_RPATH_USE_ORIGIN=TRUE && \
 | 
			
		||||
    make -j $BUILD_THREADS
 | 
			
		||||
 | 
			
		||||
RUN unzip /build/resources/navmeshes.zip -d /build/cmake_build/res/maps
 | 
			
		||||
 | 
			
		||||
FROM gcc:11 as runtime
 | 
			
		||||
 | 
			
		||||
RUN --mount=type=cache,id=runtime-apt-cache,target=/var/cache/apt \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,12 @@
 | 
			
		||||
FROM rust:alpine3.14 as LUnpack
 | 
			
		||||
 | 
			
		||||
WORKDIR /build_LUnpack
 | 
			
		||||
 | 
			
		||||
COPY ./thirdparty/LUnpack .
 | 
			
		||||
 | 
			
		||||
RUN apk add musl-dev --no-cache && cargo build --release
 | 
			
		||||
 | 
			
		||||
FROM python:3.10-alpine3.14 as prep
 | 
			
		||||
 | 
			
		||||
RUN apk add sqlite bash --no-cache
 | 
			
		||||
RUN apk add bash --no-cache
 | 
			
		||||
 | 
			
		||||
WORKDIR /setup
 | 
			
		||||
 | 
			
		||||
# copy needed files from repo
 | 
			
		||||
COPY resources/ resources/
 | 
			
		||||
COPY migrations/cdserver/ migrations/cdserver
 | 
			
		||||
COPY --from=LUnpack /build_LUnpack/target/release/lunpack /usr/local/bin/lunpack
 | 
			
		||||
ADD thirdparty/docker-utils/utils/*.py utils/
 | 
			
		||||
 | 
			
		||||
COPY docker/setup.sh /setup.sh
 | 
			
		||||
 | 
			
		||||
CMD [ "/setup.sh" ]
 | 
			
		||||
CMD [ "/setup.sh" ]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ function update_ini() {
 | 
			
		||||
    FILE="/docker/configs/$1"
 | 
			
		||||
    KEY=$2
 | 
			
		||||
    NEW_VALUE=$3
 | 
			
		||||
    sed -i "/^$KEY=/s/=.*/=$NEW_VALUE/" $FILE
 | 
			
		||||
    sed -i "s~$2=.*~$2=$3~" $FILE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function update_database_ini_values_for() {
 | 
			
		||||
@@ -32,62 +32,11 @@ function update_ini_values() {
 | 
			
		||||
    cp resources/worldconfig.ini /docker/configs/
 | 
			
		||||
    cp resources/sharedconfig.ini /docker/configs/
 | 
			
		||||
 | 
			
		||||
    update_ini worldconfig.ini chat_server_port $CHAT_SERVER_PORT
 | 
			
		||||
    update_ini worldconfig.ini max_clients $MAX_CLIENTS
 | 
			
		||||
 | 
			
		||||
    # always use the internal docker hostname
 | 
			
		||||
    update_ini masterconfig.ini master_ip "darkflame"
 | 
			
		||||
    update_ini sharedconfig.ini client_location "/client"
 | 
			
		||||
 | 
			
		||||
    update_database_ini_values_for sharedconfig.ini
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fdb_to_sqlite() {
 | 
			
		||||
    echo "Run fdb_to_sqlite"
 | 
			
		||||
    python3 utils/fdb_to_sqlite.py /client/client/res/cdclient.fdb --sqlite_path /client/client/res/CDServer.sqlite
 | 
			
		||||
 | 
			
		||||
    (
 | 
			
		||||
        cd migrations/cdserver
 | 
			
		||||
        readarray -d '' entries < <(printf '%s\0' *.sql | sort -zV)
 | 
			
		||||
        for entry in "${entries[@]}"; do
 | 
			
		||||
            echo "Execute $entry"
 | 
			
		||||
            sqlite3 /client/client/res/CDServer.sqlite < $entry
 | 
			
		||||
        done
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
update_ini_values
 | 
			
		||||
 | 
			
		||||
if [[ ! -d "/client" ]]; then
 | 
			
		||||
    echo "Client not found."
 | 
			
		||||
    echo "Did you forget to mount the client into the \"/client\" directory?"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ ! -f "/client/extracted" ]]; then
 | 
			
		||||
    echo "Start client resource extraction"
 | 
			
		||||
 | 
			
		||||
    touch globs.txt
 | 
			
		||||
 | 
			
		||||
    echo "client/res/macros/**" >> globs.txt
 | 
			
		||||
    echo "client/res/BrickModels/**" >> globs.txt
 | 
			
		||||
    echo "client/res/maps/**" >> globs.txt
 | 
			
		||||
    echo "*.fdb" >> globs.txt
 | 
			
		||||
 | 
			
		||||
    lunpack -g ./globs.txt /client/
 | 
			
		||||
 | 
			
		||||
    touch /client/extracted
 | 
			
		||||
else
 | 
			
		||||
    echo "Client already extracted. Skip this step..."
 | 
			
		||||
    echo "If you want to force a re-extract, just delete the file called \"extracted\" in the client directory"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ ! -f "/client/migrated" ]]; then
 | 
			
		||||
    echo "Start client db migration"
 | 
			
		||||
 | 
			
		||||
    fdb_to_sqlite
 | 
			
		||||
 | 
			
		||||
    touch /client/migrated
 | 
			
		||||
else
 | 
			
		||||
    echo "Client db already migrated. Skip this step..."
 | 
			
		||||
    echo "If you want to force a re-migrate, just delete the file called \"migrated\" in the client directory"
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								docker/start_server.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										29
									
								
								docker/start_server.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -1,25 +1,5 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
function symlink_client_files() {
 | 
			
		||||
    echo "Creating symlinks for client files"
 | 
			
		||||
    ln -s /client/client/res/macros/ /app/res/macros
 | 
			
		||||
    ln -s /client/client/res/BrickModels/ /app/res/BrickModels
 | 
			
		||||
    ln -s /client/client/res/chatplus_en_us.txt /app/res/chatplus_en_us.txt
 | 
			
		||||
    ln -s /client/client/res/names/ /app/res/names
 | 
			
		||||
    ln -s /client/client/res/CDServer.sqlite /app/res/CDServer.sqlite
 | 
			
		||||
    
 | 
			
		||||
    # need to create this file so the server knows the client is unpacked (see `dCommon/dClient/AssetManager.cpp`)
 | 
			
		||||
    touch /app/res/cdclient.fdb
 | 
			
		||||
    # need to iterate over entries in maps due to maps already being a directory with navmeshes/ in it
 | 
			
		||||
    (
 | 
			
		||||
        cd /client/client/res/maps
 | 
			
		||||
        readarray -d '' entries < <(printf '%s\0' * | sort -zV)
 | 
			
		||||
        for entry in "${entries[@]}"; do
 | 
			
		||||
            ln -s /client/client/res/maps/$entry /app/res/maps/
 | 
			
		||||
        done
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function symlink_config_files() {
 | 
			
		||||
    echo "Creating symlinks for config files"
 | 
			
		||||
    rm /app/*.ini
 | 
			
		||||
@@ -30,15 +10,8 @@ function symlink_config_files() {
 | 
			
		||||
    ln -s /shared_configs/configs/sharedconfig.ini /app/sharedconfig.ini
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# check to make sure the setup has completed
 | 
			
		||||
while [ ! -f "/client/extracted" ] || [ ! -f "/client/migrated" ]; do
 | 
			
		||||
    echo "Client setup not finished. Waiting for setup container to complete..."
 | 
			
		||||
    sleep 5
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if [[ ! -f "/app/initialized" ]]; then
 | 
			
		||||
    # setup symlinks for volume files
 | 
			
		||||
    symlink_client_files
 | 
			
		||||
    symlink_config_files
 | 
			
		||||
    # do not run symlinks more than once
 | 
			
		||||
    touch /app/initialized
 | 
			
		||||
@@ -49,4 +22,4 @@ fi
 | 
			
		||||
# start the server
 | 
			
		||||
echo "Starting MasterServer"
 | 
			
		||||
./MasterServer
 | 
			
		||||
tail -f /dev/null
 | 
			
		||||
tail -f /dev/null
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								thirdparty/LUnpack
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								thirdparty/LUnpack
									
									
									
									
										vendored
									
									
								
							 Submodule thirdparty/LUnpack deleted from f8d7e442a7
									
								
							
							
								
								
									
										1
									
								
								thirdparty/docker-utils
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								thirdparty/docker-utils
									
									
									
									
										vendored
									
									
								
							 Submodule thirdparty/docker-utils deleted from 3f0129e093
									
								
							
		Reference in New Issue
	
	Block a user