Merge remote-tracking branch 'upstream/main'

This commit is contained in:
EmosewaMC 2022-12-23 04:12:38 -08:00
commit a6c6474e67
32 changed files with 494 additions and 109 deletions

View File

@ -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"

View File

@ -13,7 +13,7 @@
#include "eSqliteDataType.h"
std::map<eSqliteDataType, std::string> FdbToSqlite::Convert::sqliteType = {
std::map<eSqliteDataType, std::string> FdbToSqlite::Convert::m_SqliteType = {
{ eSqliteDataType::NONE, "none"},
{ eSqliteDataType::INT32, "int32"},
{ eSqliteDataType::REAL, "real"},
@ -23,15 +23,21 @@ std::map<eSqliteDataType, std::string> 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<eSqliteDataType>(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<eSqliteDataType>(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<bool>(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);
}

View File

@ -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<eSqliteDataType, std::string> sqliteType;
std::string basePath{};
std::ifstream fdb{};
}; // class FdbToSqlite
/**
* Maps each sqlite data type to its string equivalent.
*/
static std::map<eSqliteDataType, std::string> 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__

View File

@ -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());

View File

@ -824,6 +824,22 @@ std::vector<ScriptComponent*> Entity::GetScriptComponents() {
return comps;
}
void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName) {
if (notificationName == "HitOrHealResult" || notificationName == "Hit") {
auto* destroyableComponent = GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
destroyableComponent->Subscribe(scriptObjId, scriptToAdd);
}
}
void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName) {
if (notificationName == "HitOrHealResult" || notificationName == "Hit") {
auto* destroyableComponent = GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
destroyableComponent->Unsubscribe(scriptObjId);
}
}
void Entity::SetProximityRadius(float proxRadius, std::string name) {
ProximityMonitorComponent* proxMon = GetComponent<ProximityMonitorComponent>();
if (!proxMon) {

View File

@ -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<ScriptComponent*> 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);

View File

@ -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);

View File

@ -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<std::function<void(Entity*)>> m_OnHitCallbacks;
/**
* The list of scripts subscribed to this components actions
*/
std::map<LWOOBJID, CppScripts::Script*> m_SubscribedScripts;
};
#endif // DESTROYABLECOMPONENT_H

View File

@ -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<CDComponentsRegistryTable>("ComponentsRegistry");
if (!compRegistryTable) return;
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), COMPONENT_TYPE_SCRIPT, -1);
if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable<CDScriptComponentTable>("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<CDComponentsRegistryTable>("ComponentsRegistry");
if (!compRegistryTable) return;
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), COMPONENT_TYPE_SCRIPT, -1);
if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable<CDScriptComponentTable>("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<CharacterComponent>();
if (!characterComponent) return;

View File

@ -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:

View File

@ -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;
}

View File

@ -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.

View File

@ -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<MissionComponent>();
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<MissionComponent>();
if (missionComponent) missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ACTIVITY, m_ActivityId);
}
}
} else{
auto* missionComponent = builder->GetComponent<MissionComponent>();
if (missionComponent) missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ACTIVITY, m_ActivityId);
}
LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
}

View File

@ -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<bool>(flagsOff != eAnimationFlags::IDLE_NONE);
if (flagsOff != eAnimationFlags::IDLE_NONE) bitStream.Write(flagsOff);
bitStream.Write<bool>(flagsOn != eAnimationFlags::IDLE_NONE);
if (flagsOn != eAnimationFlags::IDLE_NONE) bitStream.Write(flagsOn);
SEND_PACKET_BROADCAST;
}

View File

@ -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!");
}

View File

@ -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());

View File

@ -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);

View File

@ -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;

View File

@ -63,10 +63,11 @@ void AmDarklingDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t
if (skillComponent != nullptr) {
skillComponent->Interrupt();
skillComponent->Reset();
}
self->SetVar<int32_t>(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<int32_t>(u"weakspot", -1);
GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS);
}

View File

@ -59,8 +59,6 @@ void FvMaelstromDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_
auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
Game::logger->Log("FvMaelstromDragon", "Hit %i", destroyableComponent->GetArmor());
if (destroyableComponent->GetArmor() > 0) return;
auto weakpoint = self->GetVar<int32_t>(u"weakpoint");
@ -80,10 +78,12 @@ void FvMaelstromDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_
if (skillComponent != nullptr) {
skillComponent->Interrupt();
skillComponent->Reset();
}
self->SetVar<int32_t>(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<int32_t>(u"weakspot", -1);
GameMessages::SendNotifyObject(self->GetObjectID(), self->GetObjectID(), u"DragonRevive", UNASSIGNED_SYSTEM_ADDRESS);

View File

@ -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<SkillComponent>();
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<float_t>(u"reviveTime") != 0.0f
? self->GetVar<float_t>(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<uint32_t>(u"timesStunned", timesStunned + 1);
StunApe(self, false);

View File

@ -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})

View File

@ -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")

View File

@ -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);

View File

@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS
"CoilBackpackBase.cpp"
PARENT_SCOPE)

View File

@ -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<uint8_t>(u"coilCount", 0);
}
void CoilBackpackBase::NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {
if (damage > 0) {
self->SetVar<uint8_t>(u"coilCount", self->GetVar<uint8_t>(u"coilCount") + 1);
if (self->GetVar<uint8_t>(u"coilCount") > 4) {
auto* skillComponent = self->GetComponent<SkillComponent>();
if (!skillComponent) return;
skillComponent->CalculateBehavior(m_SkillId, m_BehaviorId, self->GetObjectID());
self->SetVar<uint8_t>(u"coilCount", 0);
}
}
}
void CoilBackpackBase::OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) {
itemOwner->Unsubscribe(itemObjId, "HitOrHealResult");
}

View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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());

View File

@ -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