Compare commits

..

1 Commits

Author SHA1 Message Date
d77a1a92bf WIP 2025-01-07 09:13:24 -06:00
37 changed files with 771 additions and 358 deletions

View File

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

View File

@@ -4,6 +4,7 @@ set(DCHATSERVER_SOURCES
"PlayerContainer.cpp" "PlayerContainer.cpp"
"ChatWebAPI.cpp" "ChatWebAPI.cpp"
"JSONUtils.cpp" "JSONUtils.cpp"
"CSRCommandHandler.cpp"
) )
add_executable(ChatServer "ChatServer.cpp") add_executable(ChatServer "ChatServer.cpp")

View File

@@ -0,0 +1,353 @@
#include "CSRCommandHandler.h"
#include "eCSRCommand.h"
#include "eHTTPStatusCode.h"
#include "JSONUtils.h"
#include "magic_enum.hpp"
#include "json.hpp"
void CSRCommandHandler::HandleCSRCommand(HTTPReply& reply, const json& data) {
auto check = JSONUtils::CheckRequiredData(data, { "command" });
if (!check.empty()) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = check;
} else {
// get command from json
std::string command_string = data["command"];
// upper
std::transform(command_string.begin(), command_string.end(), command_string.begin(), ::toupper);
eCSRCommand command = magic_enum::enum_cast<eCSRCommand>(command_string).value_or(eCSRCommand::INVALID);
switch (command) {
case eCSRCommand::QUERY_SERVER_STATUS:
QueryServerStatus(reply, data);
break;
case eCSRCommand::QUERY_CHARACTER_LOCATION:
QueryCharacterLocation(reply, data);
break;
case eCSRCommand::QUERY_CHARACTER_ONLINE_STATUS:
QueryCharacterOnlineStatus(reply, data);
break;
case eCSRCommand::INVENTORY_ADD_ITEM:
InventoryAddItem(reply, data);
break;
case eCSRCommand::INVENTORY_DELETE_ITEM:
InventoryDeleteItem(reply, data);
break;
case eCSRCommand::MODERATE_MUTE_ACCOUNT:
ModerateMuteAccount(reply, data);
break;
case eCSRCommand::MODERATE_BAN_ACCOUNT:
ModerateBanAccount(reply, data);
break;
case eCSRCommand::MODERATE_EDUCATE_CHARACTER:
ModerateEducateCharacter(reply, data);
break;
case eCSRCommand::MODERATE_KICK_CHARACTER:
ModerateKickCharacter(reply, data);
break;
case eCSRCommand::MODERATE_WARN_CHARACTER:
ModerateWarnCharacter(reply, data);
break;
case eCSRCommand::MODERATE_RENAME_CHARACTER:
ModerateRenameCharacter(reply, data);
break;
case eCSRCommand::MODERATE_DELETE_CHARACTER_FRIEND:
ModerateDeleteCharacterFriend(reply, data);
break;
case eCSRCommand::MODERATE_KILL_CHARACTER:
ModerateKillCharacter(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_HEALTH:
UpdateCharacterHealth(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_ARMOR:
UpdateCharacterArmor(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_IMAGINATION:
UpdateCharacterImagination(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_MAX_HEALTH:
UpdateCharacterMaxHealth(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_MAX_ARMOR:
UpdateCharacterMaxArmor(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_MAX_IMAGINATION:
UpdateCharacterMaxImagination(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_CURRENCY:
UpdateCharacterCurrency(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_REPUTATION:
UpdateCharacterReputation(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_LEGO_SCORE:
UpdateCharacterLegoScore(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_EMOTES:
UpdateCharacterEmotes(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_ADD_ACHIEVEMENT:
UpdateCharacterAddAchievement(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT:
UpdateCharacterCompleteAchievement(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_REMOVE_ACHIEVEMENT:
UpdateCharacterRemoveAchievement(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_POSITION_OFFLINE:
UpdateCharacterPositionOffline(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_INV_SLOT_AMOUNT:
UpdateCharacterInvSlotAmount(reply, data);
break;
case eCSRCommand::UTILITY_SAVE_CHARACTER:
UtilitySaveCharacter(reply, data);
break;
case eCSRCommand::UTILITY_SEND_MAIL:
UtilitySendMail(reply, data);
break;
case eCSRCommand::UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE:
UtilityGiveItemToAllPlayersOnline(reply, data);
break;
case eCSRCommand::METRICS_CONFIGURE:
MetricsConfigure(reply, data);
break;
case eCSRCommand::DISABLE_ZONE:
DisableZone(reply, data);
break;
case eCSRCommand::INIT_DONATION_AMOUNT:
InitDonationAmount(reply, data);
break;
case eCSRCommand::KILL_SERVERS_COUNTDOWN:
KillServersCountdown(reply, data);
break;
case eCSRCommand::DISABLE_FAQ:
DisableFAQ(reply, data);
break;
case eCSRCommand::THROTTLEQUEUE:
ThrottleQueue(reply, data);
break;
case eCSRCommand::GATEGM_ACCESS:
GateGMAccess(reply, data);
break;
case eCSRCommand::RECONNECT_CRISP:
ReconnectCrisp(reply, data);
break;
case eCSRCommand::MODERATE_KICK_ACCOUNT:
ModerateKickAccount(reply, data);
break;
case eCSRCommand::TOGGLE_CRISP_SERVER:
ToggleCrispServer(reply, data);
break;
case eCSRCommand::QUICK_DRAIN_SERVER:
QuickDrainServer(reply, data);
break;
case eCSRCommand::QUICK_DRAIN_SERVER_RENEW:
QuickDrainServerRenew(reply, data);
break;
case eCSRCommand::REPLICATE_CHARACTER:
ReplicateCharacter(reply, data);
break;
case eCSRCommand::GET_SERVER_STATUS:
GetServerStatus(reply);
break;
case eCSRCommand::RELOAD_SERVER_INIS:
ReloadServerINIs(reply);
break;
case eCSRCommand::INVALID:
default:
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid Command\"}";
break;
}
}
}
void CSRCommandHandler::QueryServerStatus(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QueryCharacterLocation(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QueryCharacterOnlineStatus(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::InventoryAddItem(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::InventoryDeleteItem(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateMuteAccount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateBanAccount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateEducateCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateKickCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateWarnCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateRenameCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateDeleteCharacterFriend(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateKillCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterHealth(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterArmor(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterImagination(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterMaxHealth(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterMaxArmor(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterMaxImagination(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterCurrency(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterReputation(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterLegoScore(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterEmotes(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterAddAchievement(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterCompleteAchievement(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterRemoveAchievement(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterPositionOffline(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterInvSlotAmount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UtilitySaveCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UtilitySendMail(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UtilityGiveItemToAllPlayersOnline(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::MetricsConfigure(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::DisableZone(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::InitDonationAmount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::KillServersCountdown(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::DisableFAQ(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ThrottleQueue(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::GateGMAccess(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ReconnectCrisp(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateKickAccount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ToggleCrispServer(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QuickDrainServer(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QuickDrainServerRenew(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ReplicateCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::GetServerStatus(HTTPReply& reply) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ReloadServerINIs(HTTPReply& reply) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}

View File

@@ -0,0 +1,62 @@
#ifndef __CSR_COMMAND_HANDLER__H__
#define __CSR_COMMAND_HANDLER__H__
#include <optional>
#include "json_fwd.hpp"
using json = nlohmann::json;
struct HTTPReply;
namespace CSRCommandHandler {
void HandleCSRCommand(HTTPReply& reply, const json& data);
void QueryServerStatus(HTTPReply& reply, const json& data);
void QueryCharacterLocation(HTTPReply& reply, const json& data);
void QueryCharacterOnlineStatus(HTTPReply& reply, const json& data);
void InventoryAddItem(HTTPReply& reply, const json& data);
void InventoryDeleteItem(HTTPReply& reply, const json& data);
void ModerateMuteAccount(HTTPReply& reply, const json& data);
void ModerateBanAccount(HTTPReply& reply, const json& data);
void ModerateEducateCharacter(HTTPReply& reply, const json& data);
void ModerateKickCharacter(HTTPReply& reply, const json& data);
void ModerateWarnCharacter(HTTPReply& reply, const json& data);
void ModerateRenameCharacter(HTTPReply& reply, const json& data);
void ModerateDeleteCharacterFriend(HTTPReply& reply, const json& data);
void ModerateKillCharacter(HTTPReply& reply, const json& data);
void UpdateCharacterHealth(HTTPReply& reply, const json& data);
void UpdateCharacterArmor(HTTPReply& reply, const json& data);
void UpdateCharacterImagination(HTTPReply& reply, const json& data);
void UpdateCharacterMaxHealth(HTTPReply& reply, const json& data);
void UpdateCharacterMaxArmor(HTTPReply& reply, const json& data);
void UpdateCharacterMaxImagination(HTTPReply& reply, const json& data);
void UpdateCharacterCurrency(HTTPReply& reply, const json& data);
void UpdateCharacterReputation(HTTPReply& reply, const json& data);
void UpdateCharacterLegoScore(HTTPReply& reply, const json& data);
void UpdateCharacterEmotes(HTTPReply& reply, const json& data);
void UpdateCharacterAddAchievement(HTTPReply& reply, const json& data);
void UpdateCharacterCompleteAchievement(HTTPReply& reply, const json& data);
void UpdateCharacterRemoveAchievement(HTTPReply& reply, const json& data);
void UpdateCharacterPositionOffline(HTTPReply& reply, const json& data);
void UpdateCharacterInvSlotAmount(HTTPReply& reply, const json& data);
void UtilitySaveCharacter(HTTPReply& reply, const json& data);
void UtilitySendMail(HTTPReply& reply, const json& data);
void UtilityGiveItemToAllPlayersOnline(HTTPReply& reply, const json& data);
void MetricsConfigure(HTTPReply& reply, const json& data);
void DisableZone(HTTPReply& reply, const json& data);
void InitDonationAmount(HTTPReply& reply, const json& data);
void KillServersCountdown(HTTPReply& reply, const json& data);
void DisableFAQ(HTTPReply& reply, const json& data);
void ThrottleQueue(HTTPReply& reply, const json& data);
void GateGMAccess(HTTPReply& reply, const json& data);
void ReconnectCrisp(HTTPReply& reply, const json& data);
void ModerateKickAccount(HTTPReply& reply, const json& data);
void ToggleCrispServer(HTTPReply& reply, const json& data);
void QuickDrainServer(HTTPReply& reply, const json& data);
void QuickDrainServerRenew(HTTPReply& reply, const json& data);
void ReplicateCharacter(HTTPReply& reply, const json& data);
void GetServerStatus(HTTPReply& reply);
void ReloadServerINIs(HTTPReply& reply);
};
#endif // !__CSR_COMMAND_HANDLER__H__

View File

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

View File

@@ -76,8 +76,7 @@ int main(int argc, char** argv) {
Game::assetManager = new AssetManager(clientPath); Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (std::runtime_error& ex) {
LOG("Got an error while setting up assets: %s", ex.what()); LOG("Got an error while setting up assets: %s", ex.what());
delete Game::logger;
delete Game::config;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -87,32 +86,18 @@ int main(int argc, char** argv) {
} catch (std::exception& ex) { } catch (std::exception& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server;
delete Game::logger; delete Game::logger;
delete Game::config;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// seyup the chat api web server
bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
ChatWebAPI chatwebapi;
if (web_server_enabled && !chatwebapi.Startup()){
// if we want the web api and it fails to start, exit
LOG("Failed to start web server, shutting down.");
Database::Destroy("ChatServer");
delete Game::logger;
delete Game::config;
return EXIT_FAILURE;
};
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1000; uint32_t masterPort = 1000;
std::string masterPassword;
auto masterInfo = Database::Get()->GetMasterInfo(); auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) { if (masterInfo) {
masterIP = masterInfo->ip; masterIP = masterInfo->ip;
masterPort = masterInfo->port; masterPort = masterInfo->port;
masterPassword = masterInfo->password;
} }
//It's safe to pass 'localhost' here, as the IP is only used as the external IP. //It's safe to pass 'localhost' here, as the IP is only used as the external IP.
std::string ourIP = "localhost"; std::string ourIP = "localhost";
@@ -121,7 +106,7 @@ int main(int argc, char** argv) {
const auto externalIPString = Game::config->GetValue("external_ip"); const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString; 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, masterPassword); Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false); 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::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
@@ -139,6 +124,11 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
ChatWebAPI chatwebapi;
if (web_server_enabled) chatwebapi.Listen();
auto lastTime = std::chrono::high_resolution_clock::now(); auto lastTime = std::chrono::high_resolution_clock::now();
Game::logger->Flush(); // once immediately before main loop Game::logger->Flush(); // once immediately before main loop

View File

@@ -8,13 +8,15 @@
#include "dServer.h" #include "dServer.h"
#include "dConfig.h" #include "dConfig.h"
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "JSONUtils.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "JSONUtils.h"
#include "eHTTPMethod.h" #include "eHTTPMethod.h"
#include "eHTTPStatusCode.h"
#include "magic_enum.hpp" #include "magic_enum.hpp"
#include "ChatPackets.h" #include "ChatPackets.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
#include "Database.h" #include "eCSRCommand.h"
#include "CSRCommandHandler.h"
#ifdef DARKFLAME_PLATFORM_WIN32 #ifdef DARKFLAME_PLATFORM_WIN32
#pragma push_macro("DELETE") #pragma push_macro("DELETE")
@@ -27,41 +29,44 @@ typedef struct mg_connection mg_connection;
typedef struct mg_http_message mg_http_message; typedef struct mg_http_message mg_http_message;
namespace { namespace {
const std::string root_path = "/api/v1/";
const char* json_content_type = "Content-Type: application/json\r\n"; const char* json_content_type = "Content-Type: application/json\r\n";
std::map<std::pair<eHTTPMethod, std::string>, WebAPIHTTPRoute> Routes {};
} }
bool ValidateAuthentication(const mg_http_message* http_msg) { void HandlePlayersRequest(HTTPReply& reply) {
// TO DO: This is just a placeholder for now
// use tokens or something at a later point if we want to implement authentication
// bit using the listen bind address to limit external access is good enough to start with
return true;
}
bool ValidateJSON(std::optional<json> data, HTTPReply& reply) {
if (!data) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid JSON\"}";
return false;
}
return true;
}
void HandlePlayersRequest(HTTPReply& reply, std::string body) {
const json data = Game::playerContainer; const json data = Game::playerContainer;
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK; reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump(); reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
} }
void HandleTeamsRequest(HTTPReply& reply, std::string body) { void HandleTeamsRequest(HTTPReply& reply) {
const json data = Game::playerContainer.GetTeamContainer(); const json data = Game::playerContainer.GetTeamContainer();
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK; reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump(); reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
} }
void HandleAnnounceRequest(HTTPReply& reply, std::string body) { void HandleInvalidRoute(HTTPReply& reply) {
auto data = GeneralUtils::TryParse<json>(body); reply.status = eHTTPStatusCode::NOT_FOUND;
if (!ValidateJSON(data, reply)) return; reply.message = "{\"error\":\"Invalid Route\"}";
}
void HandleGET(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
switch (route) {
case ChatWebAPI::eRoute::PLAYERS:
HandlePlayersRequest(reply);
break;
case ChatWebAPI::eRoute::TEAMS:
HandleTeamsRequest(reply);
break;
case ChatWebAPI::eRoute::INVALID:
default:
HandleInvalidRoute(reply);
break;
}
}
void HandleAnnounceRequest(HTTPReply& reply, const std::optional<json>& data) {
if (!JSONUtils::Validate(data, reply)) return;
const auto& good_data = data.value(); const auto& good_data = data.value();
auto check = JSONUtils::CheckRequiredData(good_data, { "title", "message" }); auto check = JSONUtils::CheckRequiredData(good_data, { "title", "message" });
@@ -80,9 +85,33 @@ void HandleAnnounceRequest(HTTPReply& reply, std::string body) {
} }
} }
void HandleInvalidRoute(HTTPReply& reply) { void HandleCSRCommandRequest(HTTPReply& reply, const std::optional<json>& data) {
reply.status = eHTTPStatusCode::NOT_FOUND; if (!JSONUtils::Validate(data, reply)) return;
reply.message = "{\"error\":\"Invalid Route\"}";
const auto& good_data = data.value();
auto check = JSONUtils::CheckRequiredData(good_data, { "command" });
if (!check.empty()) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = check;
} else {
CSRCommandHandler::HandleCSRCommand(reply, good_data);
}
}
void HandlePOST(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
auto data = GeneralUtils::TryParse<json>(body);
switch (route) {
case ChatWebAPI::eRoute::ANNOUNCE:
HandleAnnounceRequest(reply, data.value());
break;
case ChatWebAPI::eRoute::CSR:
HandleCSRCommandRequest(reply, data.value());
break;
case ChatWebAPI::eRoute::INVALID:
default:
HandleInvalidRoute(reply);
break;
}
} }
void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) { void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) {
@@ -91,7 +120,7 @@ void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_ms
if (!http_msg) { if (!http_msg) {
reply.status = eHTTPStatusCode::BAD_REQUEST; reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid Request\"}"; reply.message = "{\"error\":\"Invalid Request\"}";
} else if (ValidateAuthentication(http_msg)) { } else {
// convert method from cstring to std string // convert method from cstring to std string
std::string method_string(http_msg->method.buf, http_msg->method.len); std::string method_string(http_msg->method.buf, http_msg->method.len);
@@ -100,21 +129,38 @@ void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_ms
// convert uri from cstring to std string // convert uri from cstring to std string
std::string uri(http_msg->uri.buf, http_msg->uri.len); std::string uri(http_msg->uri.buf, http_msg->uri.len);
std::transform(uri.begin(), uri.end(), uri.begin(), ::tolower); // check for root path
if (uri.find(root_path) == 0) {
// remove root path from uri
uri.erase(0, root_path.length());
// convert uri to uppercase
std::transform(uri.begin(), uri.end(), uri.begin(), ::toupper);
// convert uri string to route enum
ChatWebAPI::eRoute route = magic_enum::enum_cast<ChatWebAPI::eRoute>(uri).value_or(ChatWebAPI::eRoute::INVALID);
// convert body from cstring to std string
std::string body(http_msg->body.buf, http_msg->body.len);
// convert body from cstring to std string switch (method) {
std::string body(http_msg->body.buf, http_msg->body.len); case eHTTPMethod::GET:
HandleGET(reply, route, body);
break;
const auto routeItr = Routes.find({method, uri}); case eHTTPMethod::POST:
HandlePOST(reply, route, body);
if (routeItr != Routes.end()) { break;
const auto& [_, route] = *routeItr; case eHTTPMethod::PUT:
route.handle(reply, body); case eHTTPMethod::DELETE:
} else HandleInvalidRoute(reply); case eHTTPMethod::HEAD:
} else { case eHTTPMethod::CONNECT:
reply.status = eHTTPStatusCode::UNAUTHORIZED; case eHTTPMethod::OPTIONS:
reply.message = "{\"error\":\"Unauthorized\"}"; case eHTTPMethod::TRACE:
case eHTTPMethod::PATCH:
case eHTTPMethod::INVALID:
default:
reply.status = eHTTPStatusCode::METHOD_NOT_ALLOWED;
reply.message = "{\"error\":\"Invalid Method\"}";
break;
}
}
} }
mg_http_reply(connection, static_cast<int>(reply.status), json_content_type, reply.message.c_str()); mg_http_reply(connection, static_cast<int>(reply.status), json_content_type, reply.message.c_str());
} }
@@ -130,14 +176,9 @@ void HandleRequests(mg_connection* connection, int request, void* request_data)
} }
} }
void ChatWebAPI::RegisterHTTPRoutes(WebAPIHTTPRoute route) { #ifdef DARKFLAME_PLATFORM_WIN32
auto [_, success] = Routes.try_emplace({ route.method, route.path }, route); #pragma pop_macro("DELETE")
if (!success) { #endif
LOG_DEBUG("Failed to register route %s", route.path.c_str());
} else {
LOG_DEBUG("Registered route %s", route.path.c_str());
}
}
ChatWebAPI::ChatWebAPI() { ChatWebAPI::ChatWebAPI() {
mg_log_set(MG_LL_NONE); mg_log_set(MG_LL_NONE);
@@ -148,8 +189,8 @@ ChatWebAPI::~ChatWebAPI() {
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
} }
bool ChatWebAPI::Startup() { void ChatWebAPI::Listen() {
// Make listen address // make listen address
std::string listen_ip = Game::config->GetValue("web_server_listen_ip"); std::string listen_ip = Game::config->GetValue("web_server_listen_ip");
if (listen_ip == "localhost") listen_ip = "127.0.0.1"; if (listen_ip == "localhost") listen_ip = "127.0.0.1";
@@ -159,38 +200,10 @@ bool ChatWebAPI::Startup() {
// Create HTTP listener // Create HTTP listener
if (!mg_http_listen(&mgr, listen_address.c_str(), HandleRequests, NULL)) { if (!mg_http_listen(&mgr, listen_address.c_str(), HandleRequests, NULL)) {
LOG("Failed to create web server listener on %s", listen_port.c_str()); LOG("Failed to create web server listener");
return false;
} }
// Register routes
// API v1 routes
std::string v1_route = "/api/v1/";
RegisterHTTPRoutes({
.path = v1_route + "players",
.method = eHTTPMethod::GET,
.handle = HandlePlayersRequest
});
RegisterHTTPRoutes({
.path = v1_route + "teams",
.method = eHTTPMethod::GET,
.handle = HandleTeamsRequest
});
RegisterHTTPRoutes({
.path = v1_route + "announce",
.method = eHTTPMethod::POST,
.handle = HandleAnnounceRequest
});
return true;
} }
void ChatWebAPI::ReceiveRequests() { void ChatWebAPI::ReceiveRequests() {
mg_mgr_poll(&mgr, 15); mg_mgr_poll(&mgr, 15);
} }
#ifdef DARKFLAME_PLATFORM_WIN32
#pragma pop_macro("DELETE")
#endif

View File

@@ -1,36 +1,30 @@
#ifndef __CHATWEBAPI_H__ #ifndef CHATWEBAPI_H
#define __CHATWEBAPI_H__ #define CHATWEBAPI_H
#include <string>
#include <functional>
#include "mongoose.h" #include "mongoose.h"
#include "eHTTPStatusCode.h"
enum class eHTTPMethod;
typedef struct mg_mgr mg_mgr; typedef struct mg_mgr mg_mgr;
struct HTTPReply {
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
std::string message = "{\"error\":\"Not Found\"}";
};
struct WebAPIHTTPRoute {
std::string path;
eHTTPMethod method;
std::function<void(HTTPReply&, const std::string&)> handle;
};
class ChatWebAPI { class ChatWebAPI {
public: public:
ChatWebAPI(); ChatWebAPI();
~ChatWebAPI(); ~ChatWebAPI();
void ReceiveRequests(); void ReceiveRequests();
void RegisterHTTPRoutes(WebAPIHTTPRoute route); void Listen();
bool Startup(); enum class eRoute {
// GET
PLAYERS,
TEAMS,
// POST
ANNOUNCE,
CSR,
// INVALID
INVALID
};
private: private:
mg_mgr mgr; mg_mgr mgr;
}; };
#endif // __CHATWEBAPI_H__ #endif

View File

@@ -1,6 +1,7 @@
#include "JSONUtils.h" #include "JSONUtils.h"
#include "json.hpp" #include "json.hpp"
#include "eHTTPStatusCode.h"
using json = nlohmann::json; using json = nlohmann::json;
@@ -17,11 +18,7 @@ void to_json(json& data, const PlayerData& playerData) {
} }
void to_json(json& data, const PlayerContainer& playerContainer) { void to_json(json& data, const PlayerContainer& playerContainer) {
data = json::array(); data = playerContainer.GetAllPlayers();
for(auto& playerData : playerContainer.GetAllPlayers()) {
if (playerData.first == LWOOBJID_EMPTY) continue;
data.push_back(playerData.second);
}
} }
void to_json(json& data, const TeamContainer& teamContainer) { void to_json(json& data, const TeamContainer& teamContainer) {
@@ -60,3 +57,12 @@ std::string JSONUtils::CheckRequiredData(const json& data, const std::vector<std
} }
return check["error"].empty() ? "" : check.dump(); return check["error"].empty() ? "" : check.dump();
} }
bool JSONUtils::Validate(const std::optional<json>& data, HTTPReply& reply) {
if (!data) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid JSON\"}";
return false;
}
return true;
}

View File

@@ -3,6 +3,12 @@
#include "json_fwd.hpp" #include "json_fwd.hpp"
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "eHTTPStatusCode.h"
struct HTTPReply {
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
std::string message = "{\"error\":\"Not Found\"}";
};
void to_json(nlohmann::json& data, const PlayerData& playerData); void to_json(nlohmann::json& data, const PlayerData& playerData);
void to_json(nlohmann::json& data, const PlayerContainer& playerContainer); void to_json(nlohmann::json& data, const PlayerContainer& playerContainer);
@@ -12,6 +18,7 @@ void to_json(nlohmann::json& data, const TeamData& teamData);
namespace JSONUtils { namespace JSONUtils {
// check required data for reqeust // check required data for reqeust
std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData); std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
bool Validate(const std::optional<nlohmann::json>& data, HTTPReply& reply);
} }
#endif // __JSONUTILS_H__ #endif // __JSONUTILS_H__

View File

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

View File

@@ -52,7 +52,6 @@ struct PlayerData {
std::vector<IgnoreData> ignoredPlayers; std::vector<IgnoreData> ignoredPlayers;
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
bool isFTP = false; bool isFTP = false;
bool isLogin = false;
}; };
@@ -98,8 +97,8 @@ public:
LWOOBJID GetId(const std::u16string& playerName); LWOOBJID GetId(const std::u16string& playerName);
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; } uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; } uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
const TeamContainer& GetTeamContainer() { return m_TeamContainer; } const TeamContainer& GetTeamContainer() { return teamContainer; }
std::vector<TeamData*>& GetTeamsMut() { return m_TeamContainer.mTeams; }; std::vector<TeamData*>& GetTeamsMut() { return teamContainer.mTeams; };
const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); }; const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
void Update(const float deltaTime); void Update(const float deltaTime);
@@ -108,7 +107,7 @@ public:
private: private:
LWOOBJID m_TeamIDCounter = 0; LWOOBJID m_TeamIDCounter = 0;
std::map<LWOOBJID, PlayerData> m_Players; std::map<LWOOBJID, PlayerData> m_Players;
TeamContainer m_TeamContainer{}; TeamContainer teamContainer{};
std::unordered_map<LWOOBJID, std::u16string> m_Names; std::unordered_map<LWOOBJID, std::u16string> m_Names;
std::map<LWOOBJID, float> m_PlayersToRemove; std::map<LWOOBJID, float> m_PlayersToRemove;
uint32_t m_MaxNumberOfBestFriends = 5; uint32_t m_MaxNumberOfBestFriends = 5;

View File

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

View File

@@ -0,0 +1,54 @@
#ifndef __ECSRCOMMAND__H__
#define __ECSRCOMMAND__H__
enum class eCSRCommand {
QUERY_SERVER_STATUS,
QUERY_CHARACTER_LOCATION,
QUERY_CHARACTER_ONLINE_STATUS,
INVENTORY_ADD_ITEM,
INVENTORY_DELETE_ITEM,
MODERATE_MUTE_ACCOUNT,
MODERATE_BAN_ACCOUNT,
MODERATE_EDUCATE_CHARACTER,
MODERATE_KICK_CHARACTER,
MODERATE_WARN_CHARACTER,
MODERATE_RENAME_CHARACTER,
MODERATE_DELETE_CHARACTER_FRIEND,
MODERATE_KILL_CHARACTER,
UPDATE_CHARACTER_HEALTH,
UPDATE_CHARACTER_ARMOR,
UPDATE_CHARACTER_IMAGINATION,
UPDATE_CHARACTER_MAX_HEALTH,
UPDATE_CHARACTER_MAX_ARMOR,
UPDATE_CHARACTER_MAX_IMAGINATION,
UPDATE_CHARACTER_CURRENCY,
UPDATE_CHARACTER_REPUTATION,
UPDATE_CHARACTER_LEGO_SCORE,
UPDATE_CHARACTER_EMOTES,
UPDATE_CHARACTER_ADD_ACHIEVEMENT,
UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT,
UPDATE_CHARACTER_REMOVE_ACHIEVEMENT,
UPDATE_CHARACTER_POSITION_OFFLINE,
UPDATE_CHARACTER_INV_SLOT_AMOUNT,
UTILITY_SAVE_CHARACTER,
UTILITY_SEND_MAIL,
UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE,
METRICS_CONFIGURE,
DISABLE_ZONE,
INIT_DONATION_AMOUNT,
KILL_SERVERS_COUNTDOWN,
DISABLE_FAQ,
THROTTLEQUEUE,
GATEGM_ACCESS,
RECONNECT_CRISP,
MODERATE_KICK_ACCOUNT,
TOGGLE_CRISP_SERVER,
QUICK_DRAIN_SERVER,
QUICK_DRAIN_SERVER_RENEW,
REPLICATE_CHARACTER,
GET_SERVER_STATUS,
RELOAD_SERVER_INIS,
INVALID
};
#endif // !__ECSRCOMMAND__H__

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -84,12 +84,11 @@ dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") { } 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); toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2; m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
// 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") {
} /* else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f); toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f);
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") { } else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 48.386536f, 50.363434f, 259.361755f); 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()); // LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
//add fallback cube: //add fallback cube:

View File

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

View File

@@ -218,48 +218,6 @@ public:
* @param skipChecks whether or not to skip the check for the quickbuild not being completed * @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 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: private:
/** /**
* Whether or not the quickbuild state has been changed since we last serialized it. * Whether or not the quickbuild state has been changed since we last serialized it.

View File

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

View File

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

View File

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

View File

@@ -40,21 +40,7 @@ public:
} }
} ReceiveDownloadCompleteCB; } ReceiveDownloadCompleteCB;
dServer::dServer( 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) {
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; mIP = ip;
mPort = port; mPort = port;
mZoneID = zoneID; mZoneID = zoneID;
@@ -70,7 +56,6 @@ dServer::dServer(
mReplicaManager = nullptr; mReplicaManager = nullptr;
mServerType = serverType; mServerType = serverType;
mConfig = config; mConfig = config;
mMasterPassword = masterPassword;
mShouldShutdown = lastSignal; mShouldShutdown = lastSignal;
//Attempt to start our server here: //Attempt to start our server here:
mIsOkay = Startup(); mIsOkay = Startup();
@@ -218,11 +203,11 @@ bool dServer::Startup() {
if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false; if (!mPeer->Startup(mMaxConnections, 10, &mSocketDescriptor, 1)) return false;
if (mIsInternal) { if (mIsInternal) {
mPeer->SetIncomingPassword(mMasterPassword.c_str(), mMasterPassword.size()); mPeer->SetIncomingPassword("3.25 DARKFLAME1", 15);
} else { } else {
UpdateBandwidthLimit(); UpdateBandwidthLimit();
UpdateMaximumMtuSize(); UpdateMaximumMtuSize();
mPeer->SetIncomingPassword(NET_PASSWORD_EXTERNAL, strnlen(NET_PASSWORD_EXTERNAL, sizeof(NET_PASSWORD_EXTERNAL))); mPeer->SetIncomingPassword("3.25 ND1", 8);
} }
mPeer->SetMaximumIncomingConnections(mMaxConnections); mPeer->SetMaximumIncomingConnections(mMaxConnections);
@@ -272,7 +257,7 @@ void dServer::SetupForMasterConnection() {
bool dServer::ConnectToMaster() { bool dServer::ConnectToMaster() {
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort); //LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, mMasterPassword.c_str(), mMasterPassword.size()); return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
} }
void dServer::UpdateReplica() { void dServer::UpdateReplica() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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