Merge remote-tracking branch 'upstream/main' into speed

This commit is contained in:
David Markowitz 2023-12-17 21:32:49 -08:00
commit d77657782e
51 changed files with 815 additions and 408 deletions

View File

@ -23,6 +23,9 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
### Supply of resource files ### Supply of resource files
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work. Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
## Step by step walkthrough for a single-player server
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
## Steps to setup server ## Steps to setup server
* [Clone this repository](#clone-the-repository) * [Clone this repository](#clone-the-repository)
* [Install dependencies](#install-dependencies) * [Install dependencies](#install-dependencies)

View File

@ -1,4 +1,5 @@
set(DCHATSERVER_SOURCES set(DCHATSERVER_SOURCES
"ChatIgnoreList.cpp"
"ChatPacketHandler.cpp" "ChatPacketHandler.cpp"
"PlayerContainer.cpp" "PlayerContainer.cpp"
) )

View File

@ -0,0 +1,173 @@
#include "ChatIgnoreList.h"
#include "PlayerContainer.h"
#include "eChatInternalMessageType.h"
#include "BitStreamUtils.h"
#include "PacketUtils.h"
#include "Game.h"
#include "Logger.h"
#include "eObjectBits.h"
#include "Database.h"
// A note to future readers, The client handles all the actual ignoring logic:
// not allowing teams, rejecting DMs, friends requets etc.
// The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receivingPlayer);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type);
}
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
if (!receiver) {
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
return;
}
if (!receiver->ignoredPlayers.empty()) {
LOG_DEBUG("Player %llu already has an ignore list", playerId);
return;
}
auto ignoreList = Database::Get()->GetIgnoreList(static_cast<uint32_t>(playerId));
if (ignoreList.empty()) {
LOG_DEBUG("Player %llu has no ignores", playerId);
return;
}
for (auto& ignoredPlayer : ignoreList) {
receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayer.id, ignoredPlayer.name });
GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::PERSISTENT);
}
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::GET_IGNORE);
bitStream.Write<uint8_t>(false); // Probably is Is Free Trial, but we don't care about that
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
bitStream.Write<uint16_t>(receiver->ignoredPlayers.size());
for (const auto& ignoredPlayer : receiver->ignoredPlayers) {
bitStream.Write(ignoredPlayer.playerId);
bitStream.Write(LUWString(ignoredPlayer.playerName, 36));
}
Game::server->Send(&bitStream, packet->systemAddress, false);
}
void ChatIgnoreList::AddIgnore(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
if (!receiver) {
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
return;
}
constexpr int32_t MAX_IGNORES = 32;
if (receiver->ignoredPlayers.size() > MAX_IGNORES) {
LOG_DEBUG("Player %llu has too many ignores", playerId);
return;
}
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
LUWString toIgnoreName(33);
inStream.Read(toIgnoreName);
std::string toIgnoreStr = toIgnoreName.GetAsString();
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::ADD_IGNORE);
// Check if the player exists
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
if (toIgnoreStr == receiver->playerName || toIgnoreStr.find("[GM]") == 0) {
LOG_DEBUG("Player %llu tried to ignore themselves", playerId);
bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR);
} else if (std::count(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), toIgnoreStr) > 0) {
LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str());
bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED);
} else {
// Get the playerId falling back to query if not online
auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr);
if (!playerData) {
// Fall back to query
auto player = Database::Get()->GetCharacterInfo(toIgnoreStr);
if (!player || player->name != toIgnoreStr) {
LOG_DEBUG("Player %s not found", toIgnoreStr.c_str());
} else {
ignoredPlayerId = player->id;
}
} else {
ignoredPlayerId = playerData->playerID;
}
if (ignoredPlayerId != LWOOBJID_EMPTY) {
Database::Get()->AddIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(ignoredPlayerId));
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT);
receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr });
LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str());
bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS);
} else {
bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND);
}
}
LUWString playerNameSend(toIgnoreStr, 33);
bitStream.Write(playerNameSend);
bitStream.Write(ignoredPlayerId);
Game::server->Send(&bitStream, packet->systemAddress, false);
}
void ChatIgnoreList::RemoveIgnore(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
if (!receiver) {
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
return;
}
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
LUWString removedIgnoreName(33);
inStream.Read(removedIgnoreName);
std::string removedIgnoreStr = removedIgnoreName.GetAsString();
auto toRemove = std::remove(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), removedIgnoreStr);
if (toRemove == receiver->ignoredPlayers.end()) {
LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str());
return;
}
Database::Get()->RemoveIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(toRemove->playerId));
receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end());
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::REMOVE_IGNORE);
bitStream.Write<int8_t>(0);
LUWString playerNameSend(removedIgnoreStr, 33);
bitStream.Write(playerNameSend);
Game::server->Send(&bitStream, packet->systemAddress, false);
}

View File

@ -0,0 +1,27 @@
#ifndef __CHATIGNORELIST__H__
#define __CHATIGNORELIST__H__
struct Packet;
#include <cstdint>
namespace ChatIgnoreList {
void GetIgnoreList(Packet* packet);
void AddIgnore(Packet* packet);
void RemoveIgnore(Packet* packet);
enum class Response : uint8_t {
ADD_IGNORE = 32,
REMOVE_IGNORE = 33,
GET_IGNORE = 34,
};
enum class AddResponse : uint8_t {
SUCCESS,
ALREADY_IGNORED,
PLAYER_NOT_FOUND,
GENERAL_ERROR,
};
};
#endif //!__CHATIGNORELIST__H__

View File

@ -19,15 +19,13 @@
#include "eClientMessageType.h" #include "eClientMessageType.h"
#include "eGameMessageType.h" #include "eGameMessageType.h"
extern PlayerContainer playerContainer;
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Get from the packet which player we want to do something with: //Get from the packet which player we want to do something with:
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = 0; LWOOBJID playerID = 0;
inStream.Read(playerID); inStream.Read(playerID);
auto player = playerContainer.GetPlayerData(playerID); auto player = Game::playerContainer.GetPlayerData(playerID);
if (!player) return; if (!player) return;
auto friendsList = Database::Get()->GetFriendsList(playerID); auto friendsList = Database::Get()->GetFriendsList(playerID);
@ -43,7 +41,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
fd.friendName = friendData.friendName; fd.friendName = friendData.friendName;
//Now check if they're online: //Now check if they're online:
auto fr = playerContainer.GetPlayerData(fd.friendID); auto fr = Game::playerContainer.GetPlayerData(fd.friendID);
if (fr) { if (fr) {
fd.isOnline = true; fd.isOnline = true;
@ -97,7 +95,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
char isBestFriendRequest{}; char isBestFriendRequest{};
inStream.Read(isBestFriendRequest); inStream.Read(isBestFriendRequest);
auto requestor = playerContainer.GetPlayerData(requestorPlayerID); auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID);
if (!requestor) { if (!requestor) {
LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str()); LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str());
return; return;
@ -107,7 +105,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN); SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN);
return; return;
}; };
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName)); std::unique_ptr<PlayerData> requestee(Game::playerContainer.GetPlayerData(playerName));
// Check if player is online first // Check if player is online first
if (isBestFriendRequest && !requestee) { if (isBestFriendRequest && !requestee) {
@ -175,7 +173,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// Only do updates if there was a change in the bff status. // Only do updates if there was a change in the bff status.
if (oldBestFriendStatus != bestFriendStatus) { if (oldBestFriendStatus != bestFriendStatus) {
auto maxBestFriends = playerContainer.GetMaxNumberOfBestFriends(); auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends();
if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) { if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) {
if (requestee->countOfBestFriends >= maxBestFriends) { if (requestee->countOfBestFriends >= maxBestFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
@ -208,7 +206,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true); if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true);
} }
} else { } else {
auto maxFriends = playerContainer.GetMaxNumberOfFriends(); auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
if (requestee->friends.size() >= maxFriends) { if (requestee->friends.size() >= maxFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
} else if (requestor->friends.size() >= maxFriends) { } else if (requestor->friends.size() >= maxFriends) {
@ -232,8 +230,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
std::string friendName = PacketUtils::ReadString(0x15, packet, true); std::string friendName = PacketUtils::ReadString(0x15, packet, true);
//Now to try and find both of these: //Now to try and find both of these:
auto requestor = playerContainer.GetPlayerData(playerID); auto requestor = Game::playerContainer.GetPlayerData(playerID);
auto requestee = playerContainer.GetPlayerData(friendName); auto requestee = Game::playerContainer.GetPlayerData(friendName);
if (!requestor || !requestee) return; if (!requestor || !requestee) return;
eAddFriendResponseType serverResponseCode{}; eAddFriendResponseType serverResponseCode{};
@ -315,7 +313,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
Database::Get()->RemoveFriend(playerID, friendID); Database::Get()->RemoveFriend(playerID, friendID);
//Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended: //Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended:
auto goonA = playerContainer.GetPlayerData(playerID); auto goonA = Game::playerContainer.GetPlayerData(playerID);
if (goonA) { if (goonA) {
// Remove the friend from our list of friends // Remove the friend from our list of friends
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) { for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
@ -328,7 +326,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
SendRemoveFriend(goonA, friendName, true); SendRemoveFriend(goonA, friendName, true);
} }
auto goonB = playerContainer.GetPlayerData(friendID); auto goonB = Game::playerContainer.GetPlayerData(friendID);
if (!goonB) return; if (!goonB) return;
// Do it again for other person // Do it again for other person
for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) { for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) {
@ -339,7 +337,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
} }
} }
std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID)); std::string goonAName = GeneralUtils::UTF16ToWTF8(Game::playerContainer.GetName(playerID));
SendRemoveFriend(goonB, goonAName, true); SendRemoveFriend(goonB, goonAName, true);
} }
@ -348,11 +346,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
LWOOBJID playerID = LWOOBJID_EMPTY; LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID); inStream.Read(playerID);
auto* sender = playerContainer.GetPlayerData(playerID); auto* sender = Game::playerContainer.GetPlayerData(playerID);
if (sender == nullptr) return; if (sender == nullptr) return;
if (playerContainer.GetIsMuted(sender)) return; if (Game::playerContainer.GetIsMuted(sender)) return;
const auto senderName = std::string(sender->playerName.c_str()); const auto senderName = std::string(sender->playerName.c_str());
@ -367,12 +365,12 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
if (channel != 8) return; if (channel != 8) return;
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
if (team == nullptr) return; if (team == nullptr) return;
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = playerContainer.GetPlayerData(memberId); auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
if (otherMember == nullptr) return; if (otherMember == nullptr) return;
@ -406,11 +404,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512); std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
//Get the bois: //Get the bois:
auto goonA = playerContainer.GetPlayerData(senderID); auto goonA = Game::playerContainer.GetPlayerData(senderID);
auto goonB = playerContainer.GetPlayerData(receiverName); auto goonB = Game::playerContainer.GetPlayerData(receiverName);
if (!goonA || !goonB) return; if (!goonA || !goonB) return;
if (playerContainer.GetIsMuted(goonA)) return; if (Game::playerContainer.GetIsMuted(goonA)) return;
std::string goonAName = goonA->playerName.c_str(); std::string goonAName = goonA->playerName.c_str();
std::string goonBName = goonB->playerName.c_str(); std::string goonBName = goonB->playerName.c_str();
@ -468,25 +466,25 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
inStream.Read(playerID); inStream.Read(playerID);
std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true); std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true);
auto* player = playerContainer.GetPlayerData(playerID); auto* player = Game::playerContainer.GetPlayerData(playerID);
if (player == nullptr) { if (player == nullptr) {
return; return;
} }
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
if (team == nullptr) { if (team == nullptr) {
team = playerContainer.CreateTeam(playerID); team = Game::playerContainer.CreateTeam(playerID);
} }
auto* other = playerContainer.GetPlayerData(invitedPlayer); auto* other = Game::playerContainer.GetPlayerData(invitedPlayer);
if (other == nullptr) { if (other == nullptr) {
return; return;
} }
if (playerContainer.GetTeam(other->playerID) != nullptr) { if (Game::playerContainer.GetTeam(other->playerID) != nullptr) {
return; return;
} }
@ -519,12 +517,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
return; return;
} }
auto* team = playerContainer.GetTeam(leaderID); auto* team = Game::playerContainer.GetTeam(leaderID);
if (team == nullptr) { if (team == nullptr) {
LOG("Failed to find team for leader (%llu)", leaderID); LOG("Failed to find team for leader (%llu)", leaderID);
team = playerContainer.GetTeam(playerID); team = Game::playerContainer.GetTeam(playerID);
} }
if (team == nullptr) { if (team == nullptr) {
@ -532,7 +530,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
return; return;
} }
playerContainer.AddMember(team, playerID); Game::playerContainer.AddMember(team, playerID);
} }
void ChatPacketHandler::HandleTeamLeave(Packet* packet) { void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
@ -542,12 +540,12 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
uint32_t size = 0; uint32_t size = 0;
inStream.Read(size); inStream.Read(size);
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
LOG("(%llu) leaving team", playerID); LOG("(%llu) leaving team", playerID);
if (team != nullptr) { if (team != nullptr) {
playerContainer.RemoveMember(team, playerID, false, false, true); Game::playerContainer.RemoveMember(team, playerID, false, false, true);
} }
} }
@ -560,24 +558,24 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) {
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str()); LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str());
auto* kicked = playerContainer.GetPlayerData(kickedPlayer); auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer);
LWOOBJID kickedId = LWOOBJID_EMPTY; LWOOBJID kickedId = LWOOBJID_EMPTY;
if (kicked != nullptr) { if (kicked != nullptr) {
kickedId = kicked->playerID; kickedId = kicked->playerID;
} else { } else {
kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer)); kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
} }
if (kickedId == LWOOBJID_EMPTY) return; if (kickedId == LWOOBJID_EMPTY) return;
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
if (team->leaderID != playerID || team->leaderID == kickedId) return; if (team->leaderID != playerID || team->leaderID == kickedId) return;
playerContainer.RemoveMember(team, kickedId, false, true, false); Game::playerContainer.RemoveMember(team, kickedId, false, true, false);
} }
} }
@ -590,16 +588,16 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) {
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str()); LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str());
auto* promoted = playerContainer.GetPlayerData(promotedPlayer); auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer);
if (promoted == nullptr) return; if (promoted == nullptr) return;
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
if (team->leaderID != playerID) return; if (team->leaderID != playerID) return;
playerContainer.PromoteMember(team, promoted->playerID); Game::playerContainer.PromoteMember(team, promoted->playerID);
} }
} }
@ -613,16 +611,16 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) {
char option; char option;
inStream.Read(option); inStream.Read(option);
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
if (team->leaderID != playerID) return; if (team->leaderID != playerID) return;
team->lootFlag = option; team->lootFlag = option;
playerContainer.TeamStatusUpdate(team); Game::playerContainer.TeamStatusUpdate(team);
playerContainer.UpdateTeamsOnWorld(team, false); Game::playerContainer.UpdateTeamsOnWorld(team, false);
} }
} }
@ -631,18 +629,18 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
LWOOBJID playerID = LWOOBJID_EMPTY; LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID); inStream.Read(playerID);
auto* team = playerContainer.GetTeam(playerID); auto* team = Game::playerContainer.GetTeam(playerID);
auto* data = playerContainer.GetPlayerData(playerID); auto* data = Game::playerContainer.GetPlayerData(playerID);
if (team != nullptr && data != nullptr) { if (team != nullptr && data != nullptr) {
if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) { if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
playerContainer.RemoveMember(team, playerID, false, false, true, true); Game::playerContainer.RemoveMember(team, playerID, false, false, true, true);
return; return;
} }
if (team->memberIDs.size() <= 1 && !team->local) { if (team->memberIDs.size() <= 1 && !team->local) {
playerContainer.DisbandTeam(team); Game::playerContainer.DisbandTeam(team);
return; return;
} }
@ -653,16 +651,16 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
} }
playerContainer.TeamStatusUpdate(team); Game::playerContainer.TeamStatusUpdate(team);
const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName); const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = playerContainer.GetPlayerData(memberId); auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
if (memberId == playerID) continue; if (memberId == playerID) continue;
const auto memberName = playerContainer.GetName(memberId); const auto memberName = Game::playerContainer.GetName(memberId);
if (otherMember != nullptr) { if (otherMember != nullptr) {
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID); ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID);
@ -670,7 +668,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
} }
playerContainer.UpdateTeamsOnWorld(team, false); Game::playerContainer.UpdateTeamsOnWorld(team, false);
} }
} }

View File

@ -19,6 +19,7 @@
#include "eChatMessageType.h" #include "eChatMessageType.h"
#include "eChatInternalMessageType.h" #include "eChatInternalMessageType.h"
#include "eWorldMessageType.h" #include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "Game.h" #include "Game.h"
@ -34,14 +35,12 @@ namespace Game {
AssetManager* assetManager = nullptr; AssetManager* assetManager = nullptr;
bool shouldShutdown = false; bool shouldShutdown = false;
std::mt19937 randomEngine; std::mt19937 randomEngine;
PlayerContainer playerContainer;
} }
Logger* SetupLogger(); Logger* SetupLogger();
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
PlayerContainer playerContainer;
int main(int argc, char** argv) { int main(int argc, char** argv) {
constexpr uint32_t chatFramerate = mediumFramerate; constexpr uint32_t chatFramerate = mediumFramerate;
constexpr uint32_t chatFrameDelta = mediumFrameDelta; constexpr uint32_t chatFrameDelta = mediumFrameDelta;
@ -108,7 +107,7 @@ int main(int argc, char** argv) {
Game::randomEngine = std::mt19937(time(0)); Game::randomEngine = std::mt19937(time(0));
playerContainer.Initialize(); Game::playerContainer.Initialize();
//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();
@ -200,19 +199,19 @@ void HandlePacket(Packet* packet) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
switch (static_cast<eChatInternalMessageType>(packet->data[3])) { switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION: case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
playerContainer.InsertPlayer(packet); Game::playerContainer.InsertPlayer(packet);
break; break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
playerContainer.RemovePlayer(packet); Game::playerContainer.RemovePlayer(packet);
break; break;
case eChatInternalMessageType::MUTE_UPDATE: case eChatInternalMessageType::MUTE_UPDATE:
playerContainer.MuteUpdate(packet); Game::playerContainer.MuteUpdate(packet);
break; break;
case eChatInternalMessageType::CREATE_TEAM: case eChatInternalMessageType::CREATE_TEAM:
playerContainer.CreateTeamServer(packet); Game::playerContainer.CreateTeamServer(packet);
break; break;
case eChatInternalMessageType::ANNOUNCEMENT: { case eChatInternalMessageType::ANNOUNCEMENT: {
@ -234,7 +233,15 @@ void HandlePacket(Packet* packet) {
break; break;
case eChatMessageType::GET_IGNORE_LIST: case eChatMessageType::GET_IGNORE_LIST:
LOG("Asked for ignore list, but is unimplemented right now."); ChatIgnoreList::GetIgnoreList(packet);
break;
case eChatMessageType::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet);
break;
case eChatMessageType::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet);
break; break;
case eChatMessageType::TEAM_GET_STATUS: case eChatMessageType::TEAM_GET_STATUS:

View File

@ -7,12 +7,26 @@
#include "dServer.h" #include "dServer.h"
#include <unordered_map> #include <unordered_map>
struct IgnoreData {
inline bool operator==(const std::string& other) const noexcept {
return playerName == other;
}
inline bool operator==(const LWOOBJID& other) const noexcept {
return playerId == other;
}
LWOOBJID playerId;
std::string playerName;
};
struct PlayerData { struct PlayerData {
LWOOBJID playerID; LWOOBJID playerID;
std::string playerName; std::string playerName;
SystemAddress sysAddr; SystemAddress sysAddr;
LWOZONEID zoneID; LWOZONEID zoneID;
std::vector<FriendData> friends; std::vector<FriendData> friends;
std::vector<IgnoreData> ignoredPlayers;
time_t muteExpire; time_t muteExpire;
uint8_t countOfBestFriends = 0; uint8_t countOfBestFriends = 0;
}; };

View File

@ -12,6 +12,7 @@ class AssetManager;
struct SystemAddress; struct SystemAddress;
class EntityManager; class EntityManager;
class dZoneManager; class dZoneManager;
class PlayerContainer;
namespace Game { namespace Game {
extern Logger* logger; extern Logger* logger;
@ -26,4 +27,5 @@ namespace Game {
extern bool shouldShutdown; extern bool shouldShutdown;
extern EntityManager* entityManager; extern EntityManager* entityManager;
extern dZoneManager* zoneManager; extern dZoneManager* zoneManager;
extern PlayerContainer playerContainer;
} }

View File

@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
PLATFORM_BOUNDARY, PLATFORM_BOUNDARY,
MODULE, MODULE,
ARCADE, ARCADE,
VEHICLE_PHYSICS, // Havok demo based HAVOK_VEHICLE_PHYSICS,
MOVEMENT_AI, MOVEMENT_AI,
EXHIBIT, EXHIBIT,
OVERHEAD_ICON, OVERHEAD_ICON,
@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t {
PROPERTY_ENTRANCE, PROPERTY_ENTRANCE,
FX, FX,
PROPERTY_MANAGEMENT, PROPERTY_MANAGEMENT,
VEHICLE_PHYSICS_NEW, // internal physics based on havok VEHICLE_PHYSICS,
PHYSICS_SYSTEM, PHYSICS_SYSTEM,
QUICK_BUILD, QUICK_BUILD,
SWITCH, SWITCH,
ZONE_CONTROL, // Minigame MINI_GAME_CONTROL,
CHANGLING, CHANGLING,
CHOICE_BUILD, CHOICE_BUILD,
PACKAGE, PACKAGE,

View File

@ -21,6 +21,7 @@
#include "ICharInfo.h" #include "ICharInfo.h"
#include "IAccounts.h" #include "IAccounts.h"
#include "IActivityLog.h" #include "IActivityLog.h"
#include "IIgnoreList.h"
#include "IAccountsRewardCodes.h" #include "IAccountsRewardCodes.h"
namespace sql { namespace sql {
@ -39,7 +40,7 @@ class GameDatabase :
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports, public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
public IAccounts, public IActivityLog, public IAccountsRewardCodes { public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
public: public:
virtual ~GameDatabase() = default; virtual ~GameDatabase() = default;
// TODO: These should be made private. // TODO: These should be made private.

View File

@ -0,0 +1,20 @@
#ifndef __IIGNORELIST__H__
#define __IIGNORELIST__H__
#include <cstdint>
#include <string>
#include <vector>
class IIgnoreList {
public:
struct Info {
std::string name;
uint32_t id;
};
virtual std::vector<Info> GetIgnoreList(const uint32_t playerId) = 0;
virtual void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0;
virtual void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0;
};
#endif //!__IIGNORELIST__H__

View File

@ -103,6 +103,9 @@ public:
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override; std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override; std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override; std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override; std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
private: private:

View File

@ -7,6 +7,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"CharXml.cpp" "CharXml.cpp"
"CommandLog.cpp" "CommandLog.cpp"
"Friends.cpp" "Friends.cpp"
"IgnoreList.cpp"
"Leaderboard.cpp" "Leaderboard.cpp"
"Mail.cpp" "Mail.cpp"
"MigrationHistory.cpp" "MigrationHistory.cpp"

View File

@ -0,0 +1,22 @@
#include "MySQLDatabase.h"
std::vector<IIgnoreList::Info> MySQLDatabase::GetIgnoreList(const uint32_t playerId) {
auto result = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId);
std::vector<IIgnoreList::Info> ignoreList;
ignoreList.reserve(result->rowsCount());
while (result->next()) {
ignoreList.push_back(IIgnoreList::Info{ result->getString("name").c_str(), result->getUInt("ignore_id") });
}
return ignoreList;
}
void MySQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
ExecuteInsert("INSERT IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId);
}
void MySQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId);
}

View File

@ -62,7 +62,7 @@
#include "ModelComponent.h" #include "ModelComponent.h"
#include "ZCompression.h" #include "ZCompression.h"
#include "PetComponent.h" #include "PetComponent.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
@ -76,7 +76,7 @@
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "eReplicaPacketType.h" #include "eReplicaPacketType.h"
#include "ZoneControlComponent.h" #include "MiniGameControlComponent.h"
#include "RacingStatsComponent.h" #include "RacingStatsComponent.h"
#include "CollectibleComponent.h" #include "CollectibleComponent.h"
#include "ItemComponent.h" #include "ItemComponent.h"
@ -217,8 +217,8 @@ void Entity::Initialize() {
AddComponent<PetComponent>(petComponentId); AddComponent<PetComponent>(petComponentId);
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ZONE_CONTROL) > 0) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) {
AddComponent<ZoneControlComponent>(); AddComponent<MiniGameControlComponent>();
} }
uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE); uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
@ -299,10 +299,10 @@ void Entity::Initialize() {
AddComponent<PhantomPhysicsComponent>()->SetPhysicsEffectActive(false); AddComponent<PhantomPhysicsComponent>()->SetPhysicsEffectActive(false);
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VEHICLE_PHYSICS) > 0) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS) > 0) {
auto* vehiclePhysicsComponent = AddComponent<VehiclePhysicsComponent>(); auto* havokVehiclePhysicsComponent = AddComponent<HavokVehiclePhysicsComponent>();
vehiclePhysicsComponent->SetPosition(m_DefaultPosition); havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition);
vehiclePhysicsComponent->SetRotation(m_DefaultRotation); havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation);
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) {
@ -554,6 +554,12 @@ void Entity::Initialize() {
Loot::CacheMatrix(activityID); Loot::CacheMatrix(activityID);
} }
const auto timeBeforeSmash = GetVar<float>(u"tmeSmsh");
if (timeBeforeSmash > 0) {
rebuildComponent->SetTimeBeforeSmash(timeBeforeSmash);
}
const auto compTime = GetVar<float>(u"compTime"); const auto compTime = GetVar<float>(u"compTime");
if (compTime > 0) { if (compTime > 0) {
@ -738,7 +744,7 @@ void Entity::Initialize() {
!HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && !HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) &&
!HasComponent(eReplicaComponentType::PROPERTY) && !HasComponent(eReplicaComponentType::PROPERTY) &&
!HasComponent(eReplicaComponentType::RACING_CONTROL) && !HasComponent(eReplicaComponentType::RACING_CONTROL) &&
!HasComponent(eReplicaComponentType::VEHICLE_PHYSICS) !HasComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS)
) )
//if (HasComponent(eReplicaComponentType::BASE_COMBAT_AI)) //if (HasComponent(eReplicaComponentType::BASE_COMBAT_AI))
{ {
@ -1017,9 +1023,9 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate); rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate);
} }
VehiclePhysicsComponent* vehiclePhysicsComponent; HavokVehiclePhysicsComponent* havokVehiclePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) { if (TryGetComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS, havokVehiclePhysicsComponent)) {
vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); havokVehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
PhantomPhysicsComponent* phantomPhysicsComponent; PhantomPhysicsComponent* phantomPhysicsComponent;
@ -1191,9 +1197,9 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
} }
} }
ZoneControlComponent* zoneControlComponent; MiniGameControlComponent* miniGameControlComponent;
if (TryGetComponent(eReplicaComponentType::ZONE_CONTROL, zoneControlComponent)) { if (TryGetComponent(eReplicaComponentType::MINI_GAME_CONTROL, miniGameControlComponent)) {
zoneControlComponent->Serialize(outBitStream, bIsInitialUpdate); miniGameControlComponent->Serialize(outBitStream, bIsInitialUpdate);
} }
// BBB Component, unused currently // BBB Component, unused currently
@ -1503,7 +1509,7 @@ void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u
destroyableComponent->Smash(source, killType, deathType); destroyableComponent->Smash(source, killType, deathType);
} }
void Entity::Kill(Entity* murderer) { void Entity::Kill(Entity* murderer, const eKillType killType) {
if (!m_PlayerIsReadyForUpdates) return; if (!m_PlayerIsReadyForUpdates) return;
for (const auto& cb : m_DieCallbacks) { for (const auto& cb : m_DieCallbacks) {
@ -1527,7 +1533,7 @@ void Entity::Kill(Entity* murderer) {
bool waitForDeathAnimation = false; bool waitForDeathAnimation = false;
if (destroyableComponent) { if (destroyableComponent) {
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0; waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
} }
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction! // Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
@ -1840,7 +1846,7 @@ const NiPoint3& Entity::GetPosition() const {
return simple->GetPosition(); return simple->GetPosition();
} }
auto* vehicel = GetComponent<VehiclePhysicsComponent>(); auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) { if (vehicel != nullptr) {
return vehicel->GetPosition(); return vehicel->GetPosition();
@ -1868,7 +1874,7 @@ const NiQuaternion& Entity::GetRotation() const {
return simple->GetRotation(); return simple->GetRotation();
} }
auto* vehicel = GetComponent<VehiclePhysicsComponent>(); auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) { if (vehicel != nullptr) {
return vehicel->GetRotation(); return vehicel->GetRotation();
@ -1896,7 +1902,7 @@ void Entity::SetPosition(NiPoint3 position) {
simple->SetPosition(position); simple->SetPosition(position);
} }
auto* vehicel = GetComponent<VehiclePhysicsComponent>(); auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) { if (vehicel != nullptr) {
vehicel->SetPosition(position); vehicel->SetPosition(position);
@ -1924,7 +1930,7 @@ void Entity::SetRotation(NiQuaternion rotation) {
simple->SetRotation(rotation); simple->SetRotation(rotation);
} }
auto* vehicel = GetComponent<VehiclePhysicsComponent>(); auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) { if (vehicel != nullptr) {
vehicel->SetRotation(rotation); vehicel->SetRotation(rotation);

View File

@ -210,7 +210,7 @@ public:
void RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled); void RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled);
void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u""); void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"");
void Kill(Entity* murderer = nullptr); void Kill(Entity* murderer = nullptr, const eKillType killType = eKillType::SILENT);
void AddRebuildCompleteCallback(const std::function<void(Entity* user)>& callback) const; void AddRebuildCompleteCallback(const std::function<void(Entity* user)>& callback) const;
void AddCollisionPhantomCallback(const std::function<void(Entity* target)>& callback); void AddCollisionPhantomCallback(const std::function<void(Entity* target)>& callback);
void AddDieCallback(const std::function<void()>& callback); void AddDieCallback(const std::function<void()>& callback);

View File

@ -110,3 +110,7 @@ void User::UserOutOfSync() {
Game::server->Disconnect(this->m_SystemAddress, eServerDisconnectIdentifiers::PLAY_SCHEDULE_TIME_DONE); Game::server->Disconnect(this->m_SystemAddress, eServerDisconnectIdentifiers::PLAY_SCHEDULE_TIME_DONE);
} }
} }
void User::UpdateBestFriendValue(const std::string_view playerName, const bool newValue) {
m_IsBestFriendMap[playerName.data()] = newValue;
}

View File

@ -43,8 +43,8 @@ public:
bool GetLastChatMessageApproved() { return m_LastChatMessageApproved; } bool GetLastChatMessageApproved() { return m_LastChatMessageApproved; }
void SetLastChatMessageApproved(bool approved) { m_LastChatMessageApproved = approved; } void SetLastChatMessageApproved(bool approved) { m_LastChatMessageApproved = approved; }
std::unordered_map<std::string, bool> GetIsBestFriendMap() { return m_IsBestFriendMap; } const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; }
void SetIsBestFriendMap(std::unordered_map<std::string, bool> mapToSet) { m_IsBestFriendMap = mapToSet; } void UpdateBestFriendValue(const std::string_view playerName, const bool newValue);
bool GetIsMuted() const; bool GetIsMuted() const;

View File

@ -271,60 +271,58 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
} }
std::stringstream xml; std::stringstream xml;
xml << "<obj v=\"1\"><mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor; xml << "<obj v=\"1\">";
xml << "<mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor;
xml << "\" hdc=\"0\" cd=\"" << shirtStyle << "\" lh=\"" << lh << "\" rh=\"" << rh << "\" es=\"" << eyebrows << "\" "; xml << "\" hdc=\"0\" cd=\"" << shirtStyle << "\" lh=\"" << lh << "\" rh=\"" << rh << "\" es=\"" << eyebrows << "\" ";
xml << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>"; xml << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>";
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" "; xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" "; xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>"; xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\"></char>";
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>"; xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">"; xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">";
std::string xmlSave1 = xml.str();
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforshirt) { LWOOBJID lwoidforshirt = ObjectIDManager::GenerateRandomObjectID();
std::stringstream xml2; LWOOBJID lwoidforpants;
LWOOBJID lwoidforshirt = idforshirt; do {
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER); lwoidforpants = ObjectIDManager::GenerateRandomObjectID();
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT); } while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants
xml2 << xmlSave1 << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
std::string xmlSave2 = xml2.str(); GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforpants) { xml << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
LWOOBJID lwoidforpants = idforpants; xml << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
std::stringstream xml3; xml << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
xml3 << xmlSave2 << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>"; //Check to see if our name was pre-approved:
bool nameOk = IsNamePreapproved(name);
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
//Check to see if our name was pre-approved: std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName;
bool nameOk = IsNamePreapproved(name); std::string pendingName = !name.empty() && !nameOk ? name : "";
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName; ICharInfo::Info info;
std::string pendingName = !name.empty() && !nameOk ? name : ""; info.name = nameToAssign;
info.pendingName = pendingName;
info.id = objectID;
info.accountId = u->GetAccountID();
ICharInfo::Info info; Database::Get()->InsertNewCharacter(info);
info.name = nameToAssign;
info.pendingName = pendingName;
info.id = objectID;
info.accountId = u->GetAccountID();
Database::Get()->InsertNewCharacter(info); //Now finally insert our character xml:
Database::Get()->InsertCharacterXml(objectID, xml.str());
//Now finally insert our character xml: WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
Database::Get()->InsertCharacterXml(objectID, xml3.str()); UserManager::RequestCharacterList(sysAddr);
});
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
});
});
});
} }
void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) { void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) {
@ -403,7 +401,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
return; return;
} }
if (Database::Get()->GetCharacterInfo(newName)) { if (!Database::Get()->GetCharacterInfo(newName)) {
if (IsNamePreapproved(newName)) { if (IsNamePreapproved(newName)) {
Database::Get()->SetCharacterName(charID, newName); Database::Get()->SetCharacterName(charID, newName);
LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str()); LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str());
@ -462,16 +460,18 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
try { try {
std::string shirtQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"character create shirt\" AND icc.color1 == "; auto stmt = CDClientDatabase::CreatePreppedStmt(
shirtQuery += std::to_string(shirtColor); "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?"
shirtQuery += " AND icc.decal == "; );
shirtQuery = shirtQuery + std::to_string(shirtStyle); stmt.bind(1, "character create shirt");
auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery); stmt.bind(2, static_cast<int>(shirtColor));
auto shirtLOT = tableData.getIntField(0, -1); stmt.bind(3, static_cast<int>(shirtStyle));
auto tableData = stmt.execQuery();
auto shirtLOT = tableData.getIntField(0, 4069);
tableData.finalize(); tableData.finalize();
return shirtLOT; return shirtLOT;
} catch (const std::exception&) { } catch (const std::exception& ex) {
LOG("Failed to execute query! Using backup..."); LOG("Could not look up shirt %i %i: %s", shirtColor, shirtStyle, ex.what());
// in case of no shirt found in CDServer, return problematic red vest. // in case of no shirt found in CDServer, return problematic red vest.
return 4069; return 4069;
} }
@ -479,14 +479,17 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
uint32_t FindCharPantsID(uint32_t pantsColor) { uint32_t FindCharPantsID(uint32_t pantsColor) {
try { try {
std::string pantsQuery = "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == \"cc pants\" AND icc.color1 == "; auto stmt = CDClientDatabase::CreatePreppedStmt(
pantsQuery += std::to_string(pantsColor); "select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?"
auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery); );
auto pantsLOT = tableData.getIntField(0, -1); stmt.bind(1, "cc pants");
stmt.bind(2, static_cast<int>(pantsColor));
auto tableData = stmt.execQuery();
auto pantsLOT = tableData.getIntField(0, 2508);
tableData.finalize(); tableData.finalize();
return pantsLOT; return pantsLOT;
} catch (const std::exception&) { } catch (const std::exception& ex) {
LOG("Failed to execute query! Using backup..."); LOG("Could not look up pants %i: %s", pantsColor, ex.what());
// in case of no pants color found in CDServer, return red pants. // in case of no pants color found in CDServer, return red pants.
return 2508; return 2508;
} }

View File

@ -15,7 +15,7 @@ void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
if (buffComponent == nullptr) return; if (buffComponent == nullptr) return;
buffComponent->ApplyBuff(m_BuffId, m_Duration, context->originator, addImmunity, cancelOnDamaged, cancelOnDeath, buffComponent->ApplyBuff(m_BuffId, m_Duration, context->originator, addImmunity, cancelOnDamaged, cancelOnDeath,
cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone); cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, m_ApplyOnTeammates);
} }
void ApplyBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) { void ApplyBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
@ -45,4 +45,5 @@ void ApplyBuffBehavior::Load() {
cancelOnUi = GetBoolean("cancel_on_ui"); cancelOnUi = GetBoolean("cancel_on_ui");
cancelOnUnequip = GetBoolean("cancel_on_unequip"); cancelOnUnequip = GetBoolean("cancel_on_unequip");
cancelOnZone = GetBoolean("cancel_on_zone"); cancelOnZone = GetBoolean("cancel_on_zone");
m_ApplyOnTeammates = GetBoolean("apply_on_teammates");
} }

View File

@ -31,4 +31,6 @@ public:
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override; void Load() override;
private:
bool m_ApplyOnTeammates;
}; };

View File

@ -38,10 +38,12 @@ void JetPackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit
} }
void JetPackBehavior::Load() { void JetPackBehavior::Load() {
this->m_WarningEffectID = GetInt("warning_effect_id"); this->m_WarningEffectID = GetInt("warning_effect_id", -1);
this->m_Airspeed = GetFloat("airspeed"); this->m_Airspeed = GetFloat("airspeed", 10);
this->m_MaxAirspeed = GetFloat("max_airspeed"); this->m_MaxAirspeed = GetFloat("max_airspeed", 15);
this->m_VerticalVelocity = GetFloat("vertical_velocity"); this->m_VerticalVelocity = GetFloat("vertical_velocity", 1);
this->m_EnableHover = GetBoolean("enable_hover"); this->m_EnableHover = GetBoolean("enable_hover", false);
// TODO: Implement proper jetpack checks, so we can set this default to false
this->m_BypassChecks = GetBoolean("bypass_checks", true); this->m_BypassChecks = GetBoolean("bypass_checks", true);
} }

View File

@ -104,7 +104,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
const auto casterPosition = self->GetPosition(); const auto casterPosition = self->GetPosition();
auto reference = self->GetPosition(); //+ m_offset; auto reference = self->GetPosition() + m_offset;
targets.clear(); targets.clear();
@ -114,46 +114,34 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam); context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
for (auto validTarget : validTargets) { for (auto validTarget : validTargets) {
if (targets.size() >= this->m_maxTargets) { if (targets.size() >= this->m_maxTargets) break;
break; if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
}
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) {
continue;
}
if (validTarget->GetIsDead()) continue; if (validTarget->GetIsDead()) continue;
const auto otherPosition = validTarget->GetPosition(); const auto targetPos = validTarget->GetPosition();
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y); // make sure we aren't too high or low in comparison to the targer
const auto heightDifference = std::abs(reference.y - targetPos.y);
/*if (otherPosition.y > reference.y && heightDifference > this->m_upperBound || otherPosition.y < reference.y && heightDifference > this->m_lowerBound) if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
{
continue; continue;
}*/
const auto forward = self->GetRotation().GetForwardVector(); const auto forward = self->GetRotation().GetForwardVector();
// forward is a normalized vector of where the caster is facing. // forward is a normalized vector of where the caster is facing.
// otherPosition is the position of the target. // targetPos is the position of the target.
// reference is the position of the caster. // reference is the position of the caster.
// If we cast a ray forward from the caster, does it come within m_farWidth of the target? // If we cast a ray forward from the caster, does it come within m_farWidth of the target?
const auto distance = Vector3::Distance(reference, otherPosition); const auto distance = Vector3::Distance(reference, targetPos);
if (m_method == 2) { if (m_method == 2) {
NiPoint3 rayPoint = casterPosition + forward * distance; NiPoint3 rayPoint = casterPosition + forward * distance;
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, targetPos) > this->m_farWidth * this->m_farWidth)
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, otherPosition) > this->m_farWidth * this->m_farWidth) {
continue; continue;
}
} }
auto normalized = (reference - otherPosition) / distance; auto normalized = (reference - targetPos) / distance;
const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180); const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180);
if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) { if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) {
targets.push_back(validTarget); targets.push_back(validTarget);
} }
@ -167,33 +155,26 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
}); });
const auto hit = !targets.empty(); const auto hit = !targets.empty();
bitStream->Write(hit); bitStream->Write(hit);
if (this->m_checkEnv) { if (this->m_checkEnv) {
const auto blocked = false; // TODO const auto blocked = false; // TODO
bitStream->Write(blocked); bitStream->Write(blocked);
} }
if (hit) { if (hit) {
if (combatAi != nullptr) { if (combatAi) combatAi->LookAt(targets[0]->GetPosition());
combatAi->LookAt(targets[0]->GetPosition());
}
context->foundTarget = true; // We want to continue with this behavior context->foundTarget = true; // We want to continue with this behavior
const auto count = static_cast<uint32_t>(targets.size()); const auto count = static_cast<uint32_t>(targets.size());
bitStream->Write(count); bitStream->Write(count);
for (auto* target : targets) { for (auto* target : targets) {
bitStream->Write(target->GetObjectID()); bitStream->Write(target->GetObjectID());
} }
for (auto* target : targets) { for (auto* target : targets) {
branch.target = target->GetObjectID(); branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch); this->m_action->Calculate(context, bitStream, branch);
} }
} else { } else {
@ -214,8 +195,8 @@ void TacArcBehavior::Load() {
GetFloat("offset_z", 0.0f) GetFloat("offset_z", 0.0f)
); );
this->m_method = GetInt("method", 1); this->m_method = GetInt("method", 1);
this->m_upperBound = GetFloat("upper_bound", 4.4f); this->m_upperBound = std::abs(GetFloat("upper_bound", 4.4f));
this->m_lowerBound = GetFloat("lower_bound", 0.4f); this->m_lowerBound = std::abs(GetFloat("lower_bound", 0.4f));
this->m_usePickedTarget = GetBoolean("use_picked_target", false); this->m_usePickedTarget = GetBoolean("use_picked_target", false);
this->m_useTargetPostion = GetBoolean("use_target_position", false); this->m_useTargetPostion = GetBoolean("use_target_position", false);
this->m_checkEnv = GetBoolean("check_env", false); this->m_checkEnv = GetBoolean("check_env", false);

View File

@ -11,9 +11,21 @@
#include "EntityManager.h" #include "EntityManager.h"
#include "CDClientManager.h" #include "CDClientManager.h"
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
#include "TeamManager.h"
std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{}; std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{};
namespace {
std::map<std::string, std::string> BuffFx = {
{ "overtime", "OTB_" },
{ "max_health", "HEALTH_" },
{ "max_imagination", "IMAGINATION_" },
{ "max_armor", "ARMOR_" },
{ "speed", "SPEED_" },
{ "loot", "LOOT_" }
};
}
BuffComponent::BuffComponent(Entity* parent) : Component(parent) { BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
} }
@ -22,32 +34,38 @@ BuffComponent::~BuffComponent() {
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (!bIsInitialUpdate) return; if (!bIsInitialUpdate) return;
if (m_Buffs.empty()) { outBitStream->Write(!m_Buffs.empty());
outBitStream->Write0(); if (!m_Buffs.empty()) {
} else {
outBitStream->Write1();
outBitStream->Write<uint32_t>(m_Buffs.size()); outBitStream->Write<uint32_t>(m_Buffs.size());
for (const auto& buff : m_Buffs) { for (const auto& [id, buff] : m_Buffs) {
outBitStream->Write<uint32_t>(buff.first); outBitStream->Write<uint32_t>(id);
outBitStream->Write0(); outBitStream->Write(buff.time != 0.0f);
outBitStream->Write0(); if (buff.time != 0.0f) outBitStream->Write(static_cast<uint32_t>(buff.time * 1000.0f));
outBitStream->Write0(); outBitStream->Write(buff.cancelOnDeath);
outBitStream->Write0(); outBitStream->Write(buff.cancelOnZone);
outBitStream->Write0(); outBitStream->Write(buff.cancelOnDamaged);
outBitStream->Write0(); outBitStream->Write(buff.cancelOnRemoveBuff);
outBitStream->Write0(); outBitStream->Write(buff.cancelOnUi);
outBitStream->Write0(); outBitStream->Write(buff.cancelOnLogout);
outBitStream->Write0(); outBitStream->Write(buff.cancelOnUnequip);
outBitStream->Write0(); // Cancel on Damage Absorb Ran Out. Generally false from what I can tell
outBitStream->Write0(); auto* team = TeamManager::Instance()->GetTeam(buff.source);
outBitStream->Write0(); bool addedByTeammate = false;
if (team) {
addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0;
}
outBitStream->Write<uint32_t>(0); outBitStream->Write(addedByTeammate); // Added by teammate. If source is in the same team as the target, this is true. Otherwise, false.
outBitStream->Write(buff.applyOnTeammates);
if (addedByTeammate) outBitStream->Write(buff.source);
outBitStream->Write<uint32_t>(buff.refCount);
} }
} }
outBitStream->Write0(); outBitStream->Write0(); // something to do with immunity buffs?
} }
void BuffComponent::Update(float deltaTime) { void BuffComponent::Update(float deltaTime) {
@ -83,17 +101,55 @@ void BuffComponent::Update(float deltaTime) {
} }
} }
const std::string& GetFxName(const std::string& buffname) {
const auto& toReturn = BuffFx[buffname];
if (toReturn.empty()) {
LOG_DEBUG("No fx name for %s", buffname.c_str());
}
return toReturn;
}
void BuffComponent::ApplyBuffFx(uint32_t buffId, const BuffParameter& buff) {
std::string fxToPlay;
const auto& buffName = GetFxName(buff.name);
if (buffName.empty()) return;
fxToPlay += std::to_string(buffId);
LOG_DEBUG("Playing %s %i", fxToPlay.c_str(), buff.effectId);
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), buff.effectId, u"cast", fxToPlay, LWOOBJID_EMPTY, 1.07f, 1.0f, false);
}
void BuffComponent::RemoveBuffFx(uint32_t buffId, const BuffParameter& buff) {
std::string fxToPlay;
const auto& buffName = GetFxName(buff.name);
if (buffName.empty()) return;
fxToPlay += std::to_string(buffId);
LOG_DEBUG("Stopping %s", fxToPlay.c_str());
GameMessages::SendStopFXEffect(m_Parent, false, fxToPlay);
}
void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOOBJID source, bool addImmunity, void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOOBJID source, bool addImmunity,
bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff,
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) { bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool applyOnTeammates) {
// Prevent buffs from stacking. // Prevent buffs from stacking.
if (HasBuff(id)) { if (HasBuff(id)) {
m_Buffs[id].refCount++;
m_Buffs[id].time = duration;
return; return;
} }
auto* team = TeamManager::Instance()->GetTeam(source);
bool addedByTeammate = false;
if (team) {
addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0;
}
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, (uint32_t)id, GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, (uint32_t)id,
(uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath, (uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone); cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, addedByTeammate, applyOnTeammates);
float tick = 0; float tick = 0;
float stacks = 0; float stacks = 0;
@ -121,17 +177,43 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
buff.stacks = stacks; buff.stacks = stacks;
buff.source = source; buff.source = source;
buff.behaviorID = behaviorID; buff.behaviorID = behaviorID;
buff.cancelOnDamaged = cancelOnDamaged;
buff.cancelOnDeath = cancelOnDeath;
buff.cancelOnLogout = cancelOnLogout;
buff.cancelOnRemoveBuff = cancelOnRemoveBuff;
buff.cancelOnUi = cancelOnUi;
buff.cancelOnUnequip = cancelOnUnequip;
buff.cancelOnZone = cancelOnZone;
buff.refCount = 1;
m_Buffs.emplace(id, buff); m_Buffs.emplace(id, buff);
auto* parent = GetParent();
if (!cancelOnDeath) return;
m_Parent->AddDieCallback([parent, id]() {
LOG_DEBUG("Removing buff %i because parent died", id);
if (!parent) return;
auto* buffComponent = parent->GetComponent<BuffComponent>();
if (buffComponent) buffComponent->RemoveBuff(id, false, false, true);
});
} }
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) { void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity, bool ignoreRefCount) {
const auto& iter = m_Buffs.find(id); const auto& iter = m_Buffs.find(id);
if (iter == m_Buffs.end()) { if (iter == m_Buffs.end()) {
return; return;
} }
if (!ignoreRefCount && !iter->second.cancelOnRemoveBuff) {
iter->second.refCount--;
LOG_DEBUG("refCount for buff %i is now %i", id, iter->second.refCount);
if (iter->second.refCount > 0) {
return;
}
}
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id); GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
m_Buffs.erase(iter); m_Buffs.erase(iter);
@ -146,6 +228,7 @@ bool BuffComponent::HasBuff(int32_t id) {
void BuffComponent::ApplyBuffEffect(int32_t id) { void BuffComponent::ApplyBuffEffect(int32_t id) {
const auto& parameters = GetBuffParameters(id); const auto& parameters = GetBuffParameters(id);
for (const auto& parameter : parameters) { for (const auto& parameter : parameters) {
ApplyBuffFx(id, parameter);
if (parameter.name == "max_health") { if (parameter.name == "max_health") {
const auto maxHealth = parameter.value; const auto maxHealth = parameter.value;
@ -182,6 +265,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
void BuffComponent::RemoveBuffEffect(int32_t id) { void BuffComponent::RemoveBuffEffect(int32_t id) {
const auto& parameters = GetBuffParameters(id); const auto& parameters = GetBuffParameters(id);
for (const auto& parameter : parameters) { for (const auto& parameter : parameters) {
RemoveBuffFx(id, parameter);
if (parameter.name == "max_health") { if (parameter.name == "max_health") {
const auto maxHealth = parameter.value; const auto maxHealth = parameter.value;
@ -251,13 +335,25 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* buffEntry = buffElement->FirstChildElement("b"); auto* buffEntry = buffElement->FirstChildElement("b");
while (buffEntry != nullptr) { while (buffEntry) {
int32_t id = buffEntry->IntAttribute("id"); int32_t id = buffEntry->IntAttribute("id");
float t = buffEntry->FloatAttribute("t"); float t = buffEntry->FloatAttribute("t");
float tk = buffEntry->FloatAttribute("tk"); float tk = buffEntry->FloatAttribute("tk");
float tt = buffEntry->FloatAttribute("tt");
int32_t s = buffEntry->FloatAttribute("s"); int32_t s = buffEntry->FloatAttribute("s");
LWOOBJID sr = buffEntry->Int64Attribute("sr"); LWOOBJID sr = buffEntry->Int64Attribute("sr");
int32_t b = buffEntry->IntAttribute("b"); int32_t b = buffEntry->IntAttribute("b");
int32_t refCount = buffEntry->IntAttribute("refCount");
bool cancelOnDamaged = buffEntry->BoolAttribute("cancelOnDamaged");
bool cancelOnDeath = buffEntry->BoolAttribute("cancelOnDeath");
bool cancelOnLogout = buffEntry->BoolAttribute("cancelOnLogout");
bool cancelOnRemoveBuff = buffEntry->BoolAttribute("cancelOnRemoveBuff");
bool cancelOnUi = buffEntry->BoolAttribute("cancelOnUi");
bool cancelOnUnequip = buffEntry->BoolAttribute("cancelOnUnequip");
bool cancelOnZone = buffEntry->BoolAttribute("cancelOnZone");
bool applyOnTeammates = buffEntry->BoolAttribute("applyOnTeammates");
Buff buff; Buff buff;
buff.id = id; buff.id = id;
@ -266,6 +362,18 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
buff.stacks = s; buff.stacks = s;
buff.source = sr; buff.source = sr;
buff.behaviorID = b; buff.behaviorID = b;
buff.refCount = refCount;
buff.tickTime = tt;
buff.cancelOnDamaged = cancelOnDamaged;
buff.cancelOnDeath = cancelOnDeath;
buff.cancelOnLogout = cancelOnLogout;
buff.cancelOnRemoveBuff = cancelOnRemoveBuff;
buff.cancelOnUi = cancelOnUi;
buff.cancelOnUnequip = cancelOnUnequip;
buff.cancelOnZone = cancelOnZone;
buff.applyOnTeammates = applyOnTeammates;
m_Buffs.emplace(id, buff); m_Buffs.emplace(id, buff);
@ -288,15 +396,27 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
buffElement->DeleteChildren(); buffElement->DeleteChildren();
} }
for (const auto& buff : m_Buffs) { for (const auto& [id, buff] : m_Buffs) {
auto* buffEntry = doc->NewElement("b"); auto* buffEntry = doc->NewElement("b");
if (buff.cancelOnZone || buff.cancelOnLogout) continue;
buffEntry->SetAttribute("id", buff.first); buffEntry->SetAttribute("id", id);
buffEntry->SetAttribute("t", buff.second.time); buffEntry->SetAttribute("t", buff.time);
buffEntry->SetAttribute("tk", buff.second.tick); buffEntry->SetAttribute("tk", buff.tick);
buffEntry->SetAttribute("s", buff.second.stacks); buffEntry->SetAttribute("tt", buff.tickTime);
buffEntry->SetAttribute("sr", buff.second.source); buffEntry->SetAttribute("s", buff.stacks);
buffEntry->SetAttribute("b", buff.second.behaviorID); buffEntry->SetAttribute("sr", buff.source);
buffEntry->SetAttribute("b", buff.behaviorID);
buffEntry->SetAttribute("refCount", buff.refCount);
buffEntry->SetAttribute("cancelOnDamaged", buff.cancelOnDamaged);
buffEntry->SetAttribute("cancelOnDeath", buff.cancelOnDeath);
buffEntry->SetAttribute("cancelOnLogout", buff.cancelOnLogout);
buffEntry->SetAttribute("cancelOnRemoveBuff", buff.cancelOnRemoveBuff);
buffEntry->SetAttribute("cancelOnUi", buff.cancelOnUi);
buffEntry->SetAttribute("cancelOnUnequip", buff.cancelOnUnequip);
buffEntry->SetAttribute("cancelOnZone", buff.cancelOnZone);
buffEntry->SetAttribute("applyOnTeammates", buff.applyOnTeammates);
buffElement->LinkEndChild(buffEntry); buffElement->LinkEndChild(buffEntry);
} }
@ -309,8 +429,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
return pair->second; return pair->second;
} }
auto query = CDClientDatabase::CreatePreppedStmt( auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
query.bind(1, (int)buffId); query.bind(1, (int)buffId);
auto result = query.execQuery(); auto result = query.execQuery();
@ -321,11 +440,12 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
BuffParameter param; BuffParameter param;
param.buffId = buffId; param.buffId = buffId;
param.name = result.getStringField(1); param.name = result.getStringField("ParameterName");
param.value = result.getFloatField(2); param.value = result.getFloatField("NumberValue");
param.effectId = result.getIntField("EffectID");
if (!result.fieldIsNull(3)) { if (!result.fieldIsNull(3)) {
std::istringstream stream(result.getStringField(3)); std::istringstream stream(result.getStringField("StringValue"));
std::string token; std::string token;
while (std::getline(stream, token, ',')) { while (std::getline(stream, token, ',')) {

View File

@ -14,8 +14,7 @@ class Entity;
/** /**
* Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much * Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much
*/ */
struct BuffParameter struct BuffParameter {
{
int32_t buffId; int32_t buffId;
std::string name; std::string name;
float value; float value;
@ -26,8 +25,7 @@ struct BuffParameter
/** /**
* Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc. * Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc.
*/ */
struct Buff struct Buff {
{
int32_t id = 0; int32_t id = 0;
float time = 0; float time = 0;
float tick = 0; float tick = 0;
@ -35,6 +33,15 @@ struct Buff
int32_t stacks = 0; int32_t stacks = 0;
LWOOBJID source = 0; LWOOBJID source = 0;
int32_t behaviorID = 0; int32_t behaviorID = 0;
bool cancelOnDamaged = false;
bool cancelOnDeath = false;
bool cancelOnLogout = false;
bool cancelOnRemoveBuff = false;
bool cancelOnUi = false;
bool cancelOnUnequip = false;
bool cancelOnZone = false;
bool applyOnTeammates = false;
uint32_t refCount = 0;
}; };
/** /**
@ -74,14 +81,17 @@ public:
*/ */
void ApplyBuff(int32_t id, float duration, LWOOBJID source, bool addImmunity = false, bool cancelOnDamaged = false, void ApplyBuff(int32_t id, float duration, LWOOBJID source, bool addImmunity = false, bool cancelOnDamaged = false,
bool cancelOnDeath = true, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnDeath = true, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true,
bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false); bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false, bool applyOnTeammates = false);
void ApplyBuffFx(uint32_t buffId, const BuffParameter& buffName);
void RemoveBuffFx(uint32_t buffId, const BuffParameter& buffName);
/** /**
* Removes a buff from the parent entity, reversing its effects * Removes a buff from the parent entity, reversing its effects
* @param id the id of the buff to remove * @param id the id of the buff to remove
* @param removeImmunity whether or not to remove immunity on removing the buff * @param removeImmunity whether or not to remove immunity on removing the buff
*/ */
void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false); void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false, bool ignoreRefCount = false);
/** /**
* Returns whether or not the entity has a buff identified by `id` * Returns whether or not the entity has a buff identified by `id`

View File

@ -43,8 +43,8 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"SoundTriggerComponent.cpp" "SoundTriggerComponent.cpp"
"SwitchComponent.cpp" "SwitchComponent.cpp"
"TriggerComponent.cpp" "TriggerComponent.cpp"
"VehiclePhysicsComponent.cpp" "HavokVehiclePhysicsComponent.cpp"
"VendorComponent.cpp" "VendorComponent.cpp"
"ZoneControlComponent.cpp" "MiniGameControlComponent.cpp"
PARENT_SCOPE PARENT_SCOPE
) )

View File

@ -10,7 +10,7 @@
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include "ControllablePhysicsComponent.h" #include "ControllablePhysicsComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "Item.h" #include "Item.h"
#include "Amf3.h" #include "Amf3.h"

View File

@ -796,7 +796,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
} }
} }
m_Parent->Kill(owner); m_Parent->Kill(owner, killType);
} }
void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) { void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {

View File

@ -1,7 +1,7 @@
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) { HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
m_Velocity = NiPoint3::ZERO; m_Velocity = NiPoint3::ZERO;
m_AngularVelocity = NiPoint3::ZERO; m_AngularVelocity = NiPoint3::ZERO;
m_IsOnGround = true; m_IsOnGround = true;
@ -12,45 +12,45 @@ VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : PhysicsCompon
m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7); m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7);
} }
void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) { void HavokVehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (vel == m_Velocity) return; if (vel == m_Velocity) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_Velocity = vel; m_Velocity = vel;
} }
void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { void HavokVehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (vel == m_AngularVelocity) return; if (vel == m_AngularVelocity) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_AngularVelocity = vel; m_AngularVelocity = vel;
} }
void VehiclePhysicsComponent::SetIsOnGround(bool val) { void HavokVehiclePhysicsComponent::SetIsOnGround(bool val) {
if (val == m_IsOnGround) return; if (val == m_IsOnGround) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_IsOnGround = val; m_IsOnGround = val;
} }
void VehiclePhysicsComponent::SetIsOnRail(bool val) { void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) {
if (val == m_IsOnRail) return; if (val == m_IsOnRail) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_IsOnRail = val; m_IsOnRail = val;
} }
void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) { void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
if (m_RemoteInputInfo == remoteInputInfo) return; if (m_RemoteInputInfo == remoteInputInfo) return;
this->m_RemoteInputInfo = remoteInputInfo; this->m_RemoteInputInfo = remoteInputInfo;
m_DirtyRemoteInput = true; m_DirtyRemoteInput = true;
} }
void VehiclePhysicsComponent::SetDirtyVelocity(bool val) { void HavokVehiclePhysicsComponent::SetDirtyVelocity(bool val) {
m_DirtyVelocity = val; m_DirtyVelocity = val;
} }
void VehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) { void HavokVehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val; m_DirtyAngularVelocity = val;
} }
void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
if (bIsInitialUpdate || m_DirtyPosition) { if (bIsInitialUpdate || m_DirtyPosition) {
@ -111,7 +111,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write0(); outBitStream->Write0();
} }
void VehiclePhysicsComponent::Update(float deltaTime) { void HavokVehiclePhysicsComponent::Update(float deltaTime) {
if (m_SoftUpdate > 5) { if (m_SoftUpdate > 5) {
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
m_SoftUpdate = 0; m_SoftUpdate = 0;

View File

@ -6,6 +6,13 @@
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
struct RemoteInputInfo { struct RemoteInputInfo {
RemoteInputInfo() {
m_RemoteInputX = 0;
m_RemoteInputY = 0;
m_IsPowersliding = false;
m_IsModified = false;
}
void operator=(const RemoteInputInfo& other) { void operator=(const RemoteInputInfo& other) {
m_RemoteInputX = other.m_RemoteInputX; m_RemoteInputX = other.m_RemoteInputX;
m_RemoteInputY = other.m_RemoteInputY; m_RemoteInputY = other.m_RemoteInputY;
@ -26,11 +33,11 @@ struct RemoteInputInfo {
/** /**
* Physics component for vehicles. * Physics component for vehicles.
*/ */
class VehiclePhysicsComponent : public PhysicsComponent { class HavokVehiclePhysicsComponent : public PhysicsComponent {
public: public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VEHICLE_PHYSICS; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
VehiclePhysicsComponent(Entity* parentEntity); HavokVehiclePhysicsComponent(Entity* parentEntity);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;

View File

@ -19,7 +19,7 @@
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "PropertyManagementComponent.h" #include "PropertyManagementComponent.h"
@ -981,7 +981,7 @@ void InventoryComponent::HandlePossession(Item* item) {
auto* mount = Game::entityManager->CreateEntity(info, nullptr, m_Parent); auto* mount = Game::entityManager->CreateEntity(info, nullptr, m_Parent);
// Check to see if the mount is a vehicle, if so, flip it // Check to see if the mount is a vehicle, if so, flip it
auto* vehicleComponent = mount->GetComponent<VehiclePhysicsComponent>(); auto* vehicleComponent = mount->GetComponent<HavokVehiclePhysicsComponent>();
if (vehicleComponent) characterComponent->SetIsRacing(true); if (vehicleComponent) characterComponent->SetIsRacing(true);
// Setup the destroyable stats // Setup the destroyable stats

View File

@ -0,0 +1,5 @@
#include "MiniGameControlComponent.h"
void MiniGameControlComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
outBitStream->Write<uint32_t>(0x40000000);
}

View File

@ -0,0 +1,15 @@
#ifndef __MINIGAMECONTROLCOMPONENT__H__
#define __MINIGAMECONTROLCOMPONENT__H__
#include "Component.h"
#include "eReplicaComponentType.h"
class MiniGameControlComponent final : public Component {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL;
MiniGameControlComponent(Entity* parent) : Component(parent) {}
void Serialize(RakNet::BitStream* outBitStream, bool isConstruction);
};
#endif //!__MINIGAMECONTROLCOMPONENT__H__

View File

@ -0,0 +1,5 @@
#include "MinigameComponent.h"
void MinigameComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
outBitStream->Write<uint32_t>(0x40000000);
}

View File

@ -145,8 +145,13 @@ void MissionComponent::RemoveMission(uint32_t missionId) {
} }
void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID associate, const std::string& targets, int32_t count, bool ignoreAchievements) { void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID associate, const std::string& targets, int32_t count, bool ignoreAchievements) {
for (const auto& pair : m_Missions) { std::vector<uint32_t> acceptedAchievements;
auto* mission = pair.second; if (count > 0 && !ignoreAchievements) {
acceptedAchievements = LookForAchievements(type, value, true, associate, targets, count);
}
for (const auto& [id, mission] : m_Missions) {
if (!mission || std::find(acceptedAchievements.begin(), acceptedAchievements.end(), mission->GetMissionId()) != acceptedAchievements.end()) continue;
if (mission->IsAchievement() && ignoreAchievements) continue; if (mission->IsAchievement() && ignoreAchievements) continue;
@ -154,10 +159,6 @@ void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID a
mission->Progress(type, value, associate, targets, count); mission->Progress(type, value, associate, targets, count);
} }
if (count > 0 && !ignoreAchievements) {
LookForAchievements(type, value, true, associate, targets, count);
}
} }
void MissionComponent::ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission) { void MissionComponent::ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission) {
@ -282,12 +283,12 @@ bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) {
#define MISSION_NEW_METHOD #define MISSION_NEW_METHOD
bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) { const std::vector<uint32_t> MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) {
#ifdef MISSION_NEW_METHOD #ifdef MISSION_NEW_METHOD
// Query for achievments, using the cache // Query for achievments, using the cache
const auto& result = QueryAchievements(type, value, targets); const auto& result = QueryAchievements(type, value, targets);
bool any = false; std::vector<uint32_t> acceptedAchievements;
for (const uint32_t missionID : result) { for (const uint32_t missionID : result) {
// Check if we already have this achievement // Check if we already have this achievement
@ -309,7 +310,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
instance->Accept(); instance->Accept();
any = true; acceptedAchievements.push_back(missionID);
if (progress) { if (progress) {
// Progress mission to bring it up to speed // Progress mission to bring it up to speed
@ -317,7 +318,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
} }
} }
return any; return acceptedAchievements;
#else #else
auto* missionTasksTable = CDClientManager::Instance().GetTable<CDMissionTasksTable>(); auto* missionTasksTable = CDClientManager::Instance().GetTable<CDMissionTasksTable>();
auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>(); auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>();
@ -326,7 +327,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
return entry.taskType == static_cast<unsigned>(type); return entry.taskType == static_cast<unsigned>(type);
}); });
auto any = false; std::vector<uint32_t> acceptedAchievements;
for (const auto& task : tasks) { for (const auto& task : tasks) {
if (GetMission(task.id) != nullptr) { if (GetMission(task.id) != nullptr) {
@ -380,14 +381,14 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
instance->Accept(); instance->Accept();
any = true; acceptedAchievements.push_back(mission.id);
if (progress) { if (progress) {
instance->Progress(type, value, associate, targets, count); instance->Progress(type, value, associate, targets, count);
} }
} }
return any; return acceptedAchievements;
#endif #endif
} }
@ -499,7 +500,7 @@ bool MissionComponent::RequiresItem(const LOT lot) {
const auto required = LookForAchievements(eMissionTaskType::GATHER, lot, false); const auto required = LookForAchievements(eMissionTaskType::GATHER, lot, false);
return required; return !required.empty();
} }

View File

@ -141,7 +141,7 @@ public:
* @param count the number of values to progress by (differs by task type) * @param count the number of values to progress by (differs by task type)
* @return true if a achievement was accepted, false otherwise * @return true if a achievement was accepted, false otherwise
*/ */
bool LookForAchievements(eMissionTaskType type, int32_t value, bool progress = true, LWOOBJID associate = LWOOBJID_EMPTY, const std::string& targets = "", int32_t count = 1); const std::vector<uint32_t> LookForAchievements(eMissionTaskType type, int32_t value, bool progress = true, LWOOBJID associate = LWOOBJID_EMPTY, const std::string& targets = "", int32_t count = 1);
/** /**
* Checks if there's a mission active that requires the collection of the specified LOT * Checks if there's a mission active that requires the collection of the specified LOT

View File

@ -164,6 +164,8 @@ public:
LWOCLONEID GetCloneId() { return clone_Id; }; LWOCLONEID GetCloneId() { return clone_Id; };
LWOOBJID GetId() const noexcept { return propertyId; }
private: private:
/** /**
* This * This

View File

@ -17,7 +17,7 @@
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "eRacingTaskParam.h" #include "eRacingTaskParam.h"
#include "Spawner.h" #include "Spawner.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "dServer.h" #include "dServer.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "dConfig.h" #include "dConfig.h"
@ -51,7 +51,7 @@ RacingControlComponent::RacingControlComponent(Entity* parent)
m_MainWorld = 1200; m_MainWorld = 1200;
const auto worldID = Game::server->GetZoneID(); const auto worldID = Game::server->GetZoneID();
if (Game::zoneManager->CheckIfAccessibleZone((worldID/10)*10)) m_MainWorld = (worldID/10)*10; if (Game::zoneManager->CheckIfAccessibleZone((worldID / 10) * 10)) m_MainWorld = (worldID / 10) * 10;
m_ActivityID = 42; m_ActivityID = 42;
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>(); CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
@ -72,7 +72,7 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
// If the race has already started, send the player back to the main world. // If the race has already started, send the player back to the main world.
if (m_Loaded || !vehicle) { if (m_Loaded || !vehicle) {
auto* playerInstance = dynamic_cast<Player*>(player); auto* playerInstance = dynamic_cast<Player*>(player);
if(playerInstance){ if (playerInstance) {
playerInstance->SendToZone(m_MainWorld); playerInstance->SendToZone(m_MainWorld);
} }
return; return;
@ -106,7 +106,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player,
if (item == nullptr) { if (item == nullptr) {
LOG("Failed to find item"); LOG("Failed to find item");
auto* playerInstance = dynamic_cast<Player*>(player); auto* playerInstance = dynamic_cast<Player*>(player);
if(playerInstance){ if (playerInstance) {
m_LoadedPlayers--; m_LoadedPlayers--;
playerInstance->SendToZone(m_MainWorld); playerInstance->SendToZone(m_MainWorld);
} }
@ -213,6 +213,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player,
0, 0,
0, 0,
0 }); 0 });
m_AllPlayersReady = false;
} }
// Construct and serialize everything when done. // Construct and serialize everything when done.
@ -330,7 +331,7 @@ void RacingControlComponent::OnRequestDie(Entity* player) {
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live. // Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination); if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination);
Game::entityManager->SerializeEntity(vehicle); Game::entityManager->SerializeEntity(vehicle);
}); });
auto* characterComponent = player->GetComponent<CharacterComponent>(); auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
@ -384,11 +385,11 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
// Calculate the score, different loot depending on player count // Calculate the score, different loot depending on player count
auto playersRating = m_LoadedPlayers; auto playersRating = m_LoadedPlayers;
if(m_LoadedPlayers == 1 && m_SoloRacing) { if (m_LoadedPlayers == 1 && m_SoloRacing) {
playersRating *= 2; playersRating *= 2;
} }
const auto score = playersRating * 10 + data->finished; const auto score = playersRating * 10 + data->finished;
Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score); Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score);
// Giving rewards // Giving rewards
@ -436,64 +437,82 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// BEGIN Scripted Activity // BEGIN Scripted Activity
outBitStream->Write1(); outBitStream->Write1();
outBitStream->Write(static_cast<uint32_t>(m_RacingPlayers.size())); outBitStream->Write(static_cast<uint32_t>(m_RacingPlayers.size()));
for (const auto& player : m_RacingPlayers) { for (const auto& player : m_RacingPlayers) {
outBitStream->Write(player.playerID); outBitStream->Write(player.playerID);
for (int i = 0; i < 10; i++) { outBitStream->Write(player.data[0]);
outBitStream->Write(player.data[i]); if (player.finished != 0) outBitStream->Write<float>(player.raceTime);
} else outBitStream->Write(player.data[1]);
if (player.finished != 0) outBitStream->Write<float>(player.bestLapTime);
else outBitStream->Write(player.data[2]);
if (player.finished == 1) outBitStream->Write<float>(1.0f);
else outBitStream->Write(player.data[3]);
outBitStream->Write(player.data[4]);
outBitStream->Write(player.data[5]);
outBitStream->Write(player.data[6]);
outBitStream->Write(player.data[7]);
outBitStream->Write(player.data[8]);
outBitStream->Write(player.data[9]);
} }
// END Scripted Activity // END Scripted Activity
outBitStream->Write1(); // Dirty? outBitStream->Write1();
outBitStream->Write(static_cast<uint16_t>(m_RacingPlayers.size())); outBitStream->Write(static_cast<uint16_t>(m_RacingPlayers.size()));
outBitStream->Write(!m_RacingPlayers.empty()); outBitStream->Write(!m_AllPlayersReady);
if (!m_RacingPlayers.empty()) { if (!m_AllPlayersReady) {
int32_t numReady = 0;
for (const auto& player : m_RacingPlayers) { for (const auto& player : m_RacingPlayers) {
outBitStream->Write1(); // Has more date outBitStream->Write1(); // Has more player data
outBitStream->Write(player.playerID); outBitStream->Write(player.playerID);
outBitStream->Write(player.vehicleID); outBitStream->Write(player.vehicleID);
outBitStream->Write(player.playerIndex); outBitStream->Write(player.playerIndex);
outBitStream->Write(player.playerLoaded); outBitStream->Write(player.playerLoaded);
if (player.playerLoaded) numReady++;
} }
outBitStream->Write0(); // No more data outBitStream->Write0(); // No more data
if (numReady == m_RacingPlayers.size()) m_AllPlayersReady = true;
} }
outBitStream->Write(!m_RacingPlayers.empty()); outBitStream->Write(!m_RacingPlayers.empty());
if (!m_RacingPlayers.empty()) { if (!m_RacingPlayers.empty()) {
for (const auto& player : m_RacingPlayers) { for (const auto& player : m_RacingPlayers) {
if (player.finished == 0) continue;
outBitStream->Write1(); // Has more date outBitStream->Write1(); // Has more date
outBitStream->Write(player.playerID); outBitStream->Write(player.playerID);
outBitStream->Write<uint32_t>(0); outBitStream->Write(player.finished);
} }
outBitStream->Write0(); // No more data outBitStream->Write0(); // No more data
} }
outBitStream->Write1(); // Dirty? outBitStream->Write(bIsInitialUpdate);
if (bIsInitialUpdate) {
outBitStream->Write(m_RemainingLaps); outBitStream->Write(m_RemainingLaps);
outBitStream->Write(static_cast<uint16_t>(m_PathName.size()));
outBitStream->Write(static_cast<uint16_t>(m_PathName.size())); for (const auto character : m_PathName) {
for (const auto character : m_PathName) { outBitStream->Write(character);
outBitStream->Write(character); }
} }
outBitStream->Write1(); // ??? outBitStream->Write(!m_RacingPlayers.empty());
outBitStream->Write1(); // ??? if (!m_RacingPlayers.empty()) {
for (const auto& player : m_RacingPlayers) {
if (player.finished == 0) continue;
outBitStream->Write1(); // Has more data
outBitStream->Write(player.playerID);
outBitStream->Write<float>(player.bestLapTime);
outBitStream->Write<float>(player.raceTime);
}
outBitStream->Write(m_LeadingPlayer); outBitStream->Write0(); // No more data
outBitStream->Write(m_RaceBestLap); }
outBitStream->Write(m_RaceBestTime);
} }
RacingPlayerInfo* RacingControlComponent::GetPlayerData(LWOOBJID playerID) { RacingPlayerInfo* RacingControlComponent::GetPlayerData(LWOOBJID playerID) {
@ -569,7 +588,7 @@ void RacingControlComponent::Update(float deltaTime) {
LoadPlayerVehicle(player, positionNumber + 1, true); LoadPlayerVehicle(player, positionNumber + 1, true);
m_Loaded = true; Game::entityManager->SerializeEntity(m_Parent);
} }
m_Loaded = true; m_Loaded = true;
@ -757,6 +776,8 @@ void RacingControlComponent::Update(float deltaTime) {
continue; continue;
} }
if (m_Finished != 0) Game::entityManager->SerializeEntity(m_Parent);
// Loop through all the waypoints and see if the player has reached a // Loop through all the waypoints and see if the player has reached a
// new checkpoint // new checkpoint
uint32_t respawnIndex = 0; uint32_t respawnIndex = 0;
@ -849,8 +870,6 @@ void RacingControlComponent::Update(float deltaTime) {
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->TrackRaceCompleted(m_Finished == 1); characterComponent->TrackRaceCompleted(m_Finished == 1);
} }
// TODO: Figure out how to update the GUI leaderboard.
} }
} }
@ -865,28 +884,3 @@ void RacingControlComponent::Update(float deltaTime) {
} }
} }
} }
std::string RacingControlComponent::FormatTimeString(time_t time) {
int32_t min = time / 60;
time -= min * 60;
int32_t sec = time;
std::string minText;
std::string secText;
if (min <= 0) {
minText = "0";
} else {
minText = std::to_string(min);
}
if (sec <= 0) {
secText = "00";
} else if (sec <= 9) {
secText = "0" + std::to_string(sec);
} else {
secText = std::to_string(sec);
}
return minText + ":" + secText + ".00";
}

View File

@ -151,13 +151,6 @@ public:
*/ */
RacingPlayerInfo* GetPlayerData(LWOOBJID playerID); RacingPlayerInfo* GetPlayerData(LWOOBJID playerID);
/**
* Formats a time to a string, currently unused
* @param time the time to format
* @return the time formatted as string
*/
static std::string FormatTimeString(time_t time);
private: private:
/** /**
@ -251,4 +244,5 @@ private:
* Value for message box response to know if we are exiting the race via the activity dialogue * Value for message box response to know if we are exiting the race via the activity dialogue
*/ */
const int32_t m_ActivityExitConfirm = 1; const int32_t m_ActivityExitConfirm = 1;
bool m_AllPlayersReady = false;
}; };

View File

@ -197,18 +197,17 @@ void RebuildComponent::Update(float deltaTime) {
DestroyableComponent* destComp = builder->GetComponent<DestroyableComponent>(); DestroyableComponent* destComp = builder->GetComponent<DestroyableComponent>();
if (!destComp) break; if (!destComp) break;
int newImagination = destComp->GetImagination();
++m_DrainedImagination; ++m_DrainedImagination;
--newImagination; const int32_t imaginationCostRemaining = m_TakeImagination - m_DrainedImagination;
const int32_t newImagination = destComp->GetImagination() - 1;
destComp->SetImagination(newImagination); destComp->SetImagination(newImagination);
Game::entityManager->SerializeEntity(builder); Game::entityManager->SerializeEntity(builder);
if (newImagination <= 0) { if (newImagination <= 0 && imaginationCostRemaining > 0) {
CancelRebuild(builder, eQuickBuildFailReason::OUT_OF_IMAGINATION, true); CancelRebuild(builder, eQuickBuildFailReason::OUT_OF_IMAGINATION, true);
break; break;
} }
} }
if (m_Timer >= m_CompleteTime && m_DrainedImagination >= m_TakeImagination) { if (m_Timer >= m_CompleteTime && m_DrainedImagination >= m_TakeImagination) {

View File

@ -285,7 +285,7 @@ private:
float m_CompleteTime = 0; float m_CompleteTime = 0;
/** /**
* The imagination that's deducted when compeleting the rebuild * The imagination that's deducted when completing the rebuild
*/ */
int m_TakeImagination = 0; int m_TakeImagination = 0;

View File

@ -1,5 +0,0 @@
#include "ZoneControlComponent.h"
void ZoneControlComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
outBitStream->Write<uint32_t>(0x40000000);
}

View File

@ -1,15 +0,0 @@
#ifndef __ZONECONTROLCOMPONENT__H__
#define __ZONECONTROLCOMPONENT__H__
#include "Component.h"
#include "eReplicaComponentType.h"
class ZoneControlComponent final : public Component {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ZONE_CONTROL;
ZoneControlComponent(Entity* parent) : Component(parent) {}
void Serialize(RakNet::BitStream* outBitStream, bool isConstruction);
};
#endif //!__ZONECONTROLCOMPONENT__H__

View File

@ -71,7 +71,7 @@
#include "MovingPlatformComponent.h" #include "MovingPlatformComponent.h"
#include "PetComponent.h" #include "PetComponent.h"
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
@ -944,14 +944,7 @@ void GameMessages::SendResurrect(Entity* entity) {
destroyableComponent->SetImagination(imaginationToRestore); destroyableComponent->SetImagination(imaginationToRestore);
} }
} }
}); });
auto cont = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS));
if (cont && entity->GetLOT() == 1) {
cont->SetPosition(entity->GetRespawnPosition());
cont->SetRotation(entity->GetRespawnRotation());
}
CBITSTREAM; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
@ -1144,17 +1137,15 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo
bitStream.Write(position.y); bitStream.Write(position.y);
bitStream.Write(position.z); bitStream.Write(position.z);
auto con = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); const bool isNotIdentity = rotation != NiQuaternion::IDENTITY;
if (con) { bitStream.Write(isNotIdentity);
auto rot = con->GetRotation();
bitStream.Write(rot.x);
bitStream.Write(rot.y);
bitStream.Write(rot.z);
bitStream.Write(rot.w);
}
//bitStream.Write(position); if (isNotIdentity) {
//bitStream.Write(rotation); bitStream.Write(rotation.w);
bitStream.Write(rotation.x);
bitStream.Write(rotation.y);
bitStream.Write(rotation.z);
}
SystemAddress sysAddr = entity->GetSystemAddress(); SystemAddress sysAddr = entity->GetSystemAddress();
SEND_PACKET; SEND_PACKET;
@ -4486,7 +4477,7 @@ void GameMessages::SendVehicleNotifyFinishedRace(LWOOBJID objectId, const System
void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration,
bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout,
bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool addedByTeammate, bool applyOnTeammates,
const SystemAddress& sysAddr) { const SystemAddress& sysAddr) {
CBITSTREAM; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
@ -4494,27 +4485,29 @@ void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uin
bitStream.Write(objectID); bitStream.Write(objectID);
bitStream.Write(eGameMessageType::ADD_BUFF); bitStream.Write(eGameMessageType::ADD_BUFF);
bitStream.Write(false); // Added by teammate bitStream.Write(addedByTeammate); // Added by teammate
bitStream.Write(false); // Apply on teammates bitStream.Write(applyOnTeammates); // Apply on teammates
bitStream.Write(false); // Cancel on damage absorb ran out
bitStream.Write(cancelOnDamaged); bitStream.Write(cancelOnDamaged);
bitStream.Write(cancelOnDeath); bitStream.Write(cancelOnDeath);
bitStream.Write(cancelOnLogout); bitStream.Write(cancelOnLogout);
bitStream.Write(false); // Cancel on move bitStream.Write(false); // Cancel on move
bitStream.Write(cancelOnRemoveBuff); bitStream.Write(cancelOnRemoveBuff);
bitStream.Write(cancelOnUi); bitStream.Write(cancelOnUi);
bitStream.Write(cancelOnUnequip); bitStream.Write(cancelOnUnequip);
bitStream.Write(cancelOnZone); bitStream.Write(cancelOnZone);
bitStream.Write(false); // Ignore immunities bitStream.Write(false); // Ignore immunities
bitStream.Write(addImmunity); bitStream.Write(addImmunity);
bitStream.Write(false); // Use ref count bitStream.Write(false); // Use ref count
bitStream.Write(buffID); bitStream.Write(casterID != LWOOBJID_EMPTY);
bitStream.Write(msDuration); if (casterID != LWOOBJID_EMPTY) bitStream.Write(casterID);
bitStream.Write(casterID); bitStream.Write(buffID);
bitStream.Write(casterID);
bitStream.Write(msDuration != 0);
if (msDuration != 0) bitStream.Write(msDuration);
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
SEND_PACKET; SEND_PACKET;
@ -5017,6 +5010,14 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity)
if (emoteID == 0) return; if (emoteID == 0) return;
std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote
CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable<CDEmoteTableTable>();
if (emotes) {
CDEmoteTable* emote = emotes->GetEmote(emoteID);
if (emote) sAnimationName = emote->animationName;
}
RenderComponent::PlayAnimation(entity, sAnimationName);
MissionComponent* missionComponent = entity->GetComponent<MissionComponent>(); MissionComponent* missionComponent = entity->GetComponent<MissionComponent>();
if (!missionComponent) return; if (!missionComponent) return;
@ -5042,14 +5043,6 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity)
missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID());
} }
} }
CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable<CDEmoteTableTable>();
if (emotes) {
CDEmoteTable* emote = emotes->GetEmote(emoteID);
if (emote) sAnimationName = emote->animationName;
}
RenderComponent::PlayAnimation(entity, sAnimationName);
} }
void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
@ -5585,7 +5578,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
std::unique_ptr<sql::PreparedStatement> stmt(Database::Get()->CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)")); std::unique_ptr<sql::PreparedStatement> stmt(Database::Get()->CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)"));
stmt->setUInt64(1, newIdBig); stmt->setUInt64(1, newIdBig);
stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules)); stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules).c_str());
auto* pCharacter = character->GetCharacter(); auto* pCharacter = character->GetCharacter();
pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT); pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT);
stmt->execute(); stmt->execute();

View File

@ -206,7 +206,7 @@ namespace GameMessages {
void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration,
bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true, bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true,
bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnUi = false, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnUi = false,
bool cancelOnUnequip = false, bool cancelOnZone = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); bool cancelOnUnequip = false, bool cancelOnZone = false, bool addedByTeammate = false, bool applyOnTeammates = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
void SendToggleGMInvis(LWOOBJID objectId, bool enabled, const SystemAddress& sysAddr); void SendToggleGMInvis(LWOOBJID objectId, bool enabled, const SystemAddress& sysAddr);

View File

@ -59,7 +59,7 @@
#include "dpShapeSphere.h" #include "dpShapeSphere.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "BuffComponent.h" #include "BuffComponent.h"
#include "SkillComponent.h" #include "SkillComponent.h"
#include "VanityUtilities.h" #include "VanityUtilities.h"
@ -938,9 +938,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable());
if (possassableEntity != nullptr) { if (possassableEntity != nullptr) {
auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>(); auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent<HavokVehiclePhysicsComponent>();
if (vehiclePhysicsComponent) { if (havokVehiclePhysicsComponent) {
vehiclePhysicsComponent->SetPosition(pos); havokVehiclePhysicsComponent->SetPosition(pos);
Game::entityManager->SerializeEntity(possassableEntity); Game::entityManager->SerializeEntity(possassableEntity);
} else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr); } else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr);
} }

View File

@ -27,7 +27,7 @@
#include "Zone.h" #include "Zone.h"
#include "PossessorComponent.h" #include "PossessorComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "dConfig.h" #include "dConfig.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "Database.h" #include "Database.h"
@ -187,17 +187,17 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
if (possessableComponent->GetPossessionType() != ePossessionType::ATTACHED_VISIBLE) updateChar = false; if (possessableComponent->GetPossessionType() != ePossessionType::ATTACHED_VISIBLE) updateChar = false;
} }
auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>(); auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent<HavokVehiclePhysicsComponent>();
if (vehiclePhysicsComponent != nullptr) { if (havokVehiclePhysicsComponent != nullptr) {
vehiclePhysicsComponent->SetPosition(position); havokVehiclePhysicsComponent->SetPosition(position);
vehiclePhysicsComponent->SetRotation(rotation); havokVehiclePhysicsComponent->SetRotation(rotation);
vehiclePhysicsComponent->SetIsOnGround(onGround); havokVehiclePhysicsComponent->SetIsOnGround(onGround);
vehiclePhysicsComponent->SetIsOnRail(onRail); havokVehiclePhysicsComponent->SetIsOnRail(onRail);
vehiclePhysicsComponent->SetVelocity(velocity); havokVehiclePhysicsComponent->SetVelocity(velocity);
vehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); havokVehiclePhysicsComponent->SetDirtyVelocity(velocityFlag);
vehiclePhysicsComponent->SetAngularVelocity(angVelocity); havokVehiclePhysicsComponent->SetAngularVelocity(angVelocity);
vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); havokVehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag);
vehiclePhysicsComponent->SetRemoteInputInfo(remoteInput); havokVehiclePhysicsComponent->SetRemoteInputInfo(remoteInput);
} else { } else {
// Need to get the mount's controllable physics // Need to get the mount's controllable physics
auto* controllablePhysicsComponent = possassableEntity->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = possassableEntity->GetComponent<ControllablePhysicsComponent>();
@ -359,8 +359,8 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa
idOfReceiver = characterIdFetch->id; idOfReceiver = characterIdFetch->id;
} }
} }
const auto& bffMap = user->GetIsBestFriendMap();
if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) { if (bffMap.find(receiver) == bffMap.end() && idOfReceiver != LWOOBJID_EMPTY) {
auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver);
if (bffInfo) { if (bffInfo) {
@ -368,11 +368,9 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa
} }
if (isBestFriend) { if (isBestFriend) {
auto tmpBestFriendMap = user->GetIsBestFriendMap(); user->UpdateBestFriendValue(receiver, true);
tmpBestFriendMap[receiver] = true;
user->SetIsBestFriendMap(tmpBestFriendMap);
} }
} else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().end()) { } else if (bffMap.find(receiver) != bffMap.end()) {
isBestFriend = true; isBestFriend = true;
} }
} }

View File

@ -101,7 +101,7 @@ void BasePropertyServer::BasePlayerLoaded(Entity* self, Entity* player) {
missionComponent->Progress( missionComponent->Progress(
eMissionTaskType::VISIT_PROPERTY, eMissionTaskType::VISIT_PROPERTY,
mapID.GetMapID(), mapID.GetMapID(),
mapID.GetCloneID() PropertyManagementComponent::Instance()->GetId()
); );
} }
} }

View File

@ -18,6 +18,7 @@
#include "PerformanceManager.h" #include "PerformanceManager.h"
#include "Diagnostics.h" #include "Diagnostics.h"
#include "BinaryPathFinder.h" #include "BinaryPathFinder.h"
#include "dPlatforms.h"
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
@ -1284,12 +1285,14 @@ void WorldShutdownProcess(uint32_t zoneId) {
} }
void WorldShutdownSequence() { void WorldShutdownSequence() {
if (Game::shouldShutdown || worldShutdownSequenceComplete) { Game::shouldShutdown = true;
#ifndef DARKFLAME_PLATFORM_WIN32
if (Game::shouldShutdown || worldShutdownSequenceComplete)
#endif
{
return; return;
} }
Game::shouldShutdown = true;
LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID); LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID);
WorldShutdownProcess(Game::server->GetZoneID()); WorldShutdownProcess(Game::server->GetZoneID());
FinalizeShutdown(); FinalizeShutdown();
@ -1302,11 +1305,17 @@ void FinalizeShutdown() {
Metrics::Clear(); Metrics::Clear();
Database::Destroy("WorldServer"); Database::Destroy("WorldServer");
if (Game::chatFilter) delete Game::chatFilter; if (Game::chatFilter) delete Game::chatFilter;
Game::chatFilter = nullptr;
if (Game::zoneManager) delete Game::zoneManager; if (Game::zoneManager) delete Game::zoneManager;
Game::zoneManager = nullptr;
if (Game::server) delete Game::server; if (Game::server) delete Game::server;
Game::server = nullptr;
if (Game::config) delete Game::config; if (Game::config) delete Game::config;
Game::config = nullptr;
if (Game::entityManager) delete Game::entityManager; if (Game::entityManager) delete Game::entityManager;
Game::entityManager = nullptr;
if (Game::logger) delete Game::logger; if (Game::logger) delete Game::logger;
Game::logger = nullptr;
worldShutdownSequenceComplete = true; worldShutdownSequenceComplete = true;

View File

@ -79,8 +79,6 @@ dZoneManager::~dZoneManager() {
delete p.second; delete p.second;
p.second = nullptr; p.second = nullptr;
} }
m_Spawners.erase(p.first);
} }
if (m_WorldConfig) delete m_WorldConfig; if (m_WorldConfig) delete m_WorldConfig;
} }

View File

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS ignore_list (
player_id BIGINT NOT NULL REFERENCES charinfo(id) ON DELETE CASCADE,
ignored_player_id BIGINT NOT NULL REFERENCES charinfo(id) ON DELETE CASCADE,
PRIMARY KEY (player_id, ignored_player_id)
);