diff --git a/CMakeLists.txt b/CMakeLists.txt index 163196fb..54f0d0dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,8 +89,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -# Create a /res directory -make_directory(${CMAKE_BINARY_DIR}/res) +# Create a /resServer directory +make_directory(${CMAKE_BINARY_DIR}/resServer) # Create a /logs directory make_directory(${CMAKE_BINARY_DIR}/logs) @@ -176,6 +176,7 @@ set(INCLUDED_DIRECTORIES "dScripts/ai" "dScripts/client" "dScripts/EquipmentScripts" + "dScripts/EquipmentTriggers" "dScripts/zone" "dScripts/02_server/DLU" "dScripts/02_server/Enemy" diff --git a/dCommon/FdbToSqlite.cpp b/dCommon/FdbToSqlite.cpp index 6015fd3a..80251e89 100644 --- a/dCommon/FdbToSqlite.cpp +++ b/dCommon/FdbToSqlite.cpp @@ -13,7 +13,7 @@ #include "eSqliteDataType.h" -std::map FdbToSqlite::Convert::sqliteType = { +std::map FdbToSqlite::Convert::m_SqliteType = { { eSqliteDataType::NONE, "none"}, { eSqliteDataType::INT32, "int32"}, { eSqliteDataType::REAL, "real"}, @@ -23,15 +23,21 @@ std::map FdbToSqlite::Convert::sqliteType = { { eSqliteDataType::TEXT_8, "text_8"} }; -FdbToSqlite::Convert::Convert(std::string basePath) { - this->basePath = basePath; +FdbToSqlite::Convert::Convert(std::string basePath, std::string binaryOutPath) { + this->m_BasePath = basePath; + 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() { - fdb.open(basePath + "/cdclient.fdb", std::ios::binary); - + if (m_ConversionStarted) return false; + this->m_ConversionStarted = true; try { - CDClientDatabase::Connect(basePath + "/CDServer.sqlite"); + CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite"); CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); @@ -44,13 +50,12 @@ bool FdbToSqlite::Convert::ConvertDatabase() { return false; } - fdb.close(); return true; } int32_t FdbToSqlite::Convert::ReadInt32() { int32_t nextInt{}; - BinaryIO::BinaryRead(fdb, nextInt); + BinaryIO::BinaryRead(m_Fdb, nextInt); return nextInt; } @@ -58,26 +63,26 @@ int64_t FdbToSqlite::Convert::ReadInt64() { int32_t prevPosition = SeekPointer(); int64_t value{}; - BinaryIO::BinaryRead(fdb, value); + BinaryIO::BinaryRead(m_Fdb, value); - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); return value; } std::string FdbToSqlite::Convert::ReadString() { int32_t prevPosition = SeekPointer(); - auto readString = BinaryIO::ReadString(fdb); + auto readString = BinaryIO::ReadString(m_Fdb); - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); return readString; } int32_t FdbToSqlite::Convert::SeekPointer() { int32_t position{}; - BinaryIO::BinaryRead(fdb, position); - int32_t prevPosition = fdb.tellg(); - fdb.seekg(position); + BinaryIO::BinaryRead(m_Fdb, position); + int32_t prevPosition = m_Fdb.tellg(); + m_Fdb.seekg(position); return prevPosition; } @@ -91,7 +96,7 @@ std::string FdbToSqlite::Convert::ReadColumnHeader() { std::string newTable = "CREATE TABLE IF NOT EXISTS '" + tableName + "' (" + columns + ");"; CDClientDatabase::ExecuteDML(newTable); - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); return tableName; } @@ -104,7 +109,7 @@ void FdbToSqlite::Convert::ReadTables(int32_t& numberOfTables) { ReadRowHeader(columnHeader); } - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); } std::string FdbToSqlite::Convert::ReadColumns(int32_t& numberOfColumns) { @@ -117,10 +122,10 @@ std::string FdbToSqlite::Convert::ReadColumns(int32_t& numberOfColumns) { if (i != 0) columnsToCreate << ", "; dataType = static_cast(ReadInt32()); name = ReadString(); - columnsToCreate << "'" << name << "' " << FdbToSqlite::Convert::sqliteType[dataType]; + columnsToCreate << "'" << name << "' " << FdbToSqlite::Convert::m_SqliteType[dataType]; } - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); return columnsToCreate.str(); } @@ -131,7 +136,7 @@ void FdbToSqlite::Convert::ReadRowHeader(std::string& tableName) { if (numberOfAllocatedRows != 0) assert((numberOfAllocatedRows & (numberOfAllocatedRows - 1)) == 0); // assert power of 2 allocation size ReadRows(numberOfAllocatedRows, tableName); - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); } void FdbToSqlite::Convert::ReadRows(int32_t& numberOfAllocatedRows, std::string& tableName) { @@ -141,28 +146,24 @@ void FdbToSqlite::Convert::ReadRows(int32_t& numberOfAllocatedRows, std::string& for (int32_t row = 0; row < numberOfAllocatedRows; row++) { int32_t rowPointer = ReadInt32(); if (rowPointer == -1) rowid++; - else ReadRow(rowid, rowPointer, tableName); + else ReadRow(rowPointer, tableName); } - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); } -void FdbToSqlite::Convert::ReadRow(int32_t& rowid, int32_t& position, std::string& tableName) { - int32_t prevPosition = fdb.tellg(); - fdb.seekg(position); +void FdbToSqlite::Convert::ReadRow(int32_t& position, std::string& tableName) { + int32_t prevPosition = m_Fdb.tellg(); + m_Fdb.seekg(position); while (true) { ReadRowInfo(tableName); int32_t linked = ReadInt32(); - - rowid += 1; - if (linked == -1) break; - - fdb.seekg(linked); + m_Fdb.seekg(linked); } - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); } void FdbToSqlite::Convert::ReadRowInfo(std::string& tableName) { @@ -171,7 +172,7 @@ void FdbToSqlite::Convert::ReadRowInfo(std::string& tableName) { int32_t numberOfColumns = ReadInt32(); ReadRowValues(numberOfColumns, tableName); - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); } void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& tableName) { @@ -191,7 +192,7 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& if (i != 0) insertedRow << ", "; // Only append comma and space after first entry in row. switch (static_cast(ReadInt32())) { case eSqliteDataType::NONE: - BinaryIO::BinaryRead(fdb, emptyValue); + BinaryIO::BinaryRead(m_Fdb, emptyValue); assert(emptyValue == 0); insertedRow << "NULL"; break; @@ -202,7 +203,7 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& break; case eSqliteDataType::REAL: - BinaryIO::BinaryRead(fdb, floatValue); + BinaryIO::BinaryRead(m_Fdb, floatValue); insertedRow << std::fixed << std::setprecision(34) << floatValue; // maximum precision of floating point number break; @@ -224,7 +225,7 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& } case eSqliteDataType::INT_BOOL: - BinaryIO::BinaryRead(fdb, boolValue); + BinaryIO::BinaryRead(m_Fdb, boolValue); insertedRow << static_cast(boolValue); break; @@ -244,5 +245,5 @@ void FdbToSqlite::Convert::ReadRowValues(int32_t& numberOfColumns, std::string& auto copiedString = insertedRow.str(); CDClientDatabase::ExecuteDML(copiedString); - fdb.seekg(prevPosition); + m_Fdb.seekg(prevPosition); } diff --git a/dCommon/FdbToSqlite.h b/dCommon/FdbToSqlite.h index a9611220..cfa5263b 100644 --- a/dCommon/FdbToSqlite.h +++ b/dCommon/FdbToSqlite.h @@ -12,38 +12,142 @@ enum class eSqliteDataType : int32_t; namespace FdbToSqlite { class Convert { public: - Convert(std::string inputFile); + /** + * Create a new convert object with an input .fdb file and an output binary path. + * + * @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(); + + /** + * Converts the input file to sqlite. Calling multiple times is safe. + * + * @return true if the database was converted properly, false otherwise. + */ bool ConvertDatabase(); + /** + * @brief Reads a 32 bit int from the fdb file. + * + * @return The read value + */ int32_t ReadInt32(); + /** + * @brief Reads a 64 bit integer from the fdb file. + * + * @return The read value + */ int64_t ReadInt64(); + /** + * @brief Reads a string from the fdb file. + * + * @return The read string + * + * TODO This needs to be translated to latin-1! + */ std::string ReadString(); + /** + * @brief Seeks to a pointer position. + * + * @return The previous position before the seek + */ int32_t SeekPointer(); + /** + * @brief Reads a column header from the fdb file and creates the table in the database + * + * @return The table name + */ std::string ReadColumnHeader(); + /** + * @brief Read the tables from the fdb file. + * + * @param numberOfTables The number of tables to read + */ void ReadTables(int32_t& numberOfTables); + /** + * @brief Reads the columns from the fdb file. + * + * @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); + /** + * @brief Reads the row header from the fdb file. + * + * @param tableName The tables name + */ void ReadRowHeader(std::string& tableName); + /** + * @brief Read the rows from the fdb file., + * + * @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 ReadRow(int32_t& rowid, int32_t& position, std::string& tableName); + /** + * @brief Reads a row from the fdb file. + * + * @param position The position to seek in the fdb to + * @param tableName The tables name + */ + void ReadRow(int32_t& position, std::string& tableName); + /** + * @brief Reads the row info from the fdb file. + * + * @param tableName The tables name + */ void ReadRowInfo(std::string& tableName); + /** + * @brief Reads each row and its values from the fdb file and inserts them into the database + * + * @param numberOfColumns The number of columns to read in + * @param tableName The tables name + */ void ReadRowValues(int32_t& numberOfColumns, std::string& tableName); private: - static std::map sqliteType; - std::string basePath{}; - std::ifstream fdb{}; - }; // class FdbToSqlite + + /** + * Maps each sqlite data type to its string equivalent. + */ + static std::map m_SqliteType; + + /** + * 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. + */ + bool m_ConversionStarted{}; + + /** + * The path where the CDServer will be stored + */ + std::string m_BinaryOutPath{}; + }; //! class FdbToSqlite }; //! namespace FdbToSqlite #endif //!__FDBTOSQLITE__H__ diff --git a/dDatabase/MigrationRunner.cpp b/dDatabase/MigrationRunner.cpp index 54def9e2..5e70c401 100644 --- a/dDatabase/MigrationRunner.cpp +++ b/dDatabase/MigrationRunner.cpp @@ -54,7 +54,7 @@ void MigrationRunner::RunMigrations() { if (doExit) continue; Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str()); - if (migration.name == "5_brick_model_sd0.sql") { + if (migration.name == "dlu/5_brick_model_sd0.sql") { runSd0Migrations = true; } else { finalSQL.append(migration.data.c_str()); diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index cbab87db..16b2c5a1 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -824,6 +824,22 @@ std::vector Entity::GetScriptComponents() { return comps; } +void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName) { + if (notificationName == "HitOrHealResult" || notificationName == "Hit") { + auto* destroyableComponent = GetComponent(); + if (!destroyableComponent) return; + destroyableComponent->Subscribe(scriptObjId, scriptToAdd); + } +} + +void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName) { + if (notificationName == "HitOrHealResult" || notificationName == "Hit") { + auto* destroyableComponent = GetComponent(); + if (!destroyableComponent) return; + destroyableComponent->Unsubscribe(scriptObjId); + } +} + void Entity::SetProximityRadius(float proxRadius, std::string name) { ProximityMonitorComponent* proxMon = GetComponent(); if (!proxMon) { diff --git a/dGame/Entity.h b/dGame/Entity.h index 6c0968f8..e0d38308 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -26,8 +26,13 @@ class Spawner; class ScriptComponent; class dpEntity; class Component; +class Item; class Character; +namespace CppScripts { + class Script; +}; + /** * An entity in the world. Has multiple components. */ @@ -139,6 +144,9 @@ public: std::vector GetScriptComponents(); + void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName); + void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName); + void SetProximityRadius(float proxRadius, std::string name); void SetProximityRadius(dpEntity* entity, std::string name); diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index c899d9e8..6902917f 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -631,6 +631,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 auto* attacker = EntityManager::Instance()->GetEntity(source); m_Parent->OnHit(attacker); m_Parent->OnHitOrHealResult(attacker, sourceDamage); + NotifySubscribers(attacker, sourceDamage); for (const auto& cb : m_OnHitCallbacks) { cb(attacker); @@ -648,6 +649,29 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 Smash(source, eKillType::VIOLENT, u"", skillID); } +void DestroyableComponent::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd) { + m_SubscribedScripts.insert(std::make_pair(scriptObjId, scriptToAdd)); + Game::logger->LogDebug("DestroyableComponent", "Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID()); + Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size()); +} + +void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) { + auto foundScript = m_SubscribedScripts.find(scriptObjId); + if (foundScript != m_SubscribedScripts.end()) { + m_SubscribedScripts.erase(foundScript); + Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID()); + } else { + Game::logger->LogDebug("DestroyableComponent", "Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId); + } + Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size()); +} + +void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) { + for (auto script : m_SubscribedScripts) { + script.second->NotifyHitOrHealResult(m_Parent, attacker, damage); + } +} + void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID) { if (m_iHealth > 0) { SetArmor(0); diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index b8e81b33..5bb990a7 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -7,6 +7,10 @@ #include "Entity.h" #include "Component.h" +namespace CppScripts { + class Script; +}; //! namespace CppScripts + /** * Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which * indicate which enemies this entity has. @@ -422,6 +426,17 @@ public: */ void AddFactionNoLookup(int32_t faction) { m_FactionIDs.push_back(faction); }; + /** + * Notify subscribed scripts of Damage actions. + * + * @param attacker The attacking Entity + * @param damage The amount of damage that was done + */ + void NotifySubscribers(Entity* attacker, uint32_t damage); + + void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd); + void Unsubscribe(LWOOBJID scriptObjId); + private: /** * Whether or not the health should be serialized @@ -557,6 +572,11 @@ private: * The list of callbacks that will be called when this entity gets hit */ std::vector> m_OnHitCallbacks; + + /** + * The list of scripts subscribed to this components actions + */ + std::map m_SubscribedScripts; }; #endif // DESTROYABLECOMPONENT_H diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 8215664c..eeb6afa7 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -27,8 +27,9 @@ #include "dConfig.h" #include "eItemType.h" #include "eUnequippableActiveType.h" +#include "CppScripts.h" -InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { +InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document): Component(parent) { this->m_Dirty = true; this->m_Equipped = {}; this->m_Pushed = {}; @@ -867,6 +868,8 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { AddItemSkills(item->GetLot()); + EquipScripts(item); + EntityManager::Instance()->SerializeEntity(m_Parent); } @@ -895,6 +898,8 @@ void InventoryComponent::UnEquipItem(Item* item) { PurgeProxies(item); + UnequipScripts(item); + EntityManager::Instance()->SerializeEntity(m_Parent); // Trigger property event @@ -904,6 +909,37 @@ void InventoryComponent::UnEquipItem(Item* item) { } } + +void InventoryComponent::EquipScripts(Item* equippedItem) { + CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance()->GetTable("ComponentsRegistry"); + if (!compRegistryTable) return; + int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), COMPONENT_TYPE_SCRIPT, -1); + if (scriptComponentID > -1) { + CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable("ScriptComponent"); + CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); + auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name); + if (!itemScript) { + Game::logger->Log("InventoryComponent", "null script?"); + } + itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId()); + } +} + +void InventoryComponent::UnequipScripts(Item* unequippedItem) { + CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance()->GetTable("ComponentsRegistry"); + if (!compRegistryTable) return; + int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), COMPONENT_TYPE_SCRIPT, -1); + if (scriptComponentID > -1) { + CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable("ScriptComponent"); + CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); + auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name); + if (!itemScript) { + Game::logger->Log("InventoryComponent", "null script?"); + } + itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId()); + } +} + void InventoryComponent::HandlePossession(Item* item) { auto* characterComponent = m_Parent->GetComponent(); if (!characterComponent) return; diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 8ae35f6c..44636d95 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -352,6 +352,20 @@ public: */ static uint32_t FindSkill(LOT lot); + /** + * Call this when you equip an item. This calls OnFactionTriggerItemEquipped for any scripts found on the items. + * + * @param equippedItem The item script to lookup and call equip on + */ + void EquipScripts(Item* equippedItem); + + /** + * Call this when you unequip an item. This calls OnFactionTriggerItemUnequipped for any scripts found on the items. + * + * @param unequippedItem The item script to lookup and call unequip on + */ + void UnequipScripts(Item* unequippedItem); + ~InventoryComponent() override; private: diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index 75465669..4b5743cd 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -98,7 +98,7 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) { } void RacingControlComponent::LoadPlayerVehicle(Entity* player, - bool initialLoad) { + uint32_t positionNumber, bool initialLoad) { // Load the player's vehicle. if (player == nullptr) { @@ -126,33 +126,18 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, auto* path = dZoneManager::Instance()->GetZone()->GetPath( GeneralUtils::UTF16ToWTF8(m_PathName)); - auto startPosition = path->pathWaypoints[0].position + NiPoint3::UNIT_Y * 3; - - const auto spacing = 15; - - // This sometimes spawns the vehicle out of the map if there are lots of - // players loaded. - - const auto range = m_LoadedPlayers * spacing; - - startPosition = - startPosition + NiPoint3::UNIT_Z * ((m_LeadingPlayer / 2) + - m_RacingPlayers.size() * spacing); - - auto startRotation = - NiQuaternion::LookAt(startPosition, startPosition + NiPoint3::UNIT_X); - - auto angles = startRotation.GetEulerAngles(); - - angles.y -= M_PI; - - startRotation = NiQuaternion::FromEulerAngles(angles); - - Game::logger->Log("RacingControlComponent", - "Start position <%f, %f, %f>, <%f, %f, %f>", - startPosition.x, startPosition.y, startPosition.z, - angles.x * (180.0f / M_PI), angles.y * (180.0f / M_PI), - angles.z * (180.0f / M_PI)); + auto spawnPointEntities = EntityManager::Instance()->GetEntitiesByLOT(4843); + auto startPosition = NiPoint3::ZERO; + auto startRotation = NiQuaternion::IDENTITY; + const std::string placementAsString = std::to_string(positionNumber); + for (auto entity : spawnPointEntities) { + if (!entity) continue; + if (entity->GetVarAsString(u"placement") == placementAsString) { + startPosition = entity->GetPosition(); + startRotation = entity->GetRotation(); + break; + } + } // Make sure the player is at the correct position. @@ -567,12 +552,12 @@ void RacingControlComponent::Update(float deltaTime) { Game::logger->Log("RacingControlComponent", "Loading all players..."); - for (size_t i = 0; i < m_LobbyPlayers.size(); i++) { + for (size_t positionNumber = 0; positionNumber < m_LobbyPlayers.size(); positionNumber++) { Game::logger->Log("RacingControlComponent", "Loading player now!"); auto* player = - EntityManager::Instance()->GetEntity(m_LobbyPlayers[i]); + EntityManager::Instance()->GetEntity(m_LobbyPlayers[positionNumber]); if (player == nullptr) { return; @@ -581,7 +566,7 @@ void RacingControlComponent::Update(float deltaTime) { Game::logger->Log("RacingControlComponent", "Loading player now NOW!"); - LoadPlayerVehicle(player, true); + LoadPlayerVehicle(player, positionNumber + 1, true); m_Loaded = true; } diff --git a/dGame/dComponents/RacingControlComponent.h b/dGame/dComponents/RacingControlComponent.h index dac60962..933178cc 100644 --- a/dGame/dComponents/RacingControlComponent.h +++ b/dGame/dComponents/RacingControlComponent.h @@ -123,7 +123,7 @@ public: * @param player The player who's vehicle to initialize. * @param initialLoad Is this the first time the player is loading in this race? */ - void LoadPlayerVehicle(Entity* player, bool initialLoad = false); + void LoadPlayerVehicle(Entity* player, uint32_t positionNumber, bool initialLoad = false); /** * Invoked when the client says it has loaded in. diff --git a/dGame/dComponents/RebuildComponent.cpp b/dGame/dComponents/RebuildComponent.cpp index 6ec9b964..c7c4e3a5 100644 --- a/dGame/dComponents/RebuildComponent.cpp +++ b/dGame/dComponents/RebuildComponent.cpp @@ -14,6 +14,7 @@ #include "Spawner.h" #include "MovingPlatformComponent.h" #include "Preconditions.h" +#include "TeamManager.h" #include "CppScripts.h" @@ -464,12 +465,20 @@ void RebuildComponent::CompleteRebuild(Entity* user) { auto* builder = GetBuilder(); - if (builder != nullptr) { - auto* missionComponent = builder->GetComponent(); - if (missionComponent != nullptr) { - missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ACTIVITY, m_ActivityId); + if (builder) { + auto* team = TeamManager::Instance()->GetTeam(builder->GetObjectID()); + if (team) { + for (const auto memberId : team->members) { // progress missions for all team members + auto* member = EntityManager::Instance()->GetEntity(memberId); + if (member) { + auto* missionComponent = member->GetComponent(); + if (missionComponent) missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ACTIVITY, m_ActivityId); + } + } + } else{ + auto* missionComponent = builder->GetComponent(); + if (missionComponent) missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ACTIVITY, m_ActivityId); } - LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1); } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 9753868d..c3b86647 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -3924,14 +3924,16 @@ void GameMessages::SendDisplayChatBubble(LWOOBJID objectId, const std::u16string } -void GameMessages::SendChangeIdleFlags(LWOOBJID objectId, eAnimationFlags FlagsOn, eAnimationFlags FlagsOff, const SystemAddress& sysAddr) { +void GameMessages::SendChangeIdleFlags(LWOOBJID objectId, eAnimationFlags flagsOn, eAnimationFlags flagsOff, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; bitStream.Write(objectId); bitStream.Write(GAME_MSG::GAME_MSG_CHANGE_IDLE_FLAGS); - bitStream.Write(FlagsOff); - bitStream.Write(FlagsOn); + bitStream.Write(flagsOff != eAnimationFlags::IDLE_NONE); + if (flagsOff != eAnimationFlags::IDLE_NONE) bitStream.Write(flagsOff); + bitStream.Write(flagsOn != eAnimationFlags::IDLE_NONE); + if (flagsOn != eAnimationFlags::IDLE_NONE) bitStream.Write(flagsOn); SEND_PACKET_BROADCAST; } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 7b1bf66c..9a63f83c 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -1762,6 +1762,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit scriptedActivityComponent->ReloadConfig(); } + Game::server->UpdateMaximumMtuSize(); Game::server->UpdateBandwidthLimit(); ChatPackets::SendSystemMessage(sysAddr, u"Successfully reloaded config for world!"); } diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index ba36247c..34ce59c7 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -146,26 +146,38 @@ int main(int argc, char** argv) { MigrationRunner::RunMigrations(); - // Check CDClient exists - if (!std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite")) { - Game::logger->Log("WorldServer", "CDServer.sqlite could not be opened. Looking for cdclient.fdb to convert to sqlite."); + const bool cdServerExists = std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite"); + const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite"); + const bool fdbExists = std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb"); - if (!std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb")) { - Game::logger->Log("WorldServer", "cdclient.fdb could not be opened. Please move a cdclient.fdb or an already converted database to build/res."); - return EXIT_FAILURE; - } - - Game::logger->Log("WorldServer", "Found cdclient.fdb. Converting to SQLite"); - - if (FdbToSqlite::Convert(Game::assetManager->GetResPath().string()).ConvertDatabase() == false) { - Game::logger->Log("MasterServer", "Failed to convert fdb to sqlite"); - return EXIT_FAILURE; + if (!cdServerExists) { + if (oldCDServerExists) { + // If the file doesn't exist in the new CDServer location, copy it there. We copy because we may not have write permissions from the previous directory. + 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", + "%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(); + } + 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", "Failed to convert fdb to sqlite."); + return FinalizeShutdown(); + } } } //Connect to CDClient try { - CDClientDatabase::Connect((Game::assetManager->GetResPath() / "CDServer.sqlite").string()); + CDClientDatabase::Connect((BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").string()); } catch (CppSQLite3Exception& e) { Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database"); Game::logger->Log("WorldServer", "Error: %s", e.errorMessage()); diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index 55e07da3..a3961d45 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -68,7 +68,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect } else { mLogger->Log("dServer", "FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port); return; } mLogger->SetLogToConsole(prevLogSetting); - mPeer->SetMTUSize(1228); // This is hard coded by lu for some reason. + //Connect to master if we are not master: if (serverType != ServerType::Master) { SetupForMasterConnection(); @@ -188,6 +188,7 @@ bool dServer::Startup() { mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15); } else { UpdateBandwidthLimit(); + UpdateMaximumMtuSize(); mPeer->SetIncomingPassword("3.25 ND1", 8); } @@ -197,6 +198,11 @@ bool dServer::Startup() { return true; } +void dServer::UpdateMaximumMtuSize() { + auto maxMtuSize = mConfig->GetValue("maximum_mtu_size"); + mPeer->SetMTUSize(maxMtuSize.empty() ? 1228 : std::stoi(maxMtuSize)); +} + void dServer::UpdateBandwidthLimit() { auto newBandwidth = mConfig->GetValue("maximum_outgoing_bandwidth"); mPeer->SetPerConnectionOutgoingBandwidthLimit(!newBandwidth.empty() ? std::stoi(newBandwidth) : 0); diff --git a/dNet/dServer.h b/dNet/dServer.h index af4c8322..d9e74d2e 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -57,6 +57,7 @@ public: ReplicaManager* GetReplicaManager() { return mReplicaManager; } void UpdateReplica(); void UpdateBandwidthLimit(); + void UpdateMaximumMtuSize(); int GetPing(const SystemAddress& sysAddr) const; int GetLatestPing(const SystemAddress& sysAddr) const; diff --git a/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp b/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp index 8ec49d3e..9e0570ef 100644 --- a/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp +++ b/dScripts/02_server/Enemy/AM/AmDarklingDragon.cpp @@ -63,10 +63,11 @@ void AmDarklingDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t if (skillComponent != nullptr) { skillComponent->Interrupt(); + skillComponent->Reset(); } self->SetVar(u"weakpoint", 2); - + GameMessages::SendChangeIdleFlags(self->GetObjectID(), eAnimationFlags::IDLE_NONE, eAnimationFlags::IDLE_COMBAT, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPlayAnimation(self, u"stunstart", 1.7f); self->AddTimer("timeToStunLoop", 1); @@ -131,7 +132,9 @@ void AmDarklingDragon::OnTimerDone(Entity* self, std::string timerName) { } if (skillComponent != nullptr) { skillComponent->Interrupt(); + skillComponent->Reset(); } + GameMessages::SendChangeIdleFlags(self->GetObjectID(), eAnimationFlags::IDLE_COMBAT, eAnimationFlags::IDLE_NONE, UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(u"weakspot", -1); GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp b/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp index 47ddb4bf..ec513694 100644 --- a/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp +++ b/dScripts/02_server/Enemy/FV/FvMaelstromDragon.cpp @@ -59,8 +59,6 @@ void FvMaelstromDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_ auto* destroyableComponent = self->GetComponent(); if (destroyableComponent != nullptr) { - Game::logger->Log("FvMaelstromDragon", "Hit %i", destroyableComponent->GetArmor()); - if (destroyableComponent->GetArmor() > 0) return; auto weakpoint = self->GetVar(u"weakpoint"); @@ -80,10 +78,12 @@ void FvMaelstromDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_ if (skillComponent != nullptr) { skillComponent->Interrupt(); + skillComponent->Reset(); } self->SetVar(u"weakpoint", 2); + GameMessages::SendChangeIdleFlags(self->GetObjectID(), eAnimationFlags::IDLE_NONE, eAnimationFlags::IDLE_COMBAT, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPlayAnimation(self, u"stunstart", 1.7f); self->AddTimer("timeToStunLoop", 1); @@ -150,8 +150,9 @@ void FvMaelstromDragon::OnTimerDone(Entity* self, std::string timerName) { if (skillComponent != nullptr) { skillComponent->Interrupt(); + skillComponent->Reset(); } - + GameMessages::SendChangeIdleFlags(self->GetObjectID(), eAnimationFlags::IDLE_COMBAT, eAnimationFlags::IDLE_NONE, UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(u"weakspot", -1); GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS); diff --git a/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp b/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp index 3d0b8e25..96419c08 100644 --- a/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp +++ b/dScripts/02_server/Enemy/General/BaseEnemyApe.cpp @@ -31,9 +31,12 @@ void BaseEnemyApe::OnHit(Entity* self, Entity* attacker) { if (destroyableComponent != nullptr && destroyableComponent->GetArmor() < 1 && !self->GetBoolean(u"knockedOut")) { StunApe(self, true); self->CancelTimer("spawnQBTime"); - + auto* skillComponent = self->GetComponent(); + if (skillComponent) { + skillComponent->Reset(); + } GameMessages::SendPlayAnimation(self, u"disable", 1.7f); - + GameMessages::SendChangeIdleFlags(self->GetObjectID(), eAnimationFlags::IDLE_NONE, eAnimationFlags::IDLE_COMBAT, UNASSIGNED_SYSTEM_ADDRESS); const auto reviveTime = self->GetVar(u"reviveTime") != 0.0f ? self->GetVar(u"reviveTime") : 12.0f; self->AddTimer("reviveTime", reviveTime); @@ -50,6 +53,7 @@ void BaseEnemyApe::OnTimerDone(Entity* self, std::string timerName) { destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor() / timesStunned); } EntityManager::Instance()->SerializeEntity(self); + GameMessages::SendChangeIdleFlags(self->GetObjectID(), eAnimationFlags::IDLE_COMBAT, eAnimationFlags::IDLE_NONE, UNASSIGNED_SYSTEM_ADDRESS); self->SetVar(u"timesStunned", timesStunned + 1); StunApe(self, false); diff --git a/dScripts/CMakeLists.txt b/dScripts/CMakeLists.txt index fce3b721..ac600dbc 100644 --- a/dScripts/CMakeLists.txt +++ b/dScripts/CMakeLists.txt @@ -39,6 +39,12 @@ foreach(file ${DSCRIPTS_SOURCES_EQUIPMENTSCRIPTS}) set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "EquipmentScripts/${file}") endforeach() +add_subdirectory(EquipmentTriggers) + +foreach(file ${DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS}) + set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "EquipmentTriggers/${file}") +endforeach() + add_subdirectory(zone) foreach(file ${DSCRIPTS_SOURCES_ZONE}) diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index dce77e4b..745a74ae 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -278,6 +278,9 @@ #include "ImaginationBackpackHealServer.h" #include "LegoDieRoll.h" #include "BuccaneerValiantShip.h" +#include "GemPack.h" +#include "ShardArmor.h" +#include "TeslaPack.h" // Survival scripts #include "AgSurvivalStromling.h" @@ -837,6 +840,12 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new BuccaneerValiantShip(); else if (scriptName == "scripts\\EquipmentScripts\\FireFirstSkillonStartup.lua") script = new FireFirstSkillonStartup(); + else if (scriptName == "scripts\\equipmenttriggers\\gempack.lua") + script = new GemPack(); + else if (scriptName == "scripts\\equipmenttriggers\\shardarmor.lua") + script = new ShardArmor(); + else if (scriptName == "scripts\\equipmenttriggers\\coilbackpack.lua") + script = new TeslaPack(); // FB else if (scriptName == "scripts\\ai\\NS\\WH\\L_ROCKHYDRANT_BROKEN.lua") diff --git a/dScripts/CppScripts.h b/dScripts/CppScripts.h index 916d2638..e4a6d655 100644 --- a/dScripts/CppScripts.h +++ b/dScripts/CppScripts.h @@ -172,6 +172,13 @@ namespace CppScripts { */ virtual void OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {}; + /** + * Invoked when self has received either a hit or heal. Only used for scripts subscribed to an entity. + * + * Equivalent to 'function notifyHitOrHealResult(self, msg)' + */ + virtual void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {}; + /** * Invoked when a player has responsed to a mission. * @@ -316,6 +323,22 @@ namespace CppScripts { virtual void OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName, float_t pathTime, float_t totalTime, int32_t waypoint) { }; + + /** + * Used by items to tell their owner that they were equipped. + * + * @param itemOwner The owner of the item + * @param itemObjId The items Object ID + */ + virtual void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) {}; + + /** + * Used by items to tell their owner that they were unequipped. + * + * @param itemOwner The owner of the item + * @param itemObjId The items Object ID + */ + virtual void OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) {}; }; Script* GetScript(Entity* parent, const std::string& scriptName); diff --git a/dScripts/EquipmentTriggers/CMakeLists.txt b/dScripts/EquipmentTriggers/CMakeLists.txt new file mode 100644 index 00000000..416ef553 --- /dev/null +++ b/dScripts/EquipmentTriggers/CMakeLists.txt @@ -0,0 +1,3 @@ +set(DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS + "CoilBackpackBase.cpp" + PARENT_SCOPE) diff --git a/dScripts/EquipmentTriggers/CoilBackpackBase.cpp b/dScripts/EquipmentTriggers/CoilBackpackBase.cpp new file mode 100644 index 00000000..d3102e0e --- /dev/null +++ b/dScripts/EquipmentTriggers/CoilBackpackBase.cpp @@ -0,0 +1,25 @@ +#include "CoilBackpackBase.h" + +#include "Entity.h" +#include "SkillComponent.h" + +void CoilBackpackBase::OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) { + itemOwner->Subscribe(itemObjId, this, "HitOrHealResult"); + itemOwner->SetVar(u"coilCount", 0); +} + +void CoilBackpackBase::NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) { + if (damage > 0) { + self->SetVar(u"coilCount", self->GetVar(u"coilCount") + 1); + if (self->GetVar(u"coilCount") > 4) { + auto* skillComponent = self->GetComponent(); + if (!skillComponent) return; + skillComponent->CalculateBehavior(m_SkillId, m_BehaviorId, self->GetObjectID()); + self->SetVar(u"coilCount", 0); + } + } +} + +void CoilBackpackBase::OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) { + itemOwner->Unsubscribe(itemObjId, "HitOrHealResult"); +} diff --git a/dScripts/EquipmentTriggers/CoilBackpackBase.h b/dScripts/EquipmentTriggers/CoilBackpackBase.h new file mode 100644 index 00000000..290c6c0f --- /dev/null +++ b/dScripts/EquipmentTriggers/CoilBackpackBase.h @@ -0,0 +1,21 @@ +#ifndef __GemPackBase__H__ +#define __GemPackBase__H__ + +#include "CppScripts.h" + +class CoilBackpackBase: public CppScripts::Script { +public: + CoilBackpackBase(uint32_t skillId, uint32_t behaviorId) { + m_SkillId = skillId; + m_BehaviorId = behaviorId; + }; + + void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) override; + void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) override; + void OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) override; +private: + uint32_t m_SkillId = 0; + uint32_t m_BehaviorId = 0; +}; + +#endif //!__GemPackBase__H__ diff --git a/dScripts/EquipmentTriggers/GemPack.h b/dScripts/EquipmentTriggers/GemPack.h new file mode 100644 index 00000000..13bf7b0b --- /dev/null +++ b/dScripts/EquipmentTriggers/GemPack.h @@ -0,0 +1,14 @@ +#ifndef __GEMPACK__H__ +#define __GEMPACK__H__ + +#include "CoilBackpackBase.h" + +class GemPack : public CoilBackpackBase { +public: + GemPack() : CoilBackpackBase(skillId, behaviorId) {}; +private: + static const uint32_t skillId = 1488; + static const uint32_t behaviorId = 36779; +}; + +#endif //!__GEMPACK__H__ diff --git a/dScripts/EquipmentTriggers/ShardArmor.h b/dScripts/EquipmentTriggers/ShardArmor.h new file mode 100644 index 00000000..01d2fe33 --- /dev/null +++ b/dScripts/EquipmentTriggers/ShardArmor.h @@ -0,0 +1,14 @@ +#ifndef __SHARDARMOR__H__ +#define __SHARDARMOR__H__ + +#include "CoilBackpackBase.h" + +class ShardArmor : public CoilBackpackBase { +public: + ShardArmor() : CoilBackpackBase(skillId, behaviorId) {}; +private: + static const uint32_t skillId = 1249; + static const uint32_t behaviorId = 29086; +}; + +#endif //!__SHARDARMOR__H__ diff --git a/dScripts/EquipmentTriggers/TeslaPack.h b/dScripts/EquipmentTriggers/TeslaPack.h new file mode 100644 index 00000000..6e8bc9a4 --- /dev/null +++ b/dScripts/EquipmentTriggers/TeslaPack.h @@ -0,0 +1,14 @@ +#ifndef __TESLAPACK__H__ +#define __TESLAPACK__H__ + +#include "CoilBackpackBase.h" + +class TeslaPack : public CoilBackpackBase { +public: + TeslaPack() : CoilBackpackBase(skillId, behaviorId) {}; +private: + static const uint32_t skillId = 1001; + static const uint32_t behaviorId = 20917; +}; + +#endif //!__TESLAPACK__H__ diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 90fb041a..de6deb2a 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -154,7 +154,7 @@ int main(int argc, char** argv) { // Connect to CDClient try { - CDClientDatabase::Connect((Game::assetManager->GetResPath() / "CDServer.sqlite").string()); + CDClientDatabase::Connect((BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").string()); } catch (CppSQLite3Exception& e) { Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database"); Game::logger->Log("WorldServer", "Error: %s", e.errorMessage()); diff --git a/resources/sharedconfig.ini b/resources/sharedconfig.ini index 439ffe2f..d2c43d11 100644 --- a/resources/sharedconfig.ini +++ b/resources/sharedconfig.ini @@ -26,5 +26,13 @@ dump_folder= # Either the folder with /res or with /client and /versions client_location= -# The maximum outgoing bandwidth in bits +# The maximum outgoing bandwidth in bits. If your clients are having +# issues with enemies taking a while to catch up to them, increse this value. maximum_outgoing_bandwidth=80000 + +# The Maximum Translation Unit (MTU) size for packets. If players are +# getting stuck at 55% on the loading screen, lower this number to +# reduce the chances of packet loss. This value only has an effect +# from 512 <= maximum_mtu_size <= 1492 so make sure to keep this +# value within that range. +maximum_mtu_size=1228