Compare commits

...

9 Commits

Author SHA1 Message Date
David Markowitz
a0913ff23d remove grandmas code 2025-01-14 02:18:46 -08:00
David Markowitz
b738504812 reduce traffic greatly 2025-01-14 02:09:54 -08:00
David Markowitz
c968dc9028 fix lego club teleport (#1731)
Tested that the lego club teleport and starbase 3001 teleports work now both before and after you visit nexus tower
2025-01-14 01:28:24 -06:00
David Markowitz
2209a4432f Update AgSpiderBossMessage.cpp (#1729) 2025-01-14 01:14:24 -06:00
David Markowitz
c855a6b9cf Remove added physics objects (#1727) 2025-01-12 14:03:35 -06:00
David Markowitz
8abc545bd1 fix: use generated bcrypt password for internal master connections (#1720)
* add password hashing for master server

* use define
2025-01-10 01:45:20 -08:00
David Markowitz
136133dde2 fix: friends (#1726)
* fix

* Update ChatPacketHandler.cpp
2025-01-08 22:44:55 -06:00
David Markowitz
23551d4ed8 fix: friends not updating and using incorrect world (#1724)
* fix: friends not updating and using incorrect world

* use better reset logic

* actual fix for real
2025-01-06 20:25:51 -06:00
David Markowitz
7599a2e81e i am surprised no one noticed this for 3 yeasr (#1721) 2025-01-06 20:19:27 -06:00
29 changed files with 233 additions and 164 deletions

View File

@@ -70,12 +70,15 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1500;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
masterPassword = masterInfo->password;
}
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
Game::randomEngine = std::mt19937(time(0));
@@ -89,7 +92,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal, masterPassword);
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();

View File

@@ -29,35 +29,33 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
if (!player) return;
if (player.friends.empty()) {
auto friendsList = Database::Get()->GetFriendsList(playerID);
for (const auto& friendData : friendsList) {
FriendData fd;
fd.isFTP = false; // not a thing in DLU
fd.friendID = friendData.friendID;
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
auto friendsList = Database::Get()->GetFriendsList(playerID);
for (const auto& friendData : friendsList) {
FriendData fd;
fd.isFTP = false; // not a thing in DLU
fd.friendID = friendData.friendID;
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
if (fd.isBestFriend) player.countOfBestFriends += 1;
fd.friendName = friendData.friendName;
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
if (fd.isBestFriend) player.countOfBestFriends += 1;
fd.friendName = friendData.friendName;
//Now check if they're online:
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
//Now check if they're online:
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
if (fr) {
fd.isOnline = true;
fd.zoneID = fr.zoneID;
if (fr) {
fd.isOnline = true;
fd.zoneID = fr.zoneID;
//Since this friend is online, we need to update them on the fact that we've just logged in:
SendFriendUpdate(fr, player, 1, fd.isBestFriend);
} else {
fd.isOnline = false;
fd.zoneID = LWOZONEID();
}
player.friends.push_back(fd);
//Since this friend is online, we need to update them on the fact that we've just logged in:
if (player.isLogin) SendFriendUpdate(fr, player, 1, fd.isBestFriend);
} else {
fd.isOnline = false;
fd.zoneID = LWOZONEID();
}
player.friends.push_back(fd);
}
//Now, we need to send the friendlist to the server they came from:
@@ -105,7 +103,8 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
return;
};
auto& requestee = Game::playerContainer.GetPlayerDataMutable(playerName);
// Intentional copy
PlayerData requestee = Game::playerContainer.GetPlayerData(playerName);
// Check if player is online first
if (isBestFriendRequest && !requestee) {
@@ -190,19 +189,24 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus);
// Sent the best friend update here if the value is 3
if (bestFriendStatus == 3U) {
requestee.countOfBestFriends += 1;
requestor.countOfBestFriends += 1;
if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true);
if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true);
for (auto& friendData : requestor.friends) {
if (friendData.friendID == requestee.playerID) {
friendData.isBestFriend = true;
}
}
for (auto& friendData : requestee.friends) {
if (friendData.friendID == requestor.playerID) {
friendData.isBestFriend = true;
requestor.countOfBestFriends += 1;
auto& toModify = Game::playerContainer.GetPlayerDataMutable(playerName);
if (toModify) {
for (auto& friendData : toModify.friends) {
if (friendData.friendID == requestor.playerID) {
friendData.isBestFriend = true;
}
}
toModify.countOfBestFriends += 1;
}
}
}

View File

@@ -92,10 +92,12 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
masterPassword = masterInfo->password;
}
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
std::string ourIP = "localhost";
@@ -104,7 +106,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal, masterPassword);
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);

View File

@@ -32,7 +32,10 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
return;
}
auto isLogin = !m_Players.contains(playerId);
auto& data = m_Players[playerId];
data = PlayerData();
data.isLogin = isLogin;
data.playerID = playerId;
uint32_t len;

View File

@@ -46,6 +46,7 @@ struct PlayerData {
std::vector<IgnoreData> ignoredPlayers;
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
bool isFTP = false;
bool isLogin = false;
};
struct TeamData {

View File

@@ -5,6 +5,7 @@
#include "Logger.h"
#include "Game.h"
#include <type_traits>
#include <unordered_map>
#include <vector>
@@ -257,10 +258,10 @@ public:
*
* @param key The key to remove from the associative portion
*/
void Remove(const std::string& key, const bool deleteValue = true) {
void Remove(const std::string& key) {
const AMFAssociative::const_iterator it = m_Associative.find(key);
if (it != m_Associative.cend()) {
if (deleteValue) m_Associative.erase(it);
m_Associative.erase(it);
}
}
@@ -343,6 +344,11 @@ public:
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
}
void Reset() {
m_Associative.clear();
m_Dense.clear();
}
private:
/**
* The associative portion. These values are key'd with strings to an AMFValue.

View File

@@ -9,10 +9,11 @@ public:
struct MasterInfo {
std::string ip;
uint32_t port{};
std::string password{};
};
// Set the master server ip and port.
virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0;
virtual void SetMasterInfo(const MasterInfo& info) = 0;
// Get the master server info.
virtual std::optional<MasterInfo> GetMasterInfo() = 0;

View File

@@ -96,7 +96,7 @@ public:
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;

View File

@@ -1,14 +1,14 @@
#include "MySQLDatabase.h"
void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
void MySQLDatabase::SetMasterInfo(const MasterInfo& info) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways.
ExecuteDelete("TRUNCATE TABLE servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022, ?)", info.ip, info.port, info.password);
}
std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
auto result = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
if (!result->next()) {
return std::nullopt;
@@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> MySQLDatabase::GetMasterInfo() {
toReturn.ip = result->getString("ip").c_str();
toReturn.port = result->getInt("port");
toReturn.password = result->getString("master_password").c_str();
return toReturn;
}

View File

@@ -94,7 +94,7 @@ public:
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;

View File

@@ -1,14 +1,14 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
void SQLiteDatabase::SetMasterInfo(const MasterInfo& info) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways.
ExecuteDelete("DELETE FROM servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`, `master_password`) VALUES ('master', ?, ?, 0, 171022 ?)", info.ip, info.port, info.password);
}
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
auto [_, result] = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
auto [_, result] = ExecuteSelect("SELECT ip, port, master_password FROM servers WHERE name='master' LIMIT 1;");
if (result.eof()) {
return std::nullopt;
@@ -18,6 +18,7 @@ std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
toReturn.ip = result.getStringField("ip");
toReturn.port = result.getIntField("port");
toReturn.password = result.getStringField("master_password");
return toReturn;
}

View File

@@ -236,7 +236,7 @@ void TestSQLDatabase::InsertNewAccount(const std::string_view username, const st
}
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
}

View File

@@ -73,7 +73,7 @@ class TestSQLDatabase : public GameDatabase {
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;

View File

@@ -386,6 +386,9 @@ void Entity::Initialize() {
if (m_Character) {
comp->LoadFromXml(m_Character->GetXMLDoc());
} else {
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
if (componentID > 0) {
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
@@ -420,9 +423,6 @@ void Entity::Initialize() {
comp->SetMinCoins(currencyValues[0].minvalue);
comp->SetMaxCoins(currencyValues[0].maxvalue);
}
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
}
} else {
comp->SetHealth(1);
@@ -1565,7 +1565,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
m_DieCallbacks.clear();
//OMAI WA MOU, SHINDERIU
//お前はもう死んでいる
GetScript()->OnDie(this, murderer);
@@ -2208,7 +2208,7 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
int32_t Entity::GetCollisionGroup() const {
for (const auto* component : m_Components | std::views::values) {
auto* compToCheck = dynamic_cast<const PhysicsComponent*>(component);
auto* compToCheck = dynamic_cast<const PhysicsComponent*>(component);
if (compToCheck) {
return compToCheck->GetCollisionGroup();
}

View File

@@ -84,11 +84,12 @@ dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
// Leaving these out for now since they cause more issues than they solve in racing tracks without proper OBB checks.
} /* else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f);
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 48.386536f, 50.363434f, 259.361755f);
} else {
} */ else {
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
//add fallback cube:

View File

@@ -65,14 +65,7 @@ void QuickBuildComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIni
outBitStream.Write(false);
}
// If build state is completed and we've already serialized once in the completed state,
// don't serializing this component anymore as this will cause the build to jump again.
// If state changes, serialization will begin again.
if (!m_StateDirty && m_State == eQuickBuildState::COMPLETED) {
outBitStream.Write0();
outBitStream.Write0();
return;
}
// BEGIN Scripted Activity
outBitStream.Write1();
@@ -90,36 +83,27 @@ void QuickBuildComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIni
}
// END Scripted Activity
outBitStream.Write1();
outBitStream.Write(m_StateDirty || bIsInitialUpdate);
if (m_StateDirty || bIsInitialUpdate) {
outBitStream.Write(m_State);
outBitStream.Write(m_State);
outBitStream.Write(m_ShowResetEffect);
outBitStream.Write(m_Activator != nullptr);
outBitStream.Write(m_ShowResetEffect);
outBitStream.Write(m_Activator != nullptr);
outBitStream.Write(m_Timer);
outBitStream.Write(m_TimerIncomplete);
outBitStream.Write(m_Timer);
outBitStream.Write(m_TimerIncomplete);
if (bIsInitialUpdate) {
outBitStream.Write(false);
outBitStream.Write(m_ActivatorPosition);
outBitStream.Write(m_RepositionPlayer);
if (bIsInitialUpdate) {
outBitStream.Write(false); // IsChoiceBuild
outBitStream.Write(m_ActivatorPosition);
outBitStream.Write(m_RepositionPlayer);
}
m_StateDirty = false;
}
m_StateDirty = false;
}
void QuickBuildComponent::Update(float deltaTime) {
m_Activator = GetActivator();
// Serialize the quickbuild every so often, fixes the odd bug where the quickbuild is not buildable
/*if (m_SoftTimer > 0.0f) {
m_SoftTimer -= deltaTime;
}
else {
m_SoftTimer = 5.0f;
Game::entityManager->SerializeEntity(m_Parent);
}*/
SetActivator(GetActivator());
switch (m_State) {
case eQuickBuildState::OPEN: {
@@ -130,12 +114,12 @@ void QuickBuildComponent::Update(float deltaTime) {
const bool isSmashGroup = spawner != nullptr ? spawner->GetIsSpawnSmashGroup() : false;
if (isSmashGroup) {
m_TimerIncomplete += deltaTime;
ModifyIncompleteTimer(deltaTime);
// For reset times < 0 this has to be handled manually
if (m_TimeBeforeSmash > 0) {
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f) {
m_ShowResetEffect = true;
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f && !m_ShowResetEffect) {
SetShowResetEffect(true);
Game::entityManager->SerializeEntity(m_Parent);
}
@@ -153,21 +137,19 @@ void QuickBuildComponent::Update(float deltaTime) {
break;
}
case eQuickBuildState::COMPLETED: {
m_Timer += deltaTime;
ModifyTimer(deltaTime);
// For reset times < 0 this has to be handled manually
if (m_ResetTime > 0) {
if (m_Timer >= m_ResetTime - 4.0f) {
if (!m_ShowResetEffect) {
m_ShowResetEffect = true;
if (m_Timer >= m_ResetTime - 4.0f && !m_ShowResetEffect) {
SetShowResetEffect(true);
Game::entityManager->SerializeEntity(m_Parent);
}
Game::entityManager->SerializeEntity(m_Parent);
}
if (m_Timer >= m_ResetTime) {
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true);
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 7.0f, false, true);
ResetQuickBuild(false);
}
@@ -185,9 +167,9 @@ void QuickBuildComponent::Update(float deltaTime) {
}
m_TimeBeforeDrain -= deltaTime;
m_Timer += deltaTime;
m_TimerIncomplete = 0;
m_ShowResetEffect = false;
ModifyTimer(deltaTime);
SetIncompleteTimer(0.0f);
SetShowResetEffect(false);
if (m_TimeBeforeDrain <= 0.0f) {
m_TimeBeforeDrain = m_CompleteTime / static_cast<float>(m_TakeImagination);
@@ -215,12 +197,12 @@ void QuickBuildComponent::Update(float deltaTime) {
break;
}
case eQuickBuildState::INCOMPLETE: {
m_TimerIncomplete += deltaTime;
ModifyIncompleteTimer(deltaTime);
// For reset times < 0 this has to be handled manually
if (m_TimeBeforeSmash > 0) {
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f) {
m_ShowResetEffect = true;
if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f && !m_ShowResetEffect) {
SetShowResetEffect(true);
Game::entityManager->SerializeEntity(m_Parent);
}
@@ -260,7 +242,7 @@ void QuickBuildComponent::SpawnActivator() {
info.spawnerID = m_Parent->GetObjectID();
info.pos = m_ActivatorPosition == NiPoint3Constant::ZERO ? m_Parent->GetPosition() : m_ActivatorPosition;
m_Activator = Game::entityManager->CreateEntity(info, nullptr, m_Parent);
SetActivator(Game::entityManager->CreateEntity(info, nullptr, m_Parent));
if (m_Activator) {
m_ActivatorId = m_Activator->GetObjectID();
Game::entityManager->ConstructEntity(m_Activator);
@@ -277,7 +259,7 @@ void QuickBuildComponent::DespawnActivator() {
m_Activator->ScheduleKillAfterUpdate();
m_Activator = nullptr;
SetActivator(nullptr);
m_ActivatorId = LWOOBJID_EMPTY;
}
@@ -405,8 +387,7 @@ void QuickBuildComponent::StartQuickBuild(Entity* const user) {
GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::BUILDING, user->GetObjectID());
GameMessages::SendEnableQuickBuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID());
m_State = eQuickBuildState::BUILDING;
m_StateDirty = true;
SetState(eQuickBuildState::BUILDING);
Game::entityManager->SerializeEntity(m_Parent);
auto* movingPlatform = m_Parent->GetComponent<MovingPlatformComponent>();
@@ -444,9 +425,8 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) {
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
m_State = eQuickBuildState::COMPLETED;
m_StateDirty = true;
m_Timer = 0.0f;
SetState(eQuickBuildState::COMPLETED);
SetTimer(0.0f);
m_DrainedImagination = 0;
Game::entityManager->SerializeEntity(m_Parent);
@@ -526,11 +506,10 @@ void QuickBuildComponent::ResetQuickBuild(const bool failed) {
GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::RESETTING, LWOOBJID_EMPTY);
m_State = eQuickBuildState::RESETTING;
m_StateDirty = true;
m_Timer = 0.0f;
m_TimerIncomplete = 0.0f;
m_ShowResetEffect = false;
SetState(eQuickBuildState::RESETTING);
SetTimer(0.0f);
SetIncompleteTimer(0.0f);
SetShowResetEffect(false);
m_DrainedImagination = 0;
Game::entityManager->SerializeEntity(m_Parent);
@@ -563,8 +542,7 @@ void QuickBuildComponent::CancelQuickBuild(Entity* const entity, const eQuickBui
GameMessages::SendTerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
// Now update the component itself
m_State = eQuickBuildState::INCOMPLETE;
m_StateDirty = true;
SetState(eQuickBuildState::INCOMPLETE);
// Notify scripts and possible subscribers
m_Parent->GetScript()->OnQuickBuildNotifyState(m_Parent, m_State);

View File

@@ -218,6 +218,48 @@ public:
* @param skipChecks whether or not to skip the check for the quickbuild not being completed
*/
void CancelQuickBuild(Entity* const builder, const eQuickBuildFailReason failReason, const bool skipChecks = false);
void SetState(const eQuickBuildState state) {
if (m_State == state) return;
m_State = state;
m_StateDirty = true;
}
void SetShowResetEffect(const bool value) {
if (m_ShowResetEffect == value) return;
m_ShowResetEffect = value;
m_StateDirty = true;
}
void SetActivator(Entity* const activator) {
if (m_Activator == activator) return;
m_Activator = activator;
m_StateDirty = true;
}
void SetTimer(const float value) {
if (m_Timer == value) return;
m_Timer = value;
m_StateDirty = true;
}
void ModifyTimer(const float value) {
if (value == 0.0f) return;
m_Timer += value;
m_StateDirty = true;
}
void SetIncompleteTimer(const float value) {
if (m_TimerIncomplete == value) return;
m_TimerIncomplete = value;
m_StateDirty = true;
}
void ModifyIncompleteTimer(const float value) {
if (value == 0.0f) return;
m_TimerIncomplete += value;
m_StateDirty = true;
}
private:
/**
* Whether or not the quickbuild state has been changed since we last serialized it.

View File

@@ -843,8 +843,10 @@ void GameMessages::SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, c
bitStream.Write(entity->GetObjectID());
bitStream.Write(MessageType::Game::DIE);
bitStream.Write(bClientDeath);
bitStream.Write(bSpawnLoot);
bitStream.Write<uint32_t>(deathType.size());
bitStream.Write(deathType);
bitStream.Write(directionRelative_AngleXZ);
bitStream.Write(directionRelative_AngleY);
@@ -854,7 +856,10 @@ void GameMessages::SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, c
if (killType != eKillType::VIOLENT) bitStream.Write(killType);
bitStream.Write(killerID);
bitStream.Write(lootOwnerID);
bitStream.Write(lootOwnerID != LWOOBJID_EMPTY);
if (lootOwnerID != LWOOBJID_EMPTY) {
bitStream.Write(lootOwnerID);
}
SEND_PACKET_BROADCAST;
}

View File

@@ -62,6 +62,14 @@ std::map<uint32_t, std::string> activeSessions;
SystemAddress authServerMasterPeerSysAddr;
SystemAddress chatServerMasterPeerSysAddr;
int GenerateBCryptPassword(const std::string& password, const int workFactor, char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE]) {
int32_t bcryptState = ::bcrypt_gensalt(workFactor, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
return 0;
}
int main(int argc, char** argv) {
constexpr uint32_t masterFramerate = mediumFramerate;
constexpr uint32_t masterFrameDelta = mediumFrameDelta;
@@ -94,7 +102,7 @@ int main(int argc, char** argv) {
std::string(folder) +
") folder from your download to the binary directory or re-run cmake.";
LOG("%s", msg.c_str());
// toss an error box up for windows users running the download
// toss an error box up for windows users running the download
#ifdef DARKFLAME_PLATFORM_WIN32
MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR);
#endif
@@ -238,10 +246,7 @@ int main(int argc, char** argv) {
// Regenerate hash based on new password
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
assert(GenerateBCryptPassword(password, 12, salt, hash) == 0);
Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE));
@@ -279,10 +284,7 @@ int main(int argc, char** argv) {
//Generate new hash for bcrypt
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
assert(GenerateBCryptPassword(password, 12, salt, hash) == 0);
//Create account
try {
@@ -318,15 +320,24 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal);
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
const auto& cfgPassword = Game::config->GetValue("master_password");
GenerateBCryptPassword(!cfgPassword.empty() ? cfgPassword : "3.25DARKFLAME1", 13, salt, hash);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal, hash);
std::string master_server_ip = "localhost";
const auto masterServerIPString = Game::config->GetValue("master_ip");
if (!masterServerIPString.empty()) master_server_ip = masterServerIPString;
if (master_server_ip == "") master_server_ip = Game::server->GetIP();
IServers::MasterInfo info;
info.ip = master_server_ip;
info.port = Game::server->GetPort();
info.password = hash;
Database::Get()->SetMasterIp(master_server_ip, Game::server->GetPort());
Database::Get()->SetMasterInfo(info);
//Create additional objects here:
PersistentIDManager::Initialize();

View File

@@ -3,4 +3,3 @@
#include "RakPeer.h"
#define NET_PASSWORD_EXTERNAL "3.25 ND1"
#define NET_PASSWORD_INTERNAL "3.25 DARKFLAME1"

View File

@@ -40,7 +40,21 @@ public:
}
} ReceiveDownloadCompleteCB;
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) {
dServer::dServer(
const std::string& ip,
int port,
int instanceID,
int maxConnections,
bool isInternal,
bool useEncryption,
Logger* logger,
const std::string masterIP,
int masterPort,
ServerType serverType,
dConfig* config,
Game::signal_t* lastSignal,
const std::string& masterPassword,
unsigned int zoneID) {
mIP = ip;
mPort = port;
mZoneID = zoneID;
@@ -56,6 +70,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
mReplicaManager = nullptr;
mServerType = serverType;
mConfig = config;
mMasterPassword = masterPassword;
mShouldShutdown = lastSignal;
//Attempt to start our server here:
mIsOkay = Startup();
@@ -203,11 +218,11 @@ bool dServer::Startup() {
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
if (mIsInternal) {
mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15);
mPeer->SetIncomingPassword(mMasterPassword.c_str(), mMasterPassword.size());
} else {
UpdateBandwidthLimit();
UpdateMaximumMtuSize();
mPeer->SetIncomingPassword("3.25 ND1", 8);
mPeer->SetIncomingPassword(NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
}
mPeer->SetMaximumIncomingConnections(mMaxConnections);
@@ -257,7 +272,7 @@ void dServer::SetupForMasterConnection() {
bool dServer::ConnectToMaster() {
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, mMasterPassword.c_str(), mMasterPassword.size());
}
void dServer::UpdateReplica() {

View File

@@ -46,6 +46,7 @@ public:
ServerType serverType,
dConfig* config,
Game::signal_t* shouldShutdown,
const std::string& masterPassword,
unsigned int zoneID = 0);
~dServer();
@@ -121,4 +122,5 @@ protected:
std::string mMasterIP;
int mMasterPort;
std::chrono::steady_clock::time_point mStartTime = std::chrono::steady_clock::now();
std::string mMasterPassword;
};

View File

@@ -10,49 +10,38 @@ void NsLegoClubDoor::OnStartup(Entity* self) {
self->SetVar(u"teleportString", m_TeleportString);
self->SetVar(u"spawnPoint", m_SpawnPoint);
args = {};
teleportArgs.Reset();
args.Insert("callbackClient", std::to_string(self->GetObjectID()));
args.Insert("strIdentifier", "choiceDoor");
args.Insert("title", "%[UI_CHOICE_DESTINATION]");
teleportArgs.Insert("callbackClient", std::to_string(self->GetObjectID()));
teleportArgs.Insert("strIdentifier", "choiceDoor");
teleportArgs.Insert("title", "%[UI_CHOICE_DESTINATION]");
AMFArrayValue* choiceOptions = args.InsertArray("options");
auto& choiceOptions = *teleportArgs.InsertArray("options");
{
AMFArrayValue* nsArgs = choiceOptions->PushArray();
auto& nsArgs = *choiceOptions.PushArray();
nsArgs->Insert("image", "textures/ui/zone_thumnails/Nimbus_Station.dds");
nsArgs->Insert("caption", "%[UI_CHOICE_NS]");
nsArgs->Insert("identifier", "zoneID_1200");
nsArgs->Insert("tooltipText", "%[UI_CHOICE_NS_HOVER]");
nsArgs.Insert("image", "textures/ui/zone_thumnails/Nimbus_Station.dds");
nsArgs.Insert("caption", "%[UI_CHOICE_NS]");
nsArgs.Insert("identifier", "zoneID_1200");
nsArgs.Insert("tooltipText", "%[UI_CHOICE_NS_HOVER]");
}
{
AMFArrayValue* ntArgs = choiceOptions->PushArray();
auto& ntArgs = *choiceOptions.PushArray();
ntArgs->Insert("image", "textures/ui/zone_thumnails/Nexus_Tower.dds");
ntArgs->Insert("caption", "%[UI_CHOICE_NT]");
ntArgs->Insert("identifier", "zoneID_1900");
ntArgs->Insert("tooltipText", "%[UI_CHOICE_NT_HOVER]");
ntArgs.Insert("image", "textures/ui/zone_thumnails/Nexus_Tower.dds");
ntArgs.Insert("caption", "%[UI_CHOICE_NT]");
ntArgs.Insert("identifier", "zoneID_1900");
ntArgs.Insert("tooltipText", "%[UI_CHOICE_NT_HOVER]");
}
options = choiceOptions;
}
void NsLegoClubDoor::OnUse(Entity* self, Entity* user) {
auto* player = user;
if (CheckChoice(self, player)) {
AMFArrayValue multiArgs;
multiArgs.Insert("callbackClient", std::to_string(self->GetObjectID()));
multiArgs.Insert("strIdentifier", "choiceDoor");
multiArgs.Insert("title", "%[UI_CHOICE_DESTINATION]");
multiArgs.Insert("options", static_cast<AMFBaseValue*>(options));
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", multiArgs);
multiArgs.Remove("options", false); // We do not want the local amf to delete the options!
GameMessages::SendUIMessageServerToSingleClient(player, player->GetSystemAddress(), "QueueChoiceBox", teleportArgs);
} else if (self->GetVar<int32_t>(u"currentZone") != m_ChoiceZoneID) {
AMFArrayValue multiArgs;
multiArgs.Insert("state", "Lobby");

View File

@@ -19,6 +19,5 @@ private:
std::string m_SpawnPoint = "NS_LEGO_Club";
std::u16string m_TeleportAnim = u"lup-teleport";
std::u16string m_TeleportString = u"ROCKET_TOOLTIP_USE_THE_GATEWAY_TO_TRAVEL_TO_LUP_WORLD";
AMFArrayValue args = {};
AMFArrayValue* options = {};
AMFArrayValue teleportArgs{};
};

View File

@@ -36,14 +36,14 @@ void AgSpiderBossMessage::OnCollisionPhantom(Entity* self, Entity* target) {
auto box = GetBox(self);
// knockback the target
auto forward = target->GetRotation().GetForwardVector();
auto forward = self->GetRotation().GetForwardVector();
box.boxTarget = target->GetObjectID();
GameMessages::SendPlayFXEffect(target->GetObjectID(), 1378, u"create", "pushBack");
RenderComponent::PlayAnimation(target, "knockback-recovery");
forward.y += 15;
forward.x *= 100;
forward.z *= 100;
GameMessages::SendKnockback(target->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, forward);
GameMessages::SendKnockback(target->GetObjectID(), LWOOBJID_EMPTY, LWOOBJID_EMPTY, 0, forward);
if (box.isTouch || box.isDisplayed) return;
box.boxSelf = self->GetObjectID();

View File

@@ -202,11 +202,13 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP = "localhost";
uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
masterPassword = masterInfo->password;
}
UserManager::Instance()->Initialize();
@@ -214,7 +216,7 @@ int main(int argc, char** argv) {
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, zoneID);
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, masterPassword, zoneID);
//Connect to the chat server:
uint32_t chatPort = 1501;
@@ -223,7 +225,7 @@ int main(int argc, char** argv) {
auto chatSock = SocketDescriptor(static_cast<uint16_t>(ourPort + 2), 0);
Game::chatServer = RakNetworkFactory::GetRakPeerInterface();
Game::chatServer->Startup(1, 30, &chatSock, 1);
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
//Set up other things:
Game::randomEngine = std::mt19937(time(0));
@@ -371,7 +373,7 @@ int main(int argc, char** argv) {
if (framesSinceChatDisconnect >= chatReconnectionTime) {
framesSinceChatDisconnect = 0;
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
Game::chatServer->Connect(masterIP.c_str(), chatPort, NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL)));
}
} else framesSinceChatDisconnect = 0;

View File

@@ -0,0 +1 @@
ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1');

View File

@@ -0,0 +1 @@
ALTER TABLE servers ADD COLUMN master_password TEXT NOT NULL DEFAULT ('3.25 DARKFLAME1');

View File

@@ -9,3 +9,5 @@ world_port_start=3000
# 0 or 1, should autostart auth, chat, and char servers
prestart_servers=1
master_password=3.25DARKFLAME1