diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index 3d0c5cc8..1865c679 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -7,6 +7,7 @@ body:
- type: checkboxes
id: checks
attributes:
+ label: "Make sure you've done the following:"
options:
- label: >
I have checked that this issue has not already been reported.
diff --git a/.github/ISSUE_TEMPLATE/documentation_improvement.yaml b/.github/ISSUE_TEMPLATE/documentation_improvement.yaml
index 12d4cb4a..e79a6a30 100644
--- a/.github/ISSUE_TEMPLATE/documentation_improvement.yaml
+++ b/.github/ISSUE_TEMPLATE/documentation_improvement.yaml
@@ -5,7 +5,9 @@ labels: ["docs", "triage"]
body:
- type: checkboxes
+ id: checks
attributes:
+ label: "Make sure you've done the following:"
options:
- label: >
I have checked that this issue has not already been reported.
diff --git a/.github/ISSUE_TEMPLATE/installation_issue.yaml b/.github/ISSUE_TEMPLATE/installation_issue.yaml
index f5cfb566..2fd9a84f 100644
--- a/.github/ISSUE_TEMPLATE/installation_issue.yaml
+++ b/.github/ISSUE_TEMPLATE/installation_issue.yaml
@@ -7,6 +7,7 @@ body:
- type: checkboxes
id: checks
attributes:
+ label: "Make sure you've done the following:"
options:
- label: >
I have read the [installation guide](https://github.com/DarkflameUniverse/DarkflameServer/blob/main/README.md).
diff --git a/.github/ISSUE_TEMPLATE/performance_issue.yaml b/.github/ISSUE_TEMPLATE/performance_issue.yaml
index f9fe4945..420a5381 100644
--- a/.github/ISSUE_TEMPLATE/performance_issue.yaml
+++ b/.github/ISSUE_TEMPLATE/performance_issue.yaml
@@ -7,6 +7,7 @@ body:
- type: checkboxes
id: checks
attributes:
+ label: "Make sure you've done the following:"
options:
- label: >
I have checked that this issue has not already been reported.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..ae7501e8
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,33 @@
+name: Cmake CI
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+ - name: cmake
+ uses: lukka/run-cmake@v10
+ with:
+ configurePreset: 'default'
+ buildPreset: 'default'
+ - name: artifacts
+ uses: actions/upload-artifact@v2
+ with:
+ name: linux-build
+ path: |
+ build
+ !build/CMakeCache.txt
+ !build/build.ninja
+ !build/_deps
+ !build/cmake_install.cmake
+ !build/*.a
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf7988c8..fe1f2751 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -123,6 +123,9 @@ make_directory(${CMAKE_BINARY_DIR}/res)
# Create a /locale directory
make_directory(${CMAKE_BINARY_DIR}/locale)
+# Create a /logs directory
+make_directory(${CMAKE_BINARY_DIR}/logs)
+
# Copy ini files on first build
if (NOT EXISTS ${PROJECT_BINARY_DIR}/authconfig.ini)
configure_file(
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 00000000..c892287a
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,26 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 12,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "default",
+ "displayName": "Default Configure Settings",
+ "description": "Sets build and install directories",
+ "binaryDir": "${sourceDir}/build",
+ "generator": "Unix Makefiles"
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "default",
+ "configurePreset": "default",
+ "displayName": "Default Build",
+ "description": "Default Build",
+ "jobs": 2
+ }
+ ]
+}
diff --git a/README.md b/README.md
index f3cb6cd7..190bfab1 100644
--- a/README.md
+++ b/README.md
@@ -289,10 +289,10 @@ Here is a summary of the commands available in-game. All commands are prefixed b
|
- instance-info
+ instanceinfo
|
- /instance-info
+ /instanceinfo
|
Displays in the chat the current zone, clone, and instance id.
diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp
index 22f1dfa0..67590fa0 100644
--- a/dAuthServer/AuthServer.cpp
+++ b/dAuthServer/AuthServer.cpp
@@ -45,6 +45,7 @@ int main(int argc, char** argv) {
dConfig config("authconfig.ini");
Game::config = &config;
Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console"))));
+ Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1");
//Connect to the MySQL Database
std::string mysql_host = config.GetValue("mysql_host");
@@ -152,11 +153,13 @@ int main(int argc, char** argv) {
dLogger * SetupLogger() {
std::string logPath = "./logs/AuthServer_" + std::to_string(time(nullptr)) + ".log";
bool logToConsole = false;
+ bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
+ logDebugStatements = true;
#endif
- return new dLogger(logPath, logToConsole);
+ return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) {
diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp
index 057a6568..737bbba4 100644
--- a/dChatServer/ChatPacketHandler.cpp
+++ b/dChatServer/ChatPacketHandler.cpp
@@ -351,6 +351,13 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet)
return;
}
+ if (team->memberIDs.size() > 3) {
+ // no more teams greater than 4
+
+ Game::logger->Log("ChatPacketHandler", "Someone tried to invite a 5th player to a team");
+ return;
+ }
+
SendTeamInvite(other, player);
Game::logger->Log("ChatPacketHandler", "Got team invite: %llu -> %s\n", playerID, invitedPlayer.c_str());
diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp
index be5ec904..81904d41 100644
--- a/dChatServer/ChatServer.cpp
+++ b/dChatServer/ChatServer.cpp
@@ -48,6 +48,7 @@ int main(int argc, char** argv) {
dConfig config("chatconfig.ini");
Game::config = &config;
Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console"))));
+ Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1");
//Connect to the MySQL Database
std::string mysql_host = config.GetValue("mysql_host");
@@ -156,14 +157,16 @@ int main(int argc, char** argv) {
return 0;
}
-dLogger* SetupLogger() {
+dLogger * SetupLogger() {
std::string logPath = "./logs/ChatServer_" + std::to_string(time(nullptr)) + ".log";
bool logToConsole = false;
+ bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
+ logDebugStatements = true;
#endif
- return new dLogger(logPath, logToConsole);
+ return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) {
diff --git a/dCommon/dLogger.cpp b/dCommon/dLogger.cpp
index 6ee0e7d2..825c10cb 100644
--- a/dCommon/dLogger.cpp
+++ b/dCommon/dLogger.cpp
@@ -1,7 +1,8 @@
#include "dLogger.h"
-dLogger::dLogger(const std::string& outpath, bool logToConsole) {
+dLogger::dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements) {
m_logToConsole = logToConsole;
+ m_logDebugStatements = logDebugStatements;
m_outpath = outpath;
#ifdef _WIN32
@@ -24,39 +25,25 @@ dLogger::~dLogger() {
#endif
}
-void dLogger::LogBasic(const std::string & message) {
- LogBasic(message.c_str());
-}
-
-void dLogger::LogBasic(const char * format, ...) {
+void dLogger::vLog(const char* format, va_list args) {
#ifdef _WIN32
time_t t = time(NULL);
struct tm time;
localtime_s(&time, &t);
-
char timeStr[70];
-
strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time);
-
char message[2048];
- va_list args;
- va_start(args, format);
vsprintf_s(message, format, args);
- va_end(args);
- if (m_logToConsole) std::cout << "[" << "time machine broke" << "] " << message;
- mFile << "[" << "time" << "] " << message;
+ if (m_logToConsole) std::cout << "[" << timeStr << "] " << message;
+ mFile << "[" << timeStr << "] " << message;
#else
time_t t = time(NULL);
struct tm * time = localtime(&t);
char timeStr[70];
strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", time);
-
char message[2048];
- va_list args;
- va_start(args, format);
vsprintf(message, format, args);
- va_end(args);
if (m_logToConsole) {
fputs("[", stdout);
@@ -76,62 +63,42 @@ void dLogger::LogBasic(const char * format, ...) {
#endif
}
-void dLogger::Log(const char * className, const char * format, ...) {
-#ifdef _WIN32
- time_t t = time(NULL);
- struct tm time;
- localtime_s(&time, &t);
-
- char timeStr[70];
- strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time);
-
- char message[2048];
+void dLogger::LogBasic(const char * format, ...) {
va_list args;
va_start(args, format);
- vsprintf_s(message, format, args);
-
+ vLog(format, args);
va_end(args);
+}
- if (m_logToConsole) std::cout << "[" << timeStr << "] [" << className << "]: " << message;
- mFile << "[" << timeStr << "] [" << className << "]: " << message;
-#else
- time_t t = time(NULL);
- struct tm * time = localtime(&t);
- char timeStr[70];
- strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", time);
-
- char message[2048];
- va_list args;
- va_start(args, format);
- vsprintf(message, format, args);
- va_end(args);
-
- if (m_logToConsole) {
- fputs("[", stdout);
- fputs(timeStr, stdout);
- fputs("] ", stdout);
- fputs("[", stdout);
- fputs(className, stdout);
- fputs("]: ", stdout);
- fputs(message, stdout);
- }
-
- if (fp != NULL) {
- fputs("[", fp);
- fputs(timeStr, fp);
- fputs("] ", fp);
- fputs("[", fp);
- fputs(className, fp);
- fputs("]: ", fp);
- fputs(message, fp);
- }
-#endif
+void dLogger::LogBasic(const std::string & message) {
+ LogBasic(message.c_str());
+}
+
+void dLogger::Log(const char * className, const char * format, ...) {
+ va_list args;
+ std::string log = "[" + std::string(className) + "] " + std::string(format);
+ va_start(args, format);
+ vLog(log.c_str(), args);
+ va_end(args);
}
void dLogger::Log(const std::string & className, const std::string & message) {
Log(className.c_str(), message.c_str());
}
+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);
+ va_start(args, format);
+ vLog(log.c_str(), args);
+ va_end(args);
+}
+
+void dLogger::LogDebug(const std::string & className, const std::string & message) {
+ LogDebug(className.c_str(), message.c_str());
+}
+
void dLogger::Flush() {
#ifdef _WIN32
mFile.flush();
diff --git a/dCommon/dLogger.h b/dCommon/dLogger.h
index 622a90d3..7448237e 100644
--- a/dCommon/dLogger.h
+++ b/dCommon/dLogger.h
@@ -7,21 +7,26 @@
class dLogger {
public:
- dLogger(const std::string& outpath, bool logToConsole);
+ dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements);
~dLogger();
void SetLogToConsole(bool logToConsole) { m_logToConsole = logToConsole; }
+ void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; }
+ void vLog(const char* format, va_list args);
void LogBasic(const std::string& message);
void LogBasic(const char* format, ...);
void Log(const char* className, const char* format, ...);
void Log(const std::string& className, const std::string& message);
+ void LogDebug(const std::string& className, const std::string& message);
+ void LogDebug(const char* className, const char* format, ...);
void Flush();
const bool GetIsLoggingToConsole() const { return m_logToConsole; }
private:
+ bool m_logDebugStatements;
bool m_logToConsole;
std::string m_outpath;
std::ofstream mFile;
diff --git a/dDatabase/Tables/CDLootMatrixTable.cpp b/dDatabase/Tables/CDLootMatrixTable.cpp
index 6d5a232d..17fd8313 100644
--- a/dDatabase/Tables/CDLootMatrixTable.cpp
+++ b/dDatabase/Tables/CDLootMatrixTable.cpp
@@ -57,6 +57,6 @@ std::vector CDLootMatrixTable::Query(std::function CDLootMatrixTable::GetEntries(void) const {
+const std::vector& CDLootMatrixTable::GetEntries(void) const {
return this->entries;
}
diff --git a/dDatabase/Tables/CDLootMatrixTable.h b/dDatabase/Tables/CDLootMatrixTable.h
index dc9410e9..f306324f 100644
--- a/dDatabase/Tables/CDLootMatrixTable.h
+++ b/dDatabase/Tables/CDLootMatrixTable.h
@@ -50,7 +50,7 @@ public:
/*!
\return The entries
*/
- std::vector GetEntries(void) const;
+ const std::vector& GetEntries(void) const;
};
diff --git a/dDatabase/Tables/CDLootTableTable.cpp b/dDatabase/Tables/CDLootTableTable.cpp
index 8f05fb80..62727ade 100644
--- a/dDatabase/Tables/CDLootTableTable.cpp
+++ b/dDatabase/Tables/CDLootTableTable.cpp
@@ -54,6 +54,6 @@ std::vector CDLootTableTable::Query(std::function CDLootTableTable::GetEntries(void) const {
+const std::vector& CDLootTableTable::GetEntries(void) const {
return this->entries;
}
diff --git a/dDatabase/Tables/CDLootTableTable.h b/dDatabase/Tables/CDLootTableTable.h
index c210073c..750adcb4 100644
--- a/dDatabase/Tables/CDLootTableTable.h
+++ b/dDatabase/Tables/CDLootTableTable.h
@@ -46,7 +46,7 @@ public:
/*!
\return The entries
*/
- std::vector GetEntries(void) const;
+ const std::vector& GetEntries(void) const;
};
diff --git a/dDatabase/Tables/CDRarityTableTable.cpp b/dDatabase/Tables/CDRarityTableTable.cpp
index f5b09b76..da41e3f3 100644
--- a/dDatabase/Tables/CDRarityTableTable.cpp
+++ b/dDatabase/Tables/CDRarityTableTable.cpp
@@ -52,6 +52,6 @@ std::vector CDRarityTableTable::Query(std::function CDRarityTableTable::GetEntries(void) const {
+const std::vector& CDRarityTableTable::GetEntries(void) const {
return this->entries;
}
diff --git a/dDatabase/Tables/CDRarityTableTable.h b/dDatabase/Tables/CDRarityTableTable.h
index c2de03a1..0a57e903 100644
--- a/dDatabase/Tables/CDRarityTableTable.h
+++ b/dDatabase/Tables/CDRarityTableTable.h
@@ -66,7 +66,7 @@ public:
/*!
\return The entries
*/
- std::vector GetEntries(void) const;
+ const std::vector& GetEntries(void) const;
};
diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.cpp b/dGame/dBehaviors/AreaOfEffectBehavior.cpp
index a5341348..b87c7c17 100644
--- a/dGame/dBehaviors/AreaOfEffectBehavior.cpp
+++ b/dGame/dBehaviors/AreaOfEffectBehavior.cpp
@@ -74,7 +74,7 @@ void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream
includeFaction = 1;
}
- for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction))
+ for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction, m_TargetSelf == 1))
{
auto* entity = EntityManager::Instance()->GetEntity(validTarget);
@@ -155,4 +155,6 @@ void AreaOfEffectBehavior::Load()
this->m_ignoreFaction = GetInt("ignore_faction");
this->m_includeFaction = GetInt("include_faction");
+
+ this->m_TargetSelf = GetInt("target_self");
}
diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.h b/dGame/dBehaviors/AreaOfEffectBehavior.h
index c2b6679d..37313499 100644
--- a/dGame/dBehaviors/AreaOfEffectBehavior.h
+++ b/dGame/dBehaviors/AreaOfEffectBehavior.h
@@ -13,6 +13,8 @@ public:
int32_t m_ignoreFaction;
int32_t m_includeFaction;
+
+ int32_t m_TargetSelf;
/*
* Inherited
diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp
index fd3117a4..2637aa61 100644
--- a/dGame/dBehaviors/BehaviorContext.cpp
+++ b/dGame/dBehaviors/BehaviorContext.cpp
@@ -149,7 +149,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit
return;
}
-
+
behavior->Sync(this, bitStream, branch);
}
@@ -325,7 +325,7 @@ void BehaviorContext::Reset()
this->scheduledUpdates.clear();
}
-std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction) const
+std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf) const
{
auto* entity = EntityManager::Instance()->GetEntity(this->caster);
@@ -353,21 +353,20 @@ std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, in
}
}
- if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && !entity->HasComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS) && targets.empty()))
+ if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && targets.empty()))
{
- DestroyableComponent* destroyableComponent;
+ DestroyableComponent* destroyableComponent;
if (!entity->TryGetComponent(COMPONENT_TYPE_DESTROYABLE, destroyableComponent))
{
return targets;
}
auto entities = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS);
-
for (auto* candidate : entities)
{
const auto id = candidate->GetObjectID();
- if (destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction))
+ if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction))
{
targets.push_back(id);
}
diff --git a/dGame/dBehaviors/BehaviorContext.h b/dGame/dBehaviors/BehaviorContext.h
index 58154512..21b2e6e9 100644
--- a/dGame/dBehaviors/BehaviorContext.h
+++ b/dGame/dBehaviors/BehaviorContext.h
@@ -102,7 +102,7 @@ struct BehaviorContext
void Reset();
- std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0) const;
+ std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false) const;
explicit BehaviorContext(LWOOBJID originator, bool calculation = false);
diff --git a/dGame/dBehaviors/ForceMovementBehavior.cpp b/dGame/dBehaviors/ForceMovementBehavior.cpp
index 332cfed6..ea8c04a7 100644
--- a/dGame/dBehaviors/ForceMovementBehavior.cpp
+++ b/dGame/dBehaviors/ForceMovementBehavior.cpp
@@ -1,44 +1,81 @@
#include "ForceMovementBehavior.h"
-
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
+#include "ControllablePhysicsComponent.h"
+#include "EntityManager.h"
-void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
-{
- if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY)
- {
- return;
- }
+void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
+ if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
+ return;
+ }
- uint32_t handle;
-
- bitStream->Read(handle);
-
- context->RegisterSyncBehavior(handle, this, branch);
+ uint32_t handle;
+ bitStream->Read(handle);
+ context->RegisterSyncBehavior(handle, this, branch);
}
void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
- uint32_t next;
+ uint32_t next;
+ bitStream->Read(next);
- bitStream->Read(next);
+ LWOOBJID target;
+ bitStream->Read(target);
- LWOOBJID target;
+ branch.target = target;
+ auto* behavior = CreateBehavior(next);
+ behavior->Handle(context, bitStream, branch);
+}
- bitStream->Read(target);
+void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
+ if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
+ return;
+ }
- auto* behavior = CreateBehavior(next);
+ auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster);
+ if (casterEntity != nullptr) {
+ auto* controllablePhysicsComponent = casterEntity->GetComponent();
+ if (controllablePhysicsComponent != nullptr) {
- branch.target = target;
+ if (m_Forward == 1) {
+ controllablePhysicsComponent->SetVelocity(controllablePhysicsComponent->GetRotation().GetForwardVector() * 25);
+ }
- behavior->Handle(context, bitStream, branch);
+ EntityManager::Instance()->SerializeEntity(casterEntity);
+ }
+ }
+
+ const auto skillHandle = context->GetUniqueSkillId();
+ bitStream->Write(skillHandle);
+
+ context->SyncCalculation(skillHandle, this->m_Duration, this, branch);
}
void ForceMovementBehavior::Load()
{
- this->m_hitAction = GetAction("hit_action");
-
- this->m_hitEnemyAction = GetAction("hit_action_enemy");
-
- this->m_hitFactionAction = GetAction("hit_action_faction");
+ this->m_hitAction = GetAction("hit_action");
+ this->m_hitEnemyAction = GetAction("hit_action_enemy");
+ this->m_hitFactionAction = GetAction("hit_action_faction");
+ this->m_Duration = GetFloat("duration");
+ this->m_Forward = GetFloat("forward");
+ this->m_Left = GetFloat("left");
+ this->m_Yaw = GetFloat("yaw");
+}
+
+void ForceMovementBehavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
+ auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster);
+ if (casterEntity != nullptr) {
+ auto* controllablePhysicsComponent = casterEntity->GetComponent();
+ if (controllablePhysicsComponent != nullptr) {
+
+ controllablePhysicsComponent->SetPosition(controllablePhysicsComponent->GetPosition() + controllablePhysicsComponent->GetVelocity() * m_Duration);
+ controllablePhysicsComponent->SetVelocity({});
+
+ EntityManager::Instance()->SerializeEntity(casterEntity);
+ }
+ }
+
+ this->m_hitAction->Calculate(context, bitStream, branch);
+ this->m_hitEnemyAction->Calculate(context, bitStream, branch);
+ this->m_hitEnemyAction->Calculate(context, bitStream, branch);
}
diff --git a/dGame/dBehaviors/ForceMovementBehavior.h b/dGame/dBehaviors/ForceMovementBehavior.h
index 5b77e4b7..50b0aa26 100644
--- a/dGame/dBehaviors/ForceMovementBehavior.h
+++ b/dGame/dBehaviors/ForceMovementBehavior.h
@@ -9,6 +9,11 @@ public:
Behavior* m_hitEnemyAction;
Behavior* m_hitFactionAction;
+
+ float_t m_Duration;
+ float_t m_Forward;
+ float_t m_Left;
+ float_t m_Yaw;
/*
* Inherited
@@ -18,8 +23,12 @@ public:
{
}
+ void Calculate(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override;
+
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
+ void SyncCalculation(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override;
+
void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp
index 44154f52..ae929d57 100644
--- a/dGame/dComponents/BaseCombatAIComponent.cpp
+++ b/dGame/dComponents/BaseCombatAIComponent.cpp
@@ -129,6 +129,9 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
BaseCombatAIComponent::~BaseCombatAIComponent() {
if (m_dpEntity)
dpWorld::Instance().RemoveEntity(m_dpEntity);
+
+ if (m_dpEntityEnemy)
+ dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
}
void BaseCombatAIComponent::Update(const float deltaTime) {
diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp
index cd67b845..01beaa5f 100644
--- a/dGame/dComponents/DestroyableComponent.cpp
+++ b/dGame/dComponents/DestroyableComponent.cpp
@@ -769,26 +769,33 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
if (team != nullptr && m_Parent->GetComponent() != nullptr)
{
LWOOBJID specificOwner = LWOOBJID_EMPTY;
+ auto* scriptedActivityComponent = m_Parent->GetComponent();
+ uint32_t teamSize = team->members.size();
+ uint32_t lootMatrixId = GetLootMatrixID();
- if (team->lootOption == 0) // Round robin
- {
- specificOwner = TeamManager::Instance()->GetNextLootOwner(team);
+ if (scriptedActivityComponent) {
+ lootMatrixId = scriptedActivityComponent->GetLootMatrixForTeamSize(teamSize);
}
- for (const auto memberId : team->members)
- {
- if (specificOwner != LWOOBJID_EMPTY && memberId != specificOwner) continue;
+ if (team->lootOption == 0) { // Round robin
+ specificOwner = TeamManager::Instance()->GetNextLootOwner(team);
- auto* member = EntityManager::Instance()->GetEntity(memberId);
+ auto* member = EntityManager::Instance()->GetEntity(specificOwner);
- if (member == nullptr) continue;
+ if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
+ }
+ else {
+ for (const auto memberId : team->members) { // Free for all
+ auto* member = EntityManager::Instance()->GetEntity(memberId);
- Loot::DropLoot(member, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
+ if (member == nullptr) continue;
+
+ LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
+ }
}
}
- else
- {
- Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
+ else { // drop loot for non team user
+ LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
}
}
}
@@ -815,7 +822,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= coinsToLoose;
- Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose);
+ LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose);
}
character->SetCoins(coinsTotal);
diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp
index e7fd02c8..bba7a892 100644
--- a/dGame/dComponents/RacingControlComponent.cpp
+++ b/dGame/dComponents/RacingControlComponent.cpp
@@ -317,7 +317,9 @@ void RacingControlComponent::OnRequestDie(Entity *player) {
return;
}
- racingPlayer.smashedTimes++;
+ if (!racingPlayer.noSmashOnReload) {
+ racingPlayer.smashedTimes++;
+ }
// Reset player to last checkpoint
GameMessages::SendRacingSetPlayerResetInfo(
@@ -390,7 +392,7 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity *player,
// Calculate the score, different loot depending on player count
const auto score = m_LoadedPlayers * 10 + data->finished;
- Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score);
+ LootGenerator::Instance().GiveActivityLoot(player, m_Parent, m_ActivityID, score);
// Giving rewards
GameMessages::SendNotifyRacingClient(
diff --git a/dGame/dComponents/RebuildComponent.cpp b/dGame/dComponents/RebuildComponent.cpp
index d7106228..7d4ae926 100644
--- a/dGame/dComponents/RebuildComponent.cpp
+++ b/dGame/dComponents/RebuildComponent.cpp
@@ -452,7 +452,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ACTIVITY, m_ActivityId);
}
- Loot::DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
+ LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
}
m_Builder = LWOOBJID_EMPTY;
diff --git a/dGame/dComponents/ScriptedActivityComponent.cpp b/dGame/dComponents/ScriptedActivityComponent.cpp
index 1dadd205..dba5714d 100644
--- a/dGame/dComponents/ScriptedActivityComponent.cpp
+++ b/dGame/dComponents/ScriptedActivityComponent.cpp
@@ -16,6 +16,7 @@
#include "GeneralUtils.h"
#include "dZoneManager.h"
#include "dConfig.h"
+#include "DestroyableComponent.h"
ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activityID) : Component(parent)
{
@@ -43,6 +44,31 @@ ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activit
}
}
}
+
+ auto* destroyableComponent = m_Parent->GetComponent();
+
+ if (destroyableComponent) {
+ // check for LMIs and set the loot LMIs
+ CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance()->GetTable("ActivityRewards");
+ std::vector activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
+
+ uint32_t startingLMI = 0;
+
+ if (activityRewards.size() > 0) {
+ startingLMI = activityRewards[0].LootMatrixIndex;
+ }
+
+ if (startingLMI > 0) {
+ // now time for bodge :)
+
+ std::vector objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
+ for (const auto& item : objectTemplateActivities) {
+ if (item.activityRating > 0 && item.activityRating < 5) {
+ m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
+ }
+ }
+ }
+ }
}
ScriptedActivityComponent::~ScriptedActivityComponent()
@@ -524,7 +550,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
maxCoins = currencyTable[0].maxvalue;
}
- Loot::DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
+ LootGenerator::Instance().DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
}
}
diff --git a/dGame/dComponents/ScriptedActivityComponent.h b/dGame/dComponents/ScriptedActivityComponent.h
index bbeca2b4..8d79df8b 100644
--- a/dGame/dComponents/ScriptedActivityComponent.h
+++ b/dGame/dComponents/ScriptedActivityComponent.h
@@ -328,6 +328,13 @@ public:
* @param mapID the map ID to set
*/
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
+
+ /**
+ * Returns the LMI that this activity points to for a team size
+ * @param teamSize the team size to get the LMI for
+ * @return the LMI that this activity points to for a team size
+ */
+ uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
private:
/**
@@ -349,6 +356,11 @@ private:
* All the activity score for the players in this activity
*/
std::vector m_ActivityPlayers;
+
+ /**
+ * LMIs for team sizes
+ */
+ std::unordered_map m_ActivityLootMatrices;
};
#endif // SCRIPTEDACTIVITYCOMPONENT_H
diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp
index 97b6e038..20f5321a 100644
--- a/dGame/dInventory/Item.cpp
+++ b/dGame/dInventory/Item.cpp
@@ -333,14 +333,14 @@ bool Item::UseNonEquip()
{
std::unordered_map result {};
- Loot::CalculateLootMatrix(pack.LootMatrixIndex, entityParent, result);
+ result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex);
if (!inventory->GetComponent()->HasSpaceForLoot(result))
{
return false;
}
- Loot::GiveLoot(inventory->GetComponent()->GetParent(), result);
+ LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result);
}
inventory->GetComponent()->RemoveItem(lot, 1);
diff --git a/dGame/dUtilities/Loot.cpp b/dGame/dUtilities/Loot.cpp
index 836961bc..4ec056a5 100644
--- a/dGame/dUtilities/Loot.cpp
+++ b/dGame/dUtilities/Loot.cpp
@@ -1,384 +1,389 @@
-#include "Loot.h"
-#include "GameMessages.h"
-
-#include "CDClientManager.h"
-#include "CDLootMatrixTable.h"
-#include "CDLootTableTable.h"
-
-#include "SimplePhysicsComponent.h"
-#include "ControllablePhysicsComponent.h"
-#include "DestroyableComponent.h"
-#include "MissionComponent.h"
-#include "CharacterComponent.h"
-#include "TeamManager.h"
-
#include
-std::vector Loot::GetLootOfRarity(const std::vector &lootTable, uint32_t rarity) {
- std::vector refinedLoot;
- for (auto loot : lootTable) {
- CDItemComponent item = Inventory::FindItemComponent(loot.itemid);
- if (item.rarity == rarity) {
- refinedLoot.push_back(loot);
- }
- else if (item.rarity == 0) {
- refinedLoot.push_back(loot); // powerups
- }
- }
+#include "Loot.h"
- return refinedLoot;
+#include "CDComponentsRegistryTable.h"
+#include "CDItemComponentTable.h"
+#include "CDLootMatrixTable.h"
+#include "CDLootTableTable.h"
+#include "CDRarityTableTable.h"
+#include "Character.h"
+#include "Entity.h"
+#include "GameMessages.h"
+#include "GeneralUtils.h"
+#include "InventoryComponent.h"
+#include "MissionComponent.h"
+
+LootGenerator::LootGenerator() {
+ CDLootTableTable* lootTableTable = CDClientManager::Instance()->GetTable("LootTable");
+ CDComponentsRegistryTable* componentsRegistryTable = CDClientManager::Instance()->GetTable("ComponentsRegistry");
+ CDItemComponentTable* itemComponentTable = CDClientManager::Instance()->GetTable("ItemComponent");
+ CDLootMatrixTable* lootMatrixTable = CDClientManager::Instance()->GetTable("LootMatrix");
+ CDRarityTableTable* rarityTableTable = CDClientManager::Instance()->GetTable("RarityTable");
+
+ // ==============================
+ // Cache Item Rarities
+ // ==============================
+
+ std::vector uniqueItems;
+
+ for (const CDLootTable& loot : lootTableTable->GetEntries()) {
+ uniqueItems.push_back(loot.itemid);
+ }
+
+ // filter out duplicates
+ std::sort(uniqueItems.begin(), uniqueItems.end());
+ uniqueItems.erase(std::unique(uniqueItems.begin(), uniqueItems.end()), uniqueItems.end());
+
+ for (const uint32_t itemID : uniqueItems) {
+ uint32_t itemComponentID = componentsRegistryTable->GetByIDAndType(itemID, COMPONENT_TYPE_ITEM);
+ const CDItemComponent& item = itemComponentTable->GetItemComponentByID(itemComponentID);
+
+ m_ItemRarities.insert({itemID, item.rarity});
+ }
+
+ // ==============================
+ // Cache Rarity Tables
+ // ==============================
+
+ std::vector uniqueRarityIndices;
+
+ for (const CDRarityTable& rarity : rarityTableTable->GetEntries()) {
+ uniqueRarityIndices.push_back(rarity.RarityTableIndex);
+ }
+
+ // filter out duplicates
+ std::sort(uniqueRarityIndices.begin(), uniqueRarityIndices.end());
+ uniqueRarityIndices.erase(std::unique(uniqueRarityIndices.begin(), uniqueRarityIndices.end()), uniqueRarityIndices.end());
+
+ for (const uint32_t index : uniqueRarityIndices) {
+ std::vector table = rarityTableTable->Query([index](const CDRarityTable& entry) { return entry.RarityTableIndex == index; });
+
+ RarityTable rarityTable;
+
+ for (const CDRarityTable& entry : table) {
+ RarityTableEntry rarity{entry.rarity, entry.randmax};
+ rarityTable.push_back(rarity);
+ }
+
+ // sort in descending order based on randMax
+ std::sort(rarityTable.begin(), rarityTable.end(), [](const RarityTableEntry& x, const RarityTableEntry& y) { return x.randMax > y.randMax; });
+
+ m_RarityTables.insert({index, rarityTable});
+ }
+
+ // ==============================
+ // Cache Loot Matrices
+ // ==============================
+
+ std::vector uniqueMatrixIndices;
+
+ for (const CDLootMatrix& matrix : lootMatrixTable->GetEntries()) {
+ uniqueMatrixIndices.push_back(matrix.LootMatrixIndex);
+ }
+
+ // filter out duplicates
+ std::sort(uniqueMatrixIndices.begin(), uniqueMatrixIndices.end());
+ uniqueMatrixIndices.erase(std::unique(uniqueMatrixIndices.begin(), uniqueMatrixIndices.end()), uniqueMatrixIndices.end());
+
+ for (const uint32_t index : uniqueMatrixIndices) {
+ std::vector matrix = lootMatrixTable->Query([index](const CDLootMatrix& entry) { return entry.LootMatrixIndex == index; });
+
+ LootMatrix lootMatrix;
+
+ for (const CDLootMatrix& entry : matrix) {
+ LootMatrixEntry matrixEntry{entry.LootTableIndex, entry.RarityTableIndex, entry.percent, entry.minToDrop, entry.maxToDrop};
+ lootMatrix.push_back(matrixEntry);
+ }
+
+ m_LootMatrices.insert({index, lootMatrix});
+ }
+
+ // ==============================
+ // Cache Loot Tables
+ // ==============================
+
+ std::vector uniqueTableIndices;
+
+ for (const CDLootTable& entry : lootTableTable->GetEntries()) {
+ uniqueTableIndices.push_back(entry.LootTableIndex);
+ }
+
+ // filter out duplicates
+ std::sort(uniqueTableIndices.begin(), uniqueTableIndices.end());
+ uniqueTableIndices.erase(std::unique(uniqueTableIndices.begin(), uniqueTableIndices.end()), uniqueTableIndices.end());
+
+ for (const uint32_t index : uniqueTableIndices) {
+ std::vector entries = lootTableTable->Query([index](const CDLootTable& entry) { return entry.LootTableIndex == index; });
+
+ LootTable lootTable;
+
+ for (const CDLootTable& entry : entries) {
+ LootTableEntry tableEntry{(LOT)entry.itemid, entry.MissionDrop};
+ lootTable.push_back(tableEntry);
+ }
+
+ // sort by item rarity descending
+ std::sort(lootTable.begin(), lootTable.end(), [&](const LootTableEntry& x, const LootTableEntry& y) {
+ return m_ItemRarities[x.itemID] > m_ItemRarities[y.itemID];
+ });
+
+ m_LootTables.insert({index, lootTable});
+ }
}
-void Loot::GiveLoot(Entity* user, uint32_t lootMatrixID) {
- user = user->GetOwner(); // If the owner is overwritten, we collect that here
+std::unordered_map LootGenerator::RollLootMatrix(Entity* player, uint32_t matrixIndex) {
+ auto* missionComponent = player->GetComponent();
- std::unordered_map result {};
+ std::unordered_map drops;
- CalculateLootMatrix(lootMatrixID, user, result);
+ if (missionComponent == nullptr) {
+ return drops;
+ }
- GiveLoot(user, result);
+ const LootMatrix& matrix = m_LootMatrices[matrixIndex];
+
+ for (const LootMatrixEntry& entry : matrix) {
+ if (GeneralUtils::GenerateRandomNumber(0, 1) < entry.percent) {
+ const LootTable& lootTable = m_LootTables[entry.lootTableIndex];
+ const RarityTable& rarityTable = m_RarityTables[entry.rarityTableIndex];
+
+ uint32_t dropCount = GeneralUtils::GenerateRandomNumber(entry.minDrop, entry.maxDrop);
+ for (uint32_t i = 0; i < dropCount; ++i) {
+ uint32_t maxRarity = 1;
+
+ float rarityRoll = GeneralUtils::GenerateRandomNumber(0, 1);
+
+ for (const RarityTableEntry& rarity : rarityTable) {
+ if (rarity.randMax >= rarityRoll) {
+ maxRarity = rarity.rarity;
+ } else {
+ break;
+ }
+ }
+
+ bool rarityFound = false;
+ std::vector possibleDrops;
+
+ for (const LootTableEntry& loot : lootTable) {
+ uint32_t rarity = m_ItemRarities[loot.itemID];
+
+ if (rarity == maxRarity) {
+ possibleDrops.push_back(loot);
+ rarityFound = true;
+ } else if (rarity < maxRarity && !rarityFound) {
+ possibleDrops.push_back(loot);
+ maxRarity = rarity;
+ }
+ }
+
+ if (possibleDrops.size() > 0) {
+ LootTableEntry drop = possibleDrops[GeneralUtils::GenerateRandomNumber(0, possibleDrops.size() - 1)];
+
+ // filter out uneeded mission items
+ if (drop.isMissionDrop && !missionComponent->RequiresItem(drop.itemID))
+ continue;
+
+ // convert faction token proxy
+ if (drop.itemID == 13763) {
+ if (missionComponent->GetMissionState(545) == MissionState::MISSION_STATE_COMPLETE)
+ drop.itemID = 8318; // "Assembly Token"
+ else if (missionComponent->GetMissionState(556) == MissionState::MISSION_STATE_COMPLETE)
+ drop.itemID = 8321; // "Venture League Token"
+ else if (missionComponent->GetMissionState(567) == MissionState::MISSION_STATE_COMPLETE)
+ drop.itemID = 8319; // "Sentinels Token"
+ else if (missionComponent->GetMissionState(578) == MissionState::MISSION_STATE_COMPLETE)
+ drop.itemID = 8320; // "Paradox Token"
+ }
+
+ if (drop.itemID == 13763) {
+ continue;
+ } // check if we aren't in faction
+
+ if (drops.find(drop.itemID) == drops.end()) {
+ drops.insert({drop.itemID, 1});
+ } else {
+ ++drops[drop.itemID];
+ }
+ }
+ }
+ }
+ }
+
+ return drops;
}
-void Loot::DropLoot(Entity* user, Entity* killedObject, uint32_t lootMatrixID, uint32_t minCoins, uint32_t maxCoins) {
- user = user->GetOwner(); // If the owner is overwritten, we collect that here
+std::unordered_map LootGenerator::RollLootMatrix(uint32_t matrixIndex) {
+ std::unordered_map drops;
- auto* inventoryComponent = user->GetComponent();
+ const LootMatrix& matrix = m_LootMatrices[matrixIndex];
- if (inventoryComponent == nullptr) {
- return;
- }
+ for (const LootMatrixEntry& entry : matrix) {
+ if (GeneralUtils::GenerateRandomNumber(0, 1) < entry.percent) {
+ const LootTable& lootTable = m_LootTables[entry.lootTableIndex];
+ const RarityTable& rarityTable = m_RarityTables[entry.rarityTableIndex];
- std::unordered_map result {};
+ uint32_t dropCount = GeneralUtils::GenerateRandomNumber(entry.minDrop, entry.maxDrop);
+ for (uint32_t i = 0; i < dropCount; ++i) {
+ uint32_t maxRarity = 1;
- CalculateLootMatrix(lootMatrixID, user, result);
+ float rarityRoll = GeneralUtils::GenerateRandomNumber(0, 1);
- DropLoot(user, killedObject, result, minCoins, maxCoins);
+ for (const RarityTableEntry& rarity : rarityTable) {
+ if (rarity.randMax >= rarityRoll) {
+ maxRarity = rarity.rarity;
+ } else {
+ break;
+ }
+ }
+
+ bool rarityFound = false;
+ std::vector possibleDrops;
+
+ for (const LootTableEntry& loot : lootTable) {
+ uint32_t rarity = m_ItemRarities[loot.itemID];
+
+ if (rarity == maxRarity) {
+ possibleDrops.push_back(loot);
+ rarityFound = true;
+ } else if (rarity < maxRarity && !rarityFound) {
+ possibleDrops.push_back(loot);
+ maxRarity = rarity;
+ }
+ }
+
+ if (possibleDrops.size() > 0) {
+ const LootTableEntry& drop = possibleDrops[GeneralUtils::GenerateRandomNumber(0, possibleDrops.size() - 1)];
+
+ if (drops.find(drop.itemID) == drops.end()) {
+ drops.insert({drop.itemID, 1});
+ } else {
+ ++drops[drop.itemID];
+ }
+ }
+ }
+ }
+ }
+
+ return drops;
}
-void Loot::GiveLoot(Entity* user, std::unordered_map& result) {
- user = user->GetOwner(); // If the owner is overwritten, we collect that here
+void LootGenerator::GiveLoot(Entity* player, uint32_t matrixIndex) {
+ player = player->GetOwner(); // If the owner is overwritten, we collect that here
- auto* inventoryComponent = user->GetComponent();
+ std::unordered_map result = RollLootMatrix(player, matrixIndex);
- if (inventoryComponent == nullptr) {
- return;
- }
-
- for (const auto& pair : result) {
- inventoryComponent->AddItem(pair.first, pair.second);
- }
+ GiveLoot(player, result);
}
-void Loot::DropLoot(Entity* user, Entity* killedObject, std::unordered_map& result, uint32_t minCoins, uint32_t maxCoins) {
- user = user->GetOwner(); // If the owner is overwritten, we collect that here
+void LootGenerator::GiveLoot(Entity* player, std::unordered_map& result) {
+ player = player->GetOwner(); // if the owner is overwritten, we collect that here
- auto* inventoryComponent = user->GetComponent();
+ auto* inventoryComponent = player->GetComponent();
- if (inventoryComponent == nullptr) {
- return;
- }
+ if (!inventoryComponent)
+ return;
- const auto spawnPosition = killedObject->GetPosition();
-
- const auto source = killedObject->GetObjectID();
-
- for (const auto& pair : result) {
- for (int i = 0; i < pair.second; ++i) {
- GameMessages::SendDropClientLoot(user, source, pair.first, 0, spawnPosition, 1);
- }
- }
-
- uint32_t coins = (int)(minCoins + GeneralUtils::GenerateRandomNumber(0, 1) * (maxCoins - minCoins));
-
- GameMessages::SendDropClientLoot(user, source, LOT_NULL, coins, spawnPosition);
+ for (const auto& pair : result) {
+ inventoryComponent->AddItem(pair.first, pair.second);
+ }
}
-void Loot::DropActivityLoot(Entity* user, Entity* source, uint32_t activityID, int32_t rating)
-{
- CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance()->GetTable("ActivityRewards");
- std::vector activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
+void LootGenerator::GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating) {
+ CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance()->GetTable("ActivityRewards");
+ std::vector activityRewards = activityRewardsTable->Query([activityID](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
- const CDActivityRewards* selectedReward = nullptr;
- for (const auto& activityReward : activityRewards)
- {
- if (activityReward.activityRating <= rating && (selectedReward == nullptr || activityReward.activityRating > selectedReward->activityRating))
- {
- selectedReward = &activityReward;
- }
- }
+ const CDActivityRewards* selectedReward = nullptr;
+ for (const auto& activityReward : activityRewards) {
+ if (activityReward.activityRating <= rating && (selectedReward == nullptr || activityReward.activityRating > selectedReward->activityRating)) {
+ selectedReward = &activityReward;
+ }
+ }
- if (selectedReward == nullptr)
- {
- return;
- }
+ if (!selectedReward)
+ return;
- uint32_t minCoins = 0;
- uint32_t maxCoins = 0;
+ uint32_t minCoins = 0;
+ uint32_t maxCoins = 0;
- CDCurrencyTableTable* currencyTableTable = CDClientManager::Instance()->GetTable("CurrencyTable");
- std::vector currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == selectedReward->CurrencyIndex && entry.npcminlevel == 1); });
+ CDCurrencyTableTable* currencyTableTable = CDClientManager::Instance()->GetTable("CurrencyTable");
+ std::vector currencyTable = currencyTableTable->Query([selectedReward](CDCurrencyTable entry) { return (entry.currencyIndex == selectedReward->CurrencyIndex && entry.npcminlevel == 1); });
- if (currencyTable.size() > 0) {
- minCoins = currencyTable[0].minvalue;
- maxCoins = currencyTable[0].maxvalue;
- }
+ if (currencyTable.size() > 0) {
+ minCoins = currencyTable[0].minvalue;
+ maxCoins = currencyTable[0].maxvalue;
+ }
- Loot::DropLoot(user, source, selectedReward->LootMatrixIndex, minCoins, maxCoins);
+ GiveLoot(player, selectedReward->LootMatrixIndex);
+
+ uint32_t coins = (int)(minCoins + GeneralUtils::GenerateRandomNumber(0, 1) * (maxCoins - minCoins));
+
+ auto* character = player->GetCharacter();
+
+ character->SetCoins(character->GetCoins() + coins);
}
-void Loot::GiveActivityLoot(Entity* user, Entity* source, uint32_t activityID, int32_t rating)
-{
- CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance()->GetTable("ActivityRewards");
- std::vector activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
+void LootGenerator::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
+ player = player->GetOwner(); // if the owner is overwritten, we collect that here
- const CDActivityRewards* selectedReward = nullptr;
- for (const auto& activityReward : activityRewards)
- {
- if (activityReward.activityRating <= rating && (selectedReward == nullptr || activityReward.activityRating > selectedReward->activityRating))
- {
- selectedReward = &activityReward;
- }
- }
+ auto* inventoryComponent = player->GetComponent();
- if (selectedReward == nullptr)
- {
- return;
- }
+ if (!inventoryComponent)
+ return;
- uint32_t minCoins = 0;
- uint32_t maxCoins = 0;
+ std::unordered_map result = RollLootMatrix(player, matrixIndex);
- CDCurrencyTableTable* currencyTableTable = CDClientManager::Instance()->GetTable("CurrencyTable");
- std::vector currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == selectedReward->CurrencyIndex && entry.npcminlevel == 1); });
-
- if (currencyTable.size() > 0) {
- minCoins = currencyTable[0].minvalue;
- maxCoins = currencyTable[0].maxvalue;
- }
-
- Loot::GiveLoot(user, selectedReward->LootMatrixIndex);
-
- uint32_t coins = (int)(minCoins + GeneralUtils::GenerateRandomNumber(0, 1) * (maxCoins - minCoins));
-
- auto* charactert = user->GetCharacter();
-
- charactert->SetCoins(charactert->GetCoins() + coins);
+ DropLoot(player, killedObject, result, minCoins, maxCoins);
}
-void Loot::CalculateLootMatrix(uint32_t lootMatrixID, Entity* user, std::unordered_map& result)
-{
- user = user->GetOwner();
+void LootGenerator::DropLoot(Entity* player, Entity* killedObject, std::unordered_map& result, uint32_t minCoins, uint32_t maxCoins) {
+ player = player->GetOwner(); // if the owner is overwritten, we collect that here
- auto* missionComponent = user->GetComponent();
+ auto* inventoryComponent = player->GetComponent();
- // Get our loot for this LOT's lootMatrixID:
- CDLootMatrixTable* lootMatrixTable = CDClientManager::Instance()->GetTable("LootMatrix");
- CDLootTableTable* lootTableTable = CDClientManager::Instance()->GetTable("LootTable");
- CDRarityTableTable* rarityTableTable = CDClientManager::Instance()->GetTable("RarityTable");
+ if (!inventoryComponent)
+ return;
- std::vector lootMatrix = lootMatrixTable->Query([lootMatrixID](CDLootMatrix entry) { return (entry.LootMatrixIndex == lootMatrixID); });
+ const auto spawnPosition = killedObject->GetPosition();
- // Now, loop through each entry
- for (uint32_t i = 0; i < lootMatrix.size(); ++i) {
- // Now, determine whether or not we should drop this
- float chanceToDrop = 1.0 - lootMatrix[i].percent;
- float shouldDrop = GeneralUtils::GenerateRandomNumber(0, 1);
+ const auto source = killedObject->GetObjectID();
- const auto rarityTableIndex = lootMatrix[i].RarityTableIndex;
+ for (const auto& pair : result) {
+ for (int i = 0; i < pair.second; ++i) {
+ GameMessages::SendDropClientLoot(player, source, pair.first, 0, spawnPosition, 1);
+ }
+ }
- std::vector rarityTable = rarityTableTable->Query([rarityTableIndex](CDRarityTable entry) { return (entry.RarityTableIndex == rarityTableIndex); });
-
- std::sort(rarityTable.begin(), rarityTable.end());
+ uint32_t coins = (int)(minCoins + GeneralUtils::GenerateRandomNumber(0, 1) * (maxCoins - minCoins));
- if (shouldDrop < chanceToDrop) {
- // We are not able to drop this item, so continue
- continue;
- }
-
- // If we reached here, we are able to drop the item
- uint32_t minToDrop = lootMatrix[i].minToDrop;
- uint32_t maxToDrop = lootMatrix[i].maxToDrop;
-
- // Now determine the number we will drop of items from this table
- uint32_t numToDrop = GeneralUtils::GenerateRandomNumber(minToDrop, maxToDrop);
-
- // Now, query the loot matrix index
- const auto lootTableIndex = lootMatrix[i].LootTableIndex;
-
- std::vector lootTable = lootTableTable->Query([lootTableIndex](CDLootTable entry) { return (entry.LootTableIndex == lootTableIndex); });
-
- // Now randomize these entries
- if (lootTable.size() > 1) {
- std::shuffle(std::begin(lootTable), std::end(lootTable), Game::randomEngine);
- }
-
- uint32_t addedItems = 0;
-
- if (lootTable.empty()) continue;
-
- while (addedItems < numToDrop) {
- addedItems++;
-
- float rarityRoll = GeneralUtils::GenerateRandomNumber(0, 1);
-
- // im sorry
- uint32_t highestRarity = 1; // LOOT_COMMON
- float highestRandMax = 0.0f;
- for (const auto& rarity : rarityTable) {
- if (rarityRoll > rarity.randmax && rarity.randmax > highestRandMax) {
- highestRandMax = rarity.randmax;
- highestRarity = rarity.rarity + 1;
- }
- }
-
- std::vector refinedLoot;
-
- if (lootTable.size() == 1)
- {
- refinedLoot = lootTable;
- }
- else
- {
- refinedLoot = GetLootOfRarity(lootTable, highestRarity);
-
- bool continueLoop = false;
- while (refinedLoot.empty())
- {
- if (highestRarity == 1)
- {
- continueLoop = true;
- break;
- }
-
- highestRarity -= 1;
-
- refinedLoot = GetLootOfRarity(lootTable, highestRarity);
-
- if (!refinedLoot.empty())
- {
- break;
- }
- }
-
- if (continueLoop) continue;
- }
-
- int randomTable = GeneralUtils::GenerateRandomNumber(0, refinedLoot.size() - 1);
-
- const auto& selectedTable = refinedLoot[randomTable];
-
- uint32_t itemLOT = selectedTable.itemid;
- bool isMissionItem = selectedTable.MissionDrop;
-
- if (isMissionItem && missionComponent != nullptr)
- {
- // TODO: this executes a query in a hot path, might be worth refactoring away
- if (!missionComponent->RequiresItem(itemLOT))
- {
- continue;
- }
- }
-
- if (lootTable.size() > numToDrop)
- {
- for (size_t i = 0; i < lootTable.size(); i++)
- {
- if (lootTable[i].id == selectedTable.id)
- {
- lootTable.erase(lootTable.begin() + i);
-
- break;
- }
- }
- }
-
- const auto& it = result.find(itemLOT);
- if (it != result.end()) {
- it->second++;
- }
- else {
- result.emplace(itemLOT, 1);
- }
- }
- }
-
- int32_t tokenCount = 0;
-
- const auto& tokens = result.find(13763);
-
- if (tokens != result.end()) {
- tokenCount = tokens->second;
-
- result.erase(tokens);
- }
-
- if (tokenCount == 0 || user == nullptr) {
- return;
- }
-
- if (missionComponent == nullptr) {
- return;
- }
-
- LOT tokenId = -1;
-
- if (missionComponent->GetMissionState(545) == MissionState::MISSION_STATE_COMPLETE) // "Join Assembly!"
- {
- tokenId = 8318; // "Assembly Token"
- }
-
- if (missionComponent->GetMissionState(556) == MissionState::MISSION_STATE_COMPLETE) // "Join Venture League!"
- {
- tokenId = 8321; // "Venture League Token"
- }
-
- if (missionComponent->GetMissionState(567) == MissionState::MISSION_STATE_COMPLETE) // "Join The Sentinels!"
- {
- tokenId = 8319; // "Sentinels Token"
- }
-
- if (missionComponent->GetMissionState(578) == MissionState::MISSION_STATE_COMPLETE) // "Join Paradox!"
- {
- tokenId = 8320; // "Paradox Token"
- }
-
- if (tokenId != -1)
- {
- result.emplace(tokenId, tokenCount);
- }
+ GameMessages::SendDropClientLoot(player, source, LOT_NULL, coins, spawnPosition);
}
-void Loot::DropItem(Entity* user, Entity* sourceObject, LOT item, int32_t currency, int32_t count, bool useTeam, bool freeForAll)
-{
- if (sourceObject == nullptr)
- {
- return;
- }
+void LootGenerator::DropActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating) {
+ CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance()->GetTable("ActivityRewards");
+ std::vector activityRewards = activityRewardsTable->Query([activityID](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
- const auto sourceID = sourceObject->GetObjectID();
- const auto sourcePosition = sourceObject->GetPosition();
+ const CDActivityRewards* selectedReward = nullptr;
+ for (const auto& activityReward : activityRewards) {
+ if (activityReward.activityRating <= rating && (selectedReward == nullptr || activityReward.activityRating > selectedReward->activityRating)) {
+ selectedReward = &activityReward;
+ }
+ }
- // If useTeam, drop the item once for each team member.
- auto* team = TeamManager::Instance()->GetTeam(user->GetObjectID());
+ if (selectedReward == nullptr) {
+ return;
+ }
- if (team != nullptr && useTeam)
- {
- for (const auto& memberID : team->members)
- {
- // Get the team member from its ID.
- auto* member = EntityManager::Instance()->GetEntity(memberID);
+ uint32_t minCoins = 0;
+ uint32_t maxCoins = 0;
- if (member == nullptr)
- {
- continue;
- }
+ CDCurrencyTableTable* currencyTableTable = CDClientManager::Instance()->GetTable("CurrencyTable");
+ std::vector currencyTable = currencyTableTable->Query([selectedReward](CDCurrencyTable entry) { return (entry.currencyIndex == selectedReward->CurrencyIndex && entry.npcminlevel == 1); });
- // Drop the item.
- GameMessages::SendDropClientLoot(member, sourceID, item, currency, sourcePosition, count);
- }
+ if (currencyTable.size() > 0) {
+ minCoins = currencyTable[0].minvalue;
+ maxCoins = currencyTable[0].maxvalue;
+ }
- return;
- }
-
- GameMessages::SendDropClientLoot(user, sourceID, item, currency, sourcePosition, count);
+ DropLoot(player, source, selectedReward->LootMatrixIndex, minCoins, maxCoins);
}
diff --git a/dGame/dUtilities/Loot.h b/dGame/dUtilities/Loot.h
index de232f77..23c6463d 100644
--- a/dGame/dUtilities/Loot.h
+++ b/dGame/dUtilities/Loot.h
@@ -1,31 +1,62 @@
#pragma once
+
#include "dCommonVars.h"
+#include
+#include "Singleton.h"
#include
-#include "CDClientManager.h"
+
class Entity;
+struct RarityTableEntry {
+ uint32_t rarity;
+ float randMax;
+};
+
+typedef std::vector RarityTable;
+
+struct LootMatrixEntry {
+ uint32_t lootTableIndex;
+ uint32_t rarityTableIndex;
+ float percent;
+ uint32_t minDrop;
+ uint32_t maxDrop;
+};
+
+typedef std::vector LootMatrix;
+
+struct LootTableEntry {
+ LOT itemID;
+ bool isMissionDrop;
+};
+
+typedef std::vector LootTable;
+
+// used for glue code with Entity and Player classes
namespace Loot {
- struct Info {
- LWOOBJID id;
- LOT lot;
- uint32_t count;
- };
+ struct Info {
+ LWOOBJID id;
+ LOT lot;
+ uint32_t count;
+ };
+}
- std::vector GetLootOfRarity(const std::vector &lootTable, uint32_t rarity);
- void DropActivityLoot(Entity* user, Entity* source, uint32_t activityID, int32_t rating = 0);
+class LootGenerator : public Singleton {
+ public:
+ LootGenerator();
- void GiveActivityLoot(Entity* user, Entity* source, uint32_t activityID, int32_t rating = 0);
+ std::unordered_map RollLootMatrix(Entity* player, uint32_t matrixIndex);
+ std::unordered_map RollLootMatrix(uint32_t matrixIndex);
+ void GiveLoot(Entity* player, uint32_t matrixIndex);
+ void GiveLoot(Entity* player, std::unordered_map& result);
+ void GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0);
+ void DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins);
+ void DropLoot(Entity* player, Entity* killedObject, std::unordered_map& result, uint32_t minCoins, uint32_t maxCoins);
+ void DropActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0);
- void CalculateLootMatrix(uint32_t lootMatrixID, Entity* user, std::unordered_map& result);
-
- void GiveLoot(Entity* user, uint32_t lootMatrixID);
-
- void DropLoot(Entity* user, Entity* killedObject, uint32_t lootMatrixID, uint32_t minCoins, uint32_t maxCoins);
-
- void GiveLoot(Entity* user, std::unordered_map& result);
-
- void DropLoot(Entity* user, Entity* killedObject, std::unordered_map& result, uint32_t minCoins, uint32_t maxCoins);
-
- void DropItem(Entity* user, Entity* sourceObject, LOT item, int32_t currency, int32_t count, bool useTeam = false, bool freeForAll = false);
-};
\ No newline at end of file
+ private:
+ std::unordered_map m_ItemRarities;
+ std::unordered_map m_RarityTables;
+ std::unordered_map m_LootMatrices;
+ std::unordered_map m_LootTables;
+};
diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp
index adcd5c30..8be99612 100644
--- a/dGame/dUtilities/SlashCommandHandler.cpp
+++ b/dGame/dUtilities/SlashCommandHandler.cpp
@@ -407,7 +407,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
stmt->execute();
delete stmt;
- if (chatCommand == "setMinifig" && args.size() == 2 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_FORUM_MODERATOR) { // could break characters so only allow if GM > 0
+ if (chatCommand == "setminifig" && args.size() == 2 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_FORUM_MODERATOR) { // could break characters so only allow if GM > 0
int32_t minifigItemId;
if (!GeneralUtils::TryParse(args[1], minifigItemId)) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig Item Id ID.");
@@ -434,7 +434,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
charComp->m_Character->SetMouth(minifigItemId);
} else if (lowerName == "righthand") {
charComp->m_Character->SetRightHand(minifigItemId);
- } else if (lowerName == "shirt") {
+ } else if (lowerName == "shirtcolor") {
charComp->m_Character->SetShirtColor(minifigItemId);
} else if (lowerName == "hands") {
charComp->m_Character->SetLeftHand(minifigItemId);
@@ -1691,6 +1691,43 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return;
}
+ if (chatCommand == "rollloot" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && args.size() >= 3) {
+ uint32_t lootMatrixIndex = 0;
+ uint32_t targetLot = 0;
+ uint32_t loops = 1;
+
+ if (!GeneralUtils::TryParse(args[0], lootMatrixIndex)) return;
+ if (!GeneralUtils::TryParse(args[1], targetLot)) return;
+ if (!GeneralUtils::TryParse(args[2], loops)) return;
+
+ uint64_t totalRuns = 0;
+
+ for (uint32_t i = 0; i < loops; i++) {
+ while (true) {
+ auto lootRoll = LootGenerator::Instance().RollLootMatrix(lootMatrixIndex);
+ totalRuns += 1;
+ bool doBreak = false;
+ for (const auto& kv : lootRoll) {
+ if ((uint32_t)kv.first == targetLot) {
+ doBreak = true;
+ }
+ }
+ if (doBreak) break;
+ }
+ }
+
+ std::u16string message = u"Ran loot drops looking for "
+ + GeneralUtils::to_u16string(targetLot)
+ + u", "
+ + GeneralUtils::to_u16string(loops)
+ + u" times. It ran "
+ + GeneralUtils::to_u16string(totalRuns)
+ + u" times. Averaging out at "
+ + GeneralUtils::to_u16string((float) totalRuns / loops);
+
+ ChatPackets::SendSystemMessage(sysAddr, message);
+ }
+
if (chatCommand == "inspect" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER && args.size() >= 1)
{
Entity* closest = nullptr;
diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp
index 218d21b6..41d34cfc 100644
--- a/dMasterServer/InstanceManager.cpp
+++ b/dMasterServer/InstanceManager.cpp
@@ -43,7 +43,8 @@ Instance * InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, L
maxPlayers = GetHardCap(mapID);
}
- instance = new Instance(mExternalIP, ++m_LastPort, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
+ uint32_t port = GetFreePort();
+ instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
//Start the actual process:
#ifdef _WIN32
@@ -59,7 +60,7 @@ Instance * InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, L
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
- cmd.append(std::to_string(m_LastPort));
+ cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(m_LastInstanceID));
cmd.append(" -maxclients ");
@@ -74,8 +75,6 @@ Instance * InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, L
system(cmd.c_str());
- m_LastPort++; //Increment it again because the next port is for World<->Server comm.
- m_LastPort++; //Increment it again because the next port is for World<->Chat comm.
m_Instances.push_back(instance);
if (instance) {
@@ -97,6 +96,25 @@ bool InstanceManager::IsPortInUse(uint32_t port) {
return false;
}
+uint32_t InstanceManager::GetFreePort() {
+ uint32_t port = m_LastPort;
+ std::vector usedPorts;
+ for (Instance* i : m_Instances) {
+ usedPorts.push_back(i->GetPort());
+ }
+
+ std::sort(usedPorts.begin(), usedPorts.end());
+
+ int portIdx = 0;
+ while (portIdx < usedPorts.size() && port == usedPorts[portIdx]) {
+ //increment by 3 since each instance uses 3 ports (instance, world-server, world-chat)
+ port += 3;
+ portIdx++;
+ }
+
+ return port;
+}
+
void InstanceManager::AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID) {
Instance* inst = FindInstance(mapID, instanceID);
if (inst) {
@@ -291,7 +309,8 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
int maxPlayers = 999;
- instance = new Instance(mExternalIP, ++m_LastPort, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
+ uint32_t port = GetFreePort();
+ instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
//Start the actual process:
std::string cmd = "start ./WorldServer.exe -zone ";
@@ -302,7 +321,7 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
- cmd.append(std::to_string(m_LastPort));
+ cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(m_LastInstanceID));
cmd.append(" -maxclients ");
@@ -317,8 +336,6 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
system(cmd.c_str());
- m_LastPort++; //Increment it again because the next port is for World<->Server comm.
- m_LastPort++; //Increment it again because the next port is for World<->Chat comm.
m_Instances.push_back(instance);
if (instance) return instance;
diff --git a/dMasterServer/InstanceManager.h b/dMasterServer/InstanceManager.h
index 4475f11c..5dc93849 100644
--- a/dMasterServer/InstanceManager.h
+++ b/dMasterServer/InstanceManager.h
@@ -102,6 +102,7 @@ public:
Instance* GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID); //Creates an instance if none found
bool IsPortInUse(uint32_t port);
+ uint32_t GetFreePort();
void AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID);
void RemovePlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID);
diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp
index 6fbe34f7..cb968169 100644
--- a/dMasterServer/MasterServer.cpp
+++ b/dMasterServer/MasterServer.cpp
@@ -47,9 +47,12 @@ namespace Game {
bool shutdownSequenceStarted = false;
void ShutdownSequence();
dLogger* SetupLogger();
+void StartAuthServer();
+void StartChatServer();
void HandlePacket(Packet* packet);
std::map activeSessions;
bool shouldShutdown = false;
+SystemAddress chatServerMasterPeerSysAddr;
int main(int argc, char** argv) {
Diagnostics::SetProcessName("Master");
@@ -72,6 +75,7 @@ int main(int argc, char** argv) {
dConfig config("masterconfig.ini");
Game::config = &config;
Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console"))));
+ Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1");
//Connect to CDClient
try {
@@ -181,35 +185,12 @@ int main(int argc, char** argv) {
//Depending on the config, start up servers:
if (config.GetValue("prestart_servers") != "" && config.GetValue("prestart_servers") == "1") {
-#ifdef __APPLE__
- //macOS doesn't need sudo to run on ports < 1024
- system("./ChatServer&");
-#elif _WIN32
- system("start ./ChatServer.exe");
-#else
- if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
- system("sudo ./ChatServer&");
- }
- else {
- system("./ChatServer&");
- }
-#endif
+ StartChatServer();
Game::im->GetInstance(0, false, 0)->SetIsReady(true);
Game::im->GetInstance(1000, false, 0)->SetIsReady(true);
-#ifdef __APPLE__
- system("./AuthServer&");
-#elif _WIN32
- system("start ./AuthServer.exe");
-#else
- if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
- system("sudo ./AuthServer&");
- }
- else {
- system("./AuthServer&");
- }
-#endif
+ StartAuthServer();
}
auto t = std::chrono::high_resolution_clock::now();
@@ -320,11 +301,13 @@ dLogger* SetupLogger() {
std::string logPath =
"./logs/MasterServer_" + std::to_string(time(nullptr)) + ".log";
bool logToConsole = false;
+ bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
+ logDebugStatements = true;
#endif
- return new dLogger(logPath, logToConsole);
+ return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) {
@@ -338,6 +321,10 @@ void HandlePacket(Packet* packet) {
if (instance) {
Game::im->RemoveInstance(instance); //Delete the old
}
+
+ if (packet->systemAddress == chatServerMasterPeerSysAddr && !shouldShutdown) {
+ StartChatServer();
+ }
}
if (packet->data[0] == ID_CONNECTION_LOST) {
@@ -350,6 +337,10 @@ void HandlePacket(Packet* packet) {
Game::im->RemoveInstance(instance); //Delete the old
//Game::im->GetInstance(zoneID.GetMapID(), false, 0); //Create the new
}
+
+ if (packet->systemAddress == chatServerMasterPeerSysAddr && !shouldShutdown) {
+ StartChatServer();
+ }
}
if (packet->data[1] == MASTER) {
@@ -438,6 +429,14 @@ void HandlePacket(Packet* packet) {
}
}
+ if (theirServerType == ServerType::Chat) {
+ SystemAddress copy;
+ copy.binaryAddress = packet->systemAddress.binaryAddress;
+ copy.port = packet->systemAddress.port;
+
+ chatServerMasterPeerSysAddr = copy;
+ }
+
Game::logger->Log("MasterServer", "Received server info, instance: %i port: %i\n", theirInstanceID, theirPort);
break;
@@ -657,6 +656,37 @@ void HandlePacket(Packet* packet) {
}
}
+void StartChatServer() {
+#ifdef __APPLE__
+ //macOS doesn't need sudo to run on ports < 1024
+ system("./ChatServer&");
+#elif _WIN32
+ system("start ./ChatServer.exe");
+#else
+ if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
+ system("sudo ./ChatServer&");
+ }
+ else {
+ system("./ChatServer&");
+ }
+#endif
+}
+
+void StartAuthServer() {
+#ifdef __APPLE__
+ system("./AuthServer&");
+#elif _WIN32
+ system("start ./AuthServer.exe");
+#else
+ if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
+ system("sudo ./AuthServer&");
+ }
+ else {
+ system("./AuthServer&");
+ }
+#endif
+}
+
void ShutdownSequence() {
if (shutdownSequenceStarted) {
return;
diff --git a/dScripts/ActivityManager.cpp b/dScripts/ActivityManager.cpp
index eeddb192..f84963df 100644
--- a/dScripts/ActivityManager.cpp
+++ b/dScripts/ActivityManager.cpp
@@ -70,7 +70,7 @@ void ActivityManager::StopActivity(Entity *self, const LWOOBJID playerID, const
SetActivityValue(self, playerID, 1, value1);
SetActivityValue(self, playerID, 2, value2);
- Loot::GiveActivityLoot(player, self, gameID, CalculateActivityRating(self, playerID));
+ LootGenerator::Instance().GiveActivityLoot(player, self, gameID, CalculateActivityRating(self, playerID));
// Save the new score to the leaderboard and show the leaderboard to the player
LeaderboardManager::SaveScore(playerID, gameID, score, value1);
diff --git a/dScripts/AgPicnicBlanket.cpp b/dScripts/AgPicnicBlanket.cpp
index 96ae6b9b..3d13cb40 100644
--- a/dScripts/AgPicnicBlanket.cpp
+++ b/dScripts/AgPicnicBlanket.cpp
@@ -8,7 +8,7 @@ void AgPicnicBlanket::OnUse(Entity *self, Entity *user) {
self->SetVar(u"active", true);
auto lootTable = std::unordered_map {{935, 3}};
- Loot::DropLoot(user, self, lootTable, 0, 0);
+ LootGenerator::Instance().DropLoot(user, self, lootTable, 0, 0);
self->AddCallbackTimer(5.0f, [self]() {
self->SetVar(u"active", false);
diff --git a/dScripts/BaseInteractDropLootServer.cpp b/dScripts/BaseInteractDropLootServer.cpp
index 20cc2f1b..0dbce047 100644
--- a/dScripts/BaseInteractDropLootServer.cpp
+++ b/dScripts/BaseInteractDropLootServer.cpp
@@ -25,7 +25,7 @@ void BaseInteractDropLootServer::BaseUse(Entity* self, Entity* user)
self->SetNetworkVar(u"bInUse", true);
- Loot::DropLoot(user, self, lootMatrix, 0, 0);
+ LootGenerator::Instance().DropLoot(user, self, lootMatrix, 0, 0);
self->AddCallbackTimer(cooldownTime, [this, self] () {
self->SetNetworkVar(u"bInUse", false);
diff --git a/dScripts/BaseRandomServer.cpp b/dScripts/BaseRandomServer.cpp
index cf408d26..182544be 100644
--- a/dScripts/BaseRandomServer.cpp
+++ b/dScripts/BaseRandomServer.cpp
@@ -91,7 +91,7 @@ void BaseRandomServer::SetSpawnerNetwork(Entity* self, const std::string& spawne
if (spawnerName == "Named_Enemies")
{
- //spawner->Reset();
+ spawner->SoftReset();
}
spawner->Activate();
@@ -173,12 +173,12 @@ void BaseRandomServer::NamedEnemyDeath(Entity* self, Spawner* spawner)
void BaseRandomServer::SpawnersUp(Entity* self)
{
-
+
}
void BaseRandomServer::SpawnersDown(Entity* self)
{
-
+
}
void BaseRandomServer::BaseOnTimerDone(Entity* self, const std::string& timerName)
diff --git a/dScripts/BaseRandomServer.h b/dScripts/BaseRandomServer.h
index e67df365..d04414f1 100644
--- a/dScripts/BaseRandomServer.h
+++ b/dScripts/BaseRandomServer.h
@@ -28,7 +28,7 @@ public:
void SpawnersUp(Entity* self);
void SpawnersDown(Entity* self);
void BaseOnTimerDone(Entity* self, const std::string& timerName);
-
+
void NotifySpawnerOfDeath(Entity* self, Spawner* spawner);
void NamedEnemyDeath(Entity* self, Spawner* spawner);
diff --git a/dScripts/BootyDigServer.cpp b/dScripts/BootyDigServer.cpp
index 93e6449c..eeb3bfd4 100644
--- a/dScripts/BootyDigServer.cpp
+++ b/dScripts/BootyDigServer.cpp
@@ -41,7 +41,7 @@ BootyDigServer::OnFireEventServerSide(Entity *self, Entity *sender, std::string
if (renderComponent != nullptr)
renderComponent->PlayEffect(7730, u"cast", "bootyshine");
- Loot::DropLoot(player, self, 231, 75, 75);
+ LootGenerator::Instance().DropLoot(player, self, 231, 75, 75);
}
}
} else if (args == "ChestDead") {
diff --git a/dScripts/BuccaneerValiantShip.cpp b/dScripts/BuccaneerValiantShip.cpp
new file mode 100644
index 00000000..1cc1e633
--- /dev/null
+++ b/dScripts/BuccaneerValiantShip.cpp
@@ -0,0 +1,19 @@
+#include "BuccaneerValiantShip.h"
+#include "SkillComponent.h"
+#include "dLogger.h"
+
+void BuccaneerValiantShip::OnStartup(Entity* self) {
+ self->AddCallbackTimer(1.0F, [self]() {
+ auto* skillComponent = self->GetComponent();
+ auto* owner = self->GetOwner();
+
+ if (skillComponent != nullptr && owner != nullptr) {
+ skillComponent->CalculateBehavior(982, 20577, LWOOBJID_EMPTY, true, false, owner->GetObjectID());
+
+ // Kill self if missed
+ self->AddCallbackTimer(1.1F, [self]() {
+ self->Kill();
+ });
+ }
+ });
+}
diff --git a/dScripts/BuccaneerValiantShip.h b/dScripts/BuccaneerValiantShip.h
new file mode 100644
index 00000000..f501d1b9
--- /dev/null
+++ b/dScripts/BuccaneerValiantShip.h
@@ -0,0 +1,6 @@
+#pragma once
+#include "CppScripts.h"
+
+class BuccaneerValiantShip : public CppScripts::Script {
+ void OnStartup(Entity *self) override;
+};
diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp
index ed59c5ba..53cbf425 100644
--- a/dScripts/CppScripts.cpp
+++ b/dScripts/CppScripts.cpp
@@ -261,6 +261,8 @@
#include "PersonalFortress.h"
#include "PropertyDevice.h"
#include "ImaginationBackpackHealServer.h"
+#include "LegoDieRoll.h"
+#include "BuccaneerValiantShip.h"
// Survival scripts
#include "AgSurvivalStromling.h"
@@ -774,6 +776,10 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new PropertyDevice();
else if (scriptName == "scripts\\02_server\\Map\\General\\L_IMAG_BACKPACK_HEALS_SERVER.lua")
script = new ImaginationBackpackHealServer();
+ else if (scriptName == "scripts\\ai\\GENERAL\\L_LEGO_DIE_ROLL.lua")
+ script = new LegoDieRoll();
+ else if (scriptName == "scripts\\EquipmentScripts\\BuccaneerValiantShip.lua")
+ script = new BuccaneerValiantShip();
//Ignore these scripts:
else if (scriptName == "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua")
@@ -782,7 +788,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = invalidToReturn;
else if (script == invalidToReturn) {
if (scriptName.length() > 0)
- Game::logger->Log("CppScripts", "Attempted to load CppScript for '" + scriptName + "', but returned InvalidScript.\n");
+ Game::logger->LogDebug("CppScripts", "Attempted to load CppScript for '" + scriptName + "', but returned InvalidScript.\n");
+ // information not really needed for sys admins but is for developers
script = invalidToReturn;
}
diff --git a/dScripts/CppScripts.h b/dScripts/CppScripts.h
index 2f4ecd24..9cd014b5 100644
--- a/dScripts/CppScripts.h
+++ b/dScripts/CppScripts.h
@@ -6,6 +6,7 @@
#include "MissionState.h"
#include "Game.h"
#include "dLogger.h"
+#include "Loot.h"
class User;
class Entity;
diff --git a/dScripts/GrowingFlower.cpp b/dScripts/GrowingFlower.cpp
index e6ca587d..1acb2455 100644
--- a/dScripts/GrowingFlower.cpp
+++ b/dScripts/GrowingFlower.cpp
@@ -10,7 +10,7 @@ void GrowingFlower::OnSkillEventFired(Entity *self, Entity *target, const std::s
const auto mission1 = self->GetVar(u"missionID");
const auto mission2 = self->GetVar(u"missionID2");
- Loot::DropActivityLoot(target, self, self->GetLOT(), 0);
+ LootGenerator::Instance().DropActivityLoot(target, self, self->GetLOT(), 0);
auto* missionComponent = target->GetComponent();
if (missionComponent != nullptr) {
diff --git a/dScripts/LegoDieRoll.cpp b/dScripts/LegoDieRoll.cpp
new file mode 100644
index 00000000..4b375a38
--- /dev/null
+++ b/dScripts/LegoDieRoll.cpp
@@ -0,0 +1,55 @@
+#include "LegoDieRoll.h"
+#include "Entity.h"
+#include "dLogger.h"
+#include "GameMessages.h"
+
+void LegoDieRoll::OnStartup(Entity* self) {
+ self->AddTimer("DoneRolling", 10.0f);
+ self->AddTimer("ThrowDice", LegoDieRoll::animTime);
+}
+
+void LegoDieRoll::OnTimerDone(Entity* self, std::string timerName) {
+ if (timerName == "DoneRolling") {
+ self->Smash(self->GetObjectID(), SILENT);
+ }
+ else if (timerName == "ThrowDice") {
+ int dieRoll = GeneralUtils::GenerateRandomNumber(1, 6);
+
+ switch (dieRoll)
+ {
+ case 1:
+ GameMessages::SendPlayAnimation(self, u"roll-die-1");
+ break;
+ case 2:
+ GameMessages::SendPlayAnimation(self, u"roll-die-2");
+ break;
+ case 3:
+ GameMessages::SendPlayAnimation(self, u"roll-die-3");
+ break;
+ case 4:
+ GameMessages::SendPlayAnimation(self, u"roll-die-4");
+ break;
+ case 5:
+ GameMessages::SendPlayAnimation(self, u"roll-die-5");
+ break;
+ case 6:
+ {
+ GameMessages::SendPlayAnimation(self, u"roll-die-6");
+ // tracking the It's Truly Random Achievement
+ auto* owner = self->GetOwner();
+ auto* missionComponent = owner->GetComponent();
+
+ if (missionComponent != nullptr) {
+ const auto rollMissionState = missionComponent->GetMissionState(756);
+ if (rollMissionState == MissionState::MISSION_STATE_ACTIVE) {
+ missionComponent->ForceProgress(756, 1103, 1);
+ }
+ }
+ break;
+ }
+ default:
+ Game::logger->LogDebug("LegoDieRoll", "Invalid animation: roll-die-%i\n", dieRoll);
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dScripts/LegoDieRoll.h b/dScripts/LegoDieRoll.h
new file mode 100644
index 00000000..3b28d529
--- /dev/null
+++ b/dScripts/LegoDieRoll.h
@@ -0,0 +1,11 @@
+#pragma once
+#include "CppScripts.h"
+
+class LegoDieRoll : public CppScripts::Script {
+public:
+ void OnStartup(Entity* self);
+ void OnTimerDone(Entity* self, std::string timerName);
+private:
+ constexpr static const float animTime = 2.0f;
+};
+
diff --git a/dScripts/MinigameTreasureChestServer.cpp b/dScripts/MinigameTreasureChestServer.cpp
index 3380aaae..ff32b527 100644
--- a/dScripts/MinigameTreasureChestServer.cpp
+++ b/dScripts/MinigameTreasureChestServer.cpp
@@ -18,14 +18,24 @@ void MinigameTreasureChestServer::OnUse(Entity *self, Entity *user) {
UpdatePlayer(self, user->GetObjectID());
auto* team = TeamManager::Instance()->GetTeam(user->GetObjectID());
+ uint32_t activityRating = 0;
if (team != nullptr) {
for (const auto& teamMemberID : team->members) {
auto* teamMember = EntityManager::Instance()->GetEntity(teamMemberID);
- if (teamMember != nullptr)
- Loot::DropActivityLoot(teamMember, self, sac->GetActivityID(), CalculateActivityRating(self, teamMemberID));
+ if (teamMember != nullptr) {
+ activityRating = CalculateActivityRating(self, teamMemberID);
+
+ if (self->GetLOT() == frakjawChestId) activityRating = team->members.size();
+
+ LootGenerator::Instance().DropActivityLoot(teamMember, self, sac->GetActivityID(), activityRating);
+ }
}
} else {
- Loot::DropActivityLoot(user, self, sac->GetActivityID(), CalculateActivityRating(self, user->GetObjectID()));
+ activityRating = CalculateActivityRating(self, user->GetObjectID());
+
+ if (self->GetLOT() == frakjawChestId) activityRating = 1;
+
+ LootGenerator::Instance().DropActivityLoot(user, self, sac->GetActivityID(), activityRating);
}
sac->PlayerRemove(user->GetObjectID());
diff --git a/dScripts/MinigameTreasureChestServer.h b/dScripts/MinigameTreasureChestServer.h
index f2cd4328..c718f016 100644
--- a/dScripts/MinigameTreasureChestServer.h
+++ b/dScripts/MinigameTreasureChestServer.h
@@ -2,7 +2,10 @@
#include "ActivityManager.h"
class MinigameTreasureChestServer : public ActivityManager {
+public:
void OnStartup(Entity* self) override;
void OnUse(Entity* self, Entity* user) override;
uint32_t CalculateActivityRating(Entity *self, LWOOBJID playerID) override;
+private:
+ const uint32_t frakjawChestId = 16486;
};
diff --git a/dScripts/NjDragonEmblemChestServer.cpp b/dScripts/NjDragonEmblemChestServer.cpp
index 717e68b2..ea699c55 100644
--- a/dScripts/NjDragonEmblemChestServer.cpp
+++ b/dScripts/NjDragonEmblemChestServer.cpp
@@ -10,6 +10,6 @@ void NjDragonEmblemChestServer::OnUse(Entity *self, Entity *user) {
auto* destroyable = self->GetComponent();
if (destroyable != nullptr) {
- Loot::DropLoot(user, self, destroyable->GetLootMatrixID(), 0, 0);
+ LootGenerator::Instance().DropLoot(user, self, destroyable->GetLootMatrixID(), 0, 0);
}
}
diff --git a/dScripts/SGCannon.cpp b/dScripts/SGCannon.cpp
index 59373b5d..a691443e 100644
--- a/dScripts/SGCannon.cpp
+++ b/dScripts/SGCannon.cpp
@@ -423,7 +423,7 @@ void SGCannon::SpawnNewModel(Entity *self) {
if (lootMatrix != 0) {
std::unordered_map toDrop = {};
- Loot::CalculateLootMatrix(lootMatrix, player, toDrop);
+ toDrop = LootGenerator::Instance().RollLootMatrix(player, lootMatrix);
for (auto drop : toDrop) {
rewardModel->OnFireEventServerSide(self, ModelToBuildEvent, drop.first);
@@ -581,7 +581,7 @@ void SGCannon::StopGame(Entity *self, bool cancel) {
);
}
- Loot::GiveActivityLoot(player, self, GetGameID(self), self->GetVar(TotalScoreVariable));
+ LootGenerator::Instance().GiveActivityLoot(player, self, GetGameID(self), self->GetVar(TotalScoreVariable));
StopActivity(self, player->GetObjectID(), self->GetVar(TotalScoreVariable),
self->GetVar(MaxStreakVariable), percentage);
diff --git a/dScripts/ScriptedPowerupSpawner.cpp b/dScripts/ScriptedPowerupSpawner.cpp
index bd85eeef..5fc013c1 100644
--- a/dScripts/ScriptedPowerupSpawner.cpp
+++ b/dScripts/ScriptedPowerupSpawner.cpp
@@ -24,7 +24,7 @@ void ScriptedPowerupSpawner::OnTimerDone(Entity *self, std::string message) {
renderComponent->PlayEffect(0, u"cast", "N_cast");
}
- Loot::DropItem(owner, self, itemLOT, 0, 1, true, true);
+ LootGenerator::Instance().DropLoot(owner, self, itemLOT, 0, 1);
}
// Increment the current cycle
diff --git a/dScripts/TreasureChestDragonServer.cpp b/dScripts/TreasureChestDragonServer.cpp
index 582b634e..d0e8eaec 100644
--- a/dScripts/TreasureChestDragonServer.cpp
+++ b/dScripts/TreasureChestDragonServer.cpp
@@ -39,12 +39,12 @@ void TreasureChestDragonServer::OnUse(Entity* self, Entity* user)
if (memberObject == nullptr) continue;
- Loot::DropActivityLoot(memberObject, self, scriptedActivityComponent->GetActivityID(), rating);
+ LootGenerator::Instance().DropActivityLoot(memberObject, self, scriptedActivityComponent->GetActivityID(), rating);
}
}
else
{
- Loot::DropActivityLoot(user, self, scriptedActivityComponent->GetActivityID(), rating);
+ LootGenerator::Instance().DropActivityLoot(user, self, scriptedActivityComponent->GetActivityID(), rating);
}
self->Smash(self->GetObjectID());
diff --git a/dScripts/VeMissionConsole.cpp b/dScripts/VeMissionConsole.cpp
index 7519b9ec..4a506dac 100644
--- a/dScripts/VeMissionConsole.cpp
+++ b/dScripts/VeMissionConsole.cpp
@@ -4,7 +4,7 @@
#include "GameMessages.h"
void VeMissionConsole::OnUse(Entity *self, Entity *user) {
- Loot::DropActivityLoot(user, self, 12551);
+ LootGenerator::Instance().DropActivityLoot(user, self, 12551);
auto* inventoryComponent = user->GetComponent();
if (inventoryComponent != nullptr) {
diff --git a/dScripts/WishingWellServer.cpp b/dScripts/WishingWellServer.cpp
index 78181672..36bca973 100644
--- a/dScripts/WishingWellServer.cpp
+++ b/dScripts/WishingWellServer.cpp
@@ -24,7 +24,7 @@ void WishingWellServer::OnUse(Entity* self, Entity* user)
GameMessages::SendPlayNDAudioEmitter(self, user->GetSystemAddress(), audio);
}
- Loot::DropActivityLoot(
+ LootGenerator::Instance().DropActivityLoot(
user,
self,
static_cast(scriptedActivity->GetActivityID()),
diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp
index 3e9a8961..2a6cbaa5 100644
--- a/dWorldServer/WorldServer.cpp
+++ b/dWorldServer/WorldServer.cpp
@@ -73,6 +73,7 @@ namespace Game {
}
bool chatDisabled = false;
+bool chatConnected = false;
bool worldShutdownSequenceStarted = false;
bool worldShutdownSequenceComplete = false;
void WorldShutdownSequence();
@@ -89,6 +90,7 @@ struct tempSessionInfo {
std::map m_PendingUsers;
int instanceID = 0;
int g_CloneID = 0;
+std::string databaseChecksum = "";
int main(int argc, char** argv) {
Diagnostics::SetProcessName("World");
@@ -136,6 +138,7 @@ int main(int argc, char** argv) {
dConfig config("worldconfig.ini");
Game::config = &config;
Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console"))));
+ Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1");
if (config.GetValue("disable_chat") == "1") chatDisabled = true;
// Connect to CDClient
@@ -185,6 +188,7 @@ int main(int argc, char** argv) {
ObjectIDManager::Instance()->Initialize();
UserManager::Instance()->Initialize();
+ LootGenerator::Instance();
Game::chatFilter = new dChatFilter("./res/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf"))));
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, zoneID);
@@ -209,6 +213,7 @@ int main(int argc, char** argv) {
Packet* packet = nullptr;
int framesSinceLastFlush = 0;
int framesSinceMasterDisconnect = 0;
+ int framesSinceChatDisconnect = 0;
int framesSinceLastUsersSave = 0;
int framesSinceLastSQLPing = 0;
int framesSinceLastUser = 0;
@@ -234,6 +239,47 @@ int main(int argc, char** argv) {
Game::physicsWorld = &dpWorld::Instance(); //just in case some old code references it
dZoneManager::Instance()->Initialize(LWOZONEID(zoneID, instanceID, cloneID));
g_CloneID = cloneID;
+
+ // pre calculate the FDB checksum
+ if (Game::config->GetValue("check_fdb") == "1") {
+ std::ifstream fileStream;
+
+ static const std::vector aliases = {
+ "res/CDServers.fdb",
+ "res/cdserver.fdb",
+ "res/CDClient.fdb",
+ "res/cdclient.fdb",
+ };
+
+ for (const auto& file : aliases) {
+ fileStream.open(file, std::ios::binary | std::ios::in);
+ if (fileStream.is_open()) {
+ break;
+ }
+ }
+
+ const int bufferSize = 1024;
+ MD5* md5 = new MD5();
+
+ char fileStreamBuffer[1024] = {};
+
+ while (!fileStream.eof()) {
+ memset(fileStreamBuffer, 0, bufferSize);
+ fileStream.read(fileStreamBuffer, bufferSize);
+ md5->update(fileStreamBuffer, fileStream.gcount());
+ }
+
+ fileStream.close();
+
+ const char* nullTerminateBuffer = "\0";
+ md5->update(nullTerminateBuffer, 1); // null terminate the data
+ md5->finalize();
+ databaseChecksum = md5->hexdigest();
+
+ delete md5;
+
+ Game::logger->Log("WorldServer", "FDB Checksum calculated as: %s\n", databaseChecksum.c_str());
+ }
}
while (true) {
@@ -275,6 +321,19 @@ int main(int argc, char** argv) {
}
else framesSinceMasterDisconnect = 0;
+ // Check if we're still connected to chat:
+ if (!chatConnected) {
+ framesSinceChatDisconnect++;
+
+ // Attempt to reconnect every 30 seconds.
+ if (framesSinceChatDisconnect >= 2000) {
+ framesSinceChatDisconnect = 0;
+
+ Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
+ }
+ }
+ else framesSinceChatDisconnect = 0;
+
//In world we'd update our other systems here.
if (zoneID != 0 && deltaTime > 0.0f) {
@@ -503,21 +562,27 @@ int main(int argc, char** argv) {
dLogger * SetupLogger(int zoneID, int instanceID) {
std::string logPath = "./logs/WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID) + "_" + std::to_string(time(nullptr)) + ".log";
bool logToConsole = false;
+ bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
+ logDebugStatements = true;
#endif
- return new dLogger(logPath, logToConsole);
+ return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacketChat(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
Game::logger->Log("WorldServer", "Lost our connection to chat, zone(%i), instance(%i)\n", Game::server->GetZoneID(), Game::server->GetInstanceID());
+
+ chatConnected = false;
}
if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) {
Game::logger->Log("WorldServer", "Established connection to chat, zone(%i), instance (%i)\n",Game::server -> GetZoneID(), Game::server -> GetInstanceID());
Game::chatSysAddr = packet->systemAddress;
+
+ chatConnected = true;
}
if (packet->data[0] == ID_USER_PACKET_ENUM) {
@@ -838,7 +903,33 @@ void HandlePacket(Packet* packet) {
case MSG_WORLD_CLIENT_VALIDATION: {
std::string username = PacketUtils::ReadString(0x08, packet, true);
std::string sessionKey = PacketUtils::ReadString(74, packet, true);
+ std::string clientDatabaseChecksum = PacketUtils::ReadString(packet->length - 33, packet, false);
+ // sometimes client puts a null terminator at the end of the checksum and sometimes doesn't, weird
+ clientDatabaseChecksum = clientDatabaseChecksum.substr(0, 32);
+
+ // If the check is turned on, validate the client's database checksum.
+ if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) {
+ uint32_t gmLevel = 0;
+ auto* stmt = Database::CreatePreppedStmt("SELECT gm_level FROM accounts WHERE name=? LIMIT 1;");
+ stmt->setString(1, username.c_str());
+
+ auto* res = stmt->executeQuery();
+ while (res->next()) {
+ gmLevel = res->getInt(1);
+ }
+
+ delete stmt;
+ delete res;
+
+ // Developers may skip this check
+ if (gmLevel < 8 && clientDatabaseChecksum != databaseChecksum) {
+ Game::logger->Log("WorldServer", "Client's database checksum does not match the server's, aborting connection.\n");
+ Game::server->Disconnect(packet->systemAddress, SERVER_DISCON_KICK);
+ return;
+ }
+ }
+
//Request the session info from Master:
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_REQUEST_SESSION_KEY);
diff --git a/dZoneManager/Level.cpp b/dZoneManager/Level.cpp
index 0e9318ca..b678ed95 100644
--- a/dZoneManager/Level.cpp
+++ b/dZoneManager/Level.cpp
@@ -11,6 +11,8 @@
#include "GeneralUtils.h"
#include "Entity.h"
#include "EntityManager.h"
+#include "CDFeatureGatingTable.h"
+#include "CDClientManager.h"
Level::Level(Zone* parentZone, const std::string& filepath) {
m_ParentZone = parentZone;
diff --git a/resources/authconfig.ini b/resources/authconfig.ini
index fd1fc5c4..40ca146e 100644
--- a/resources/authconfig.ini
+++ b/resources/authconfig.ini
@@ -19,6 +19,9 @@ max_clients=999
# 0 or 1, should log to console
log_to_console=1
+# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation
+log_debug_statements=0
+
# 0 or 1, should ignore playkeys
# If 1 everyone with an account will be able to login, regardless of if they have a key or not
dont_use_keys=0
diff --git a/resources/chatconfig.ini b/resources/chatconfig.ini
index 28db5988..f30fb8f9 100644
--- a/resources/chatconfig.ini
+++ b/resources/chatconfig.ini
@@ -19,5 +19,8 @@ max_clients=999
# 0 or 1, should log to console
log_to_console=1
+# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation
+log_debug_statements=0
+
# 0 or 1, should not compile chat hash map to file
dont_generate_dcf=0
diff --git a/resources/masterconfig.ini b/resources/masterconfig.ini
index 120a3743..cedb7c3a 100644
--- a/resources/masterconfig.ini
+++ b/resources/masterconfig.ini
@@ -32,5 +32,8 @@ max_clients=999
# 0 or 1, should log to console
log_to_console=1
+# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation
+log_debug_statements=0
+
# 0 or 1, should autostart auth, chat, and char servers
prestart_servers=1
diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini
index e5932ec7..d8d8ef87 100644
--- a/resources/worldconfig.ini
+++ b/resources/worldconfig.ini
@@ -20,6 +20,9 @@ max_clients=999
# 0 or 1, should log to console
log_to_console=1
+# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation
+log_debug_statements=0
+
# 0 or 1, should not compile chat hash map to file
dont_generate_dcf=0
@@ -47,3 +50,6 @@ solo_racing=0
# Disables the anti-speedhack system. If you get kicked randomly you might want to disable this, as it might just be lag
disable_anti_speedhack=0
+
+# 0 or 1, check server fdb (res/CDServer.fdb) against clients
+check_fdb=0
\ No newline at end of file
|