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
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
* [Clone this repository](#clone-the-repository)
* [Install dependencies](#install-dependencies)

View File

@ -1,4 +1,5 @@
set(DCHATSERVER_SOURCES
"ChatIgnoreList.cpp"
"ChatPacketHandler.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 "eGameMessageType.h"
extern PlayerContainer playerContainer;
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Get from the packet which player we want to do something with:
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = 0;
inStream.Read(playerID);
auto player = playerContainer.GetPlayerData(playerID);
auto player = Game::playerContainer.GetPlayerData(playerID);
if (!player) return;
auto friendsList = Database::Get()->GetFriendsList(playerID);
@ -43,7 +41,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
fd.friendName = friendData.friendName;
//Now check if they're online:
auto fr = playerContainer.GetPlayerData(fd.friendID);
auto fr = Game::playerContainer.GetPlayerData(fd.friendID);
if (fr) {
fd.isOnline = true;
@ -97,7 +95,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
char isBestFriendRequest{};
inStream.Read(isBestFriendRequest);
auto requestor = playerContainer.GetPlayerData(requestorPlayerID);
auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID);
if (!requestor) {
LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str());
return;
@ -107,7 +105,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN);
return;
};
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName));
std::unique_ptr<PlayerData> requestee(Game::playerContainer.GetPlayerData(playerName));
// Check if player is online first
if (isBestFriendRequest && !requestee) {
@ -175,7 +173,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// Only do updates if there was a change in the bff status.
if (oldBestFriendStatus != bestFriendStatus) {
auto maxBestFriends = playerContainer.GetMaxNumberOfBestFriends();
auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends();
if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) {
if (requestee->countOfBestFriends >= maxBestFriends) {
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);
}
} else {
auto maxFriends = playerContainer.GetMaxNumberOfFriends();
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
if (requestee->friends.size() >= maxFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
} else if (requestor->friends.size() >= maxFriends) {
@ -232,8 +230,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
std::string friendName = PacketUtils::ReadString(0x15, packet, true);
//Now to try and find both of these:
auto requestor = playerContainer.GetPlayerData(playerID);
auto requestee = playerContainer.GetPlayerData(friendName);
auto requestor = Game::playerContainer.GetPlayerData(playerID);
auto requestee = Game::playerContainer.GetPlayerData(friendName);
if (!requestor || !requestee) return;
eAddFriendResponseType serverResponseCode{};
@ -315,7 +313,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
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:
auto goonA = playerContainer.GetPlayerData(playerID);
auto goonA = Game::playerContainer.GetPlayerData(playerID);
if (goonA) {
// Remove the friend from our list of friends
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
@ -328,7 +326,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
SendRemoveFriend(goonA, friendName, true);
}
auto goonB = playerContainer.GetPlayerData(friendID);
auto goonB = Game::playerContainer.GetPlayerData(friendID);
if (!goonB) return;
// Do it again for other person
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);
}
@ -348,11 +346,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
auto* sender = playerContainer.GetPlayerData(playerID);
auto* sender = Game::playerContainer.GetPlayerData(playerID);
if (sender == nullptr) return;
if (playerContainer.GetIsMuted(sender)) return;
if (Game::playerContainer.GetIsMuted(sender)) return;
const auto senderName = std::string(sender->playerName.c_str());
@ -367,12 +365,12 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
if (channel != 8) return;
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team == nullptr) return;
for (const auto memberId : team->memberIDs) {
auto* otherMember = playerContainer.GetPlayerData(memberId);
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
if (otherMember == nullptr) return;
@ -406,11 +404,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
//Get the bois:
auto goonA = playerContainer.GetPlayerData(senderID);
auto goonB = playerContainer.GetPlayerData(receiverName);
auto goonA = Game::playerContainer.GetPlayerData(senderID);
auto goonB = Game::playerContainer.GetPlayerData(receiverName);
if (!goonA || !goonB) return;
if (playerContainer.GetIsMuted(goonA)) return;
if (Game::playerContainer.GetIsMuted(goonA)) return;
std::string goonAName = goonA->playerName.c_str();
std::string goonBName = goonB->playerName.c_str();
@ -468,25 +466,25 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
inStream.Read(playerID);
std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true);
auto* player = playerContainer.GetPlayerData(playerID);
auto* player = Game::playerContainer.GetPlayerData(playerID);
if (player == nullptr) {
return;
}
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
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) {
return;
}
if (playerContainer.GetTeam(other->playerID) != nullptr) {
if (Game::playerContainer.GetTeam(other->playerID) != nullptr) {
return;
}
@ -519,12 +517,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
return;
}
auto* team = playerContainer.GetTeam(leaderID);
auto* team = Game::playerContainer.GetTeam(leaderID);
if (team == nullptr) {
LOG("Failed to find team for leader (%llu)", leaderID);
team = playerContainer.GetTeam(playerID);
team = Game::playerContainer.GetTeam(playerID);
}
if (team == nullptr) {
@ -532,7 +530,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
return;
}
playerContainer.AddMember(team, playerID);
Game::playerContainer.AddMember(team, playerID);
}
void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
@ -542,12 +540,12 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
uint32_t size = 0;
inStream.Read(size);
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
LOG("(%llu) leaving team", playerID);
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());
auto* kicked = playerContainer.GetPlayerData(kickedPlayer);
auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer);
LWOOBJID kickedId = LWOOBJID_EMPTY;
if (kicked != nullptr) {
kickedId = kicked->playerID;
} else {
kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
}
if (kickedId == LWOOBJID_EMPTY) return;
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) {
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());
auto* promoted = playerContainer.GetPlayerData(promotedPlayer);
auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer);
if (promoted == nullptr) return;
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) {
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;
inStream.Read(option);
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) {
if (team->leaderID != playerID) return;
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;
inStream.Read(playerID);
auto* team = playerContainer.GetTeam(playerID);
auto* data = playerContainer.GetPlayerData(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
auto* data = Game::playerContainer.GetPlayerData(playerID);
if (team != nullptr && data != nullptr) {
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;
}
if (team->memberIDs.size() <= 1 && !team->local) {
playerContainer.DisbandTeam(team);
Game::playerContainer.DisbandTeam(team);
return;
}
@ -653,16 +651,16 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
}
playerContainer.TeamStatusUpdate(team);
Game::playerContainer.TeamStatusUpdate(team);
const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName);
for (const auto memberId : team->memberIDs) {
auto* otherMember = playerContainer.GetPlayerData(memberId);
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
if (memberId == playerID) continue;
const auto memberName = playerContainer.GetName(memberId);
const auto memberName = Game::playerContainer.GetName(memberId);
if (otherMember != nullptr) {
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));
}
playerContainer.UpdateTeamsOnWorld(team, false);
Game::playerContainer.UpdateTeamsOnWorld(team, false);
}
}

View File

@ -19,6 +19,7 @@
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "Game.h"
@ -34,14 +35,12 @@ namespace Game {
AssetManager* assetManager = nullptr;
bool shouldShutdown = false;
std::mt19937 randomEngine;
PlayerContainer playerContainer;
}
Logger* SetupLogger();
void HandlePacket(Packet* packet);
PlayerContainer playerContainer;
int main(int argc, char** argv) {
constexpr uint32_t chatFramerate = mediumFramerate;
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
@ -108,7 +107,7 @@ int main(int argc, char** argv) {
Game::randomEngine = std::mt19937(time(0));
playerContainer.Initialize();
Game::playerContainer.Initialize();
//Run it until server gets a kill message from Master:
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) {
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
playerContainer.InsertPlayer(packet);
Game::playerContainer.InsertPlayer(packet);
break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
playerContainer.RemovePlayer(packet);
Game::playerContainer.RemovePlayer(packet);
break;
case eChatInternalMessageType::MUTE_UPDATE:
playerContainer.MuteUpdate(packet);
Game::playerContainer.MuteUpdate(packet);
break;
case eChatInternalMessageType::CREATE_TEAM:
playerContainer.CreateTeamServer(packet);
Game::playerContainer.CreateTeamServer(packet);
break;
case eChatInternalMessageType::ANNOUNCEMENT: {
@ -234,7 +233,15 @@ void HandlePacket(Packet* packet) {
break;
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;
case eChatMessageType::TEAM_GET_STATUS:

View File

@ -7,12 +7,26 @@
#include "dServer.h"
#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 {
LWOOBJID playerID;
std::string playerName;
SystemAddress sysAddr;
LWOZONEID zoneID;
std::vector<FriendData> friends;
std::vector<IgnoreData> ignoredPlayers;
time_t muteExpire;
uint8_t countOfBestFriends = 0;
};

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#include "ICharInfo.h"
#include "IAccounts.h"
#include "IActivityLog.h"
#include "IIgnoreList.h"
#include "IAccountsRewardCodes.h"
namespace sql {
@ -39,7 +40,7 @@ class GameDatabase :
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
public IAccounts, public IActivityLog, public IAccountsRewardCodes {
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
public:
virtual ~GameDatabase() = default;
// 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<bool> IsPlaykeyActive(const int32_t playkeyId) 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;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
private:

View File

@ -7,6 +7,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"CharXml.cpp"
"CommandLog.cpp"
"Friends.cpp"
"IgnoreList.cpp"
"Leaderboard.cpp"
"Mail.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 "ZCompression.h"
#include "PetComponent.h"
#include "VehiclePhysicsComponent.h"
#include "HavokVehiclePhysicsComponent.h"
#include "PossessableComponent.h"
#include "PossessorComponent.h"
#include "ModuleAssemblyComponent.h"
@ -76,7 +76,7 @@
#include "eGameMasterLevel.h"
#include "eReplicaComponentType.h"
#include "eReplicaPacketType.h"
#include "ZoneControlComponent.h"
#include "MiniGameControlComponent.h"
#include "RacingStatsComponent.h"
#include "CollectibleComponent.h"
#include "ItemComponent.h"
@ -217,8 +217,8 @@ void Entity::Initialize() {
AddComponent<PetComponent>(petComponentId);
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ZONE_CONTROL) > 0) {
AddComponent<ZoneControlComponent>();
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) {
AddComponent<MiniGameControlComponent>();
}
uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
@ -299,10 +299,10 @@ void Entity::Initialize() {
AddComponent<PhantomPhysicsComponent>()->SetPhysicsEffectActive(false);
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VEHICLE_PHYSICS) > 0) {
auto* vehiclePhysicsComponent = AddComponent<VehiclePhysicsComponent>();
vehiclePhysicsComponent->SetPosition(m_DefaultPosition);
vehiclePhysicsComponent->SetRotation(m_DefaultRotation);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS) > 0) {
auto* havokVehiclePhysicsComponent = AddComponent<HavokVehiclePhysicsComponent>();
havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition);
havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation);
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) {
@ -554,6 +554,12 @@ void Entity::Initialize() {
Loot::CacheMatrix(activityID);
}
const auto timeBeforeSmash = GetVar<float>(u"tmeSmsh");
if (timeBeforeSmash > 0) {
rebuildComponent->SetTimeBeforeSmash(timeBeforeSmash);
}
const auto compTime = GetVar<float>(u"compTime");
if (compTime > 0) {
@ -738,7 +744,7 @@ void Entity::Initialize() {
!HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) &&
!HasComponent(eReplicaComponentType::PROPERTY) &&
!HasComponent(eReplicaComponentType::RACING_CONTROL) &&
!HasComponent(eReplicaComponentType::VEHICLE_PHYSICS)
!HasComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS)
)
//if (HasComponent(eReplicaComponentType::BASE_COMBAT_AI))
{
@ -1017,9 +1023,9 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate);
}
VehiclePhysicsComponent* vehiclePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) {
vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
HavokVehiclePhysicsComponent* havokVehiclePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS, havokVehiclePhysicsComponent)) {
havokVehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
}
PhantomPhysicsComponent* phantomPhysicsComponent;
@ -1191,9 +1197,9 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
}
}
ZoneControlComponent* zoneControlComponent;
if (TryGetComponent(eReplicaComponentType::ZONE_CONTROL, zoneControlComponent)) {
zoneControlComponent->Serialize(outBitStream, bIsInitialUpdate);
MiniGameControlComponent* miniGameControlComponent;
if (TryGetComponent(eReplicaComponentType::MINI_GAME_CONTROL, miniGameControlComponent)) {
miniGameControlComponent->Serialize(outBitStream, bIsInitialUpdate);
}
// 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);
}
void Entity::Kill(Entity* murderer) {
void Entity::Kill(Entity* murderer, const eKillType killType) {
if (!m_PlayerIsReadyForUpdates) return;
for (const auto& cb : m_DieCallbacks) {
@ -1527,7 +1533,7 @@ void Entity::Kill(Entity* murderer) {
bool waitForDeathAnimation = false;
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!
@ -1840,7 +1846,7 @@ const NiPoint3& Entity::GetPosition() const {
return simple->GetPosition();
}
auto* vehicel = GetComponent<VehiclePhysicsComponent>();
auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) {
return vehicel->GetPosition();
@ -1868,7 +1874,7 @@ const NiQuaternion& Entity::GetRotation() const {
return simple->GetRotation();
}
auto* vehicel = GetComponent<VehiclePhysicsComponent>();
auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) {
return vehicel->GetRotation();
@ -1896,7 +1902,7 @@ void Entity::SetPosition(NiPoint3 position) {
simple->SetPosition(position);
}
auto* vehicel = GetComponent<VehiclePhysicsComponent>();
auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) {
vehicel->SetPosition(position);
@ -1924,7 +1930,7 @@ void Entity::SetRotation(NiQuaternion rotation) {
simple->SetRotation(rotation);
}
auto* vehicel = GetComponent<VehiclePhysicsComponent>();
auto* vehicel = GetComponent<HavokVehiclePhysicsComponent>();
if (vehicel != nullptr) {
vehicel->SetRotation(rotation);

View File

@ -210,7 +210,7 @@ public:
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 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 AddCollisionPhantomCallback(const std::function<void(Entity* target)>& 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);
}
}
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; }
void SetLastChatMessageApproved(bool approved) { m_LastChatMessageApproved = approved; }
std::unordered_map<std::string, bool> GetIsBestFriendMap() { return m_IsBestFriendMap; }
void SetIsBestFriendMap(std::unordered_map<std::string, bool> mapToSet) { m_IsBestFriendMap = mapToSet; }
const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; }
void UpdateBestFriendValue(const std::string_view playerName, const bool newValue);
bool GetIsMuted() const;

View File

@ -271,60 +271,58 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
}
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 << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>";
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 << "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 << "<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) {
std::stringstream xml2;
LWOOBJID lwoidforshirt = ObjectIDManager::GenerateRandomObjectID();
LWOOBJID lwoidforpants;
LWOOBJID lwoidforshirt = idforshirt;
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
xml2 << xmlSave1 << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
do {
lwoidforpants = ObjectIDManager::GenerateRandomObjectID();
} while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants
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) {
LWOOBJID lwoidforpants = idforpants;
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
xml << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
xml << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
std::stringstream xml3;
xml3 << xmlSave2 << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
xml << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
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:
bool nameOk = IsNamePreapproved(name);
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName;
std::string pendingName = !name.empty() && !nameOk ? name : "";
std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName;
std::string pendingName = !name.empty() && !nameOk ? name : "";
ICharInfo::Info info;
info.name = nameToAssign;
info.pendingName = pendingName;
info.id = objectID;
info.accountId = u->GetAccountID();
ICharInfo::Info info;
info.name = nameToAssign;
info.pendingName = pendingName;
info.id = objectID;
info.accountId = u->GetAccountID();
Database::Get()->InsertNewCharacter(info);
Database::Get()->InsertNewCharacter(info);
//Now finally insert our character xml:
Database::Get()->InsertCharacterXml(objectID, xml.str());
//Now finally insert our character xml:
Database::Get()->InsertCharacterXml(objectID, xml3.str());
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
});
});
});
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
});
}
void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) {
@ -403,7 +401,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
return;
}
if (Database::Get()->GetCharacterInfo(newName)) {
if (!Database::Get()->GetCharacterInfo(newName)) {
if (IsNamePreapproved(newName)) {
Database::Get()->SetCharacterName(charID, newName);
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) {
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 == ";
shirtQuery += std::to_string(shirtColor);
shirtQuery += " AND icc.decal == ";
shirtQuery = shirtQuery + std::to_string(shirtStyle);
auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery);
auto shirtLOT = tableData.getIntField(0, -1);
auto stmt = CDClientDatabase::CreatePreppedStmt(
"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 == ?"
);
stmt.bind(1, "character create shirt");
stmt.bind(2, static_cast<int>(shirtColor));
stmt.bind(3, static_cast<int>(shirtStyle));
auto tableData = stmt.execQuery();
auto shirtLOT = tableData.getIntField(0, 4069);
tableData.finalize();
return shirtLOT;
} catch (const std::exception&) {
LOG("Failed to execute query! Using backup...");
} catch (const std::exception& ex) {
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.
return 4069;
}
@ -479,14 +479,17 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
uint32_t FindCharPantsID(uint32_t pantsColor) {
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 == ";
pantsQuery += std::to_string(pantsColor);
auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery);
auto pantsLOT = tableData.getIntField(0, -1);
auto stmt = CDClientDatabase::CreatePreppedStmt(
"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 == ?"
);
stmt.bind(1, "cc pants");
stmt.bind(2, static_cast<int>(pantsColor));
auto tableData = stmt.execQuery();
auto pantsLOT = tableData.getIntField(0, 2508);
tableData.finalize();
return pantsLOT;
} catch (const std::exception&) {
LOG("Failed to execute query! Using backup...");
} catch (const std::exception& ex) {
LOG("Could not look up pants %i: %s", pantsColor, ex.what());
// in case of no pants color found in CDServer, return red pants.
return 2508;
}

View File

@ -15,7 +15,7 @@ void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
if (buffComponent == nullptr) return;
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) {
@ -45,4 +45,5 @@ void ApplyBuffBehavior::Load() {
cancelOnUi = GetBoolean("cancel_on_ui");
cancelOnUnequip = GetBoolean("cancel_on_unequip");
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 Load() override;
private:
bool m_ApplyOnTeammates;
};

View File

@ -38,10 +38,12 @@ void JetPackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit
}
void JetPackBehavior::Load() {
this->m_WarningEffectID = GetInt("warning_effect_id");
this->m_Airspeed = GetFloat("airspeed");
this->m_MaxAirspeed = GetFloat("max_airspeed");
this->m_VerticalVelocity = GetFloat("vertical_velocity");
this->m_EnableHover = GetBoolean("enable_hover");
this->m_BypassChecks = GetBoolean("bypass_checks", true);
this->m_WarningEffectID = GetInt("warning_effect_id", -1);
this->m_Airspeed = GetFloat("airspeed", 10);
this->m_MaxAirspeed = GetFloat("max_airspeed", 15);
this->m_VerticalVelocity = GetFloat("vertical_velocity", 1);
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);
}

View File

@ -104,7 +104,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
const auto casterPosition = self->GetPosition();
auto reference = self->GetPosition(); //+ m_offset;
auto reference = self->GetPosition() + m_offset;
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);
for (auto validTarget : validTargets) {
if (targets.size() >= this->m_maxTargets) {
break;
}
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) {
continue;
}
if (targets.size() >= this->m_maxTargets) break;
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
if (validTarget->GetIsDead()) continue;
const auto otherPosition = validTarget->GetPosition();
const auto targetPos = validTarget->GetPosition();
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y);
/*if (otherPosition.y > reference.y && heightDifference > this->m_upperBound || otherPosition.y < reference.y && heightDifference > this->m_lowerBound)
{
// make sure we aren't too high or low in comparison to the targer
const auto heightDifference = std::abs(reference.y - targetPos.y);
if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
continue;
}*/
const auto forward = self->GetRotation().GetForwardVector();
// 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.
// 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) {
NiPoint3 rayPoint = casterPosition + forward * distance;
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, otherPosition) > this->m_farWidth * this->m_farWidth) {
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, targetPos) > this->m_farWidth * this->m_farWidth)
continue;
}
}
auto normalized = (reference - otherPosition) / distance;
auto normalized = (reference - targetPos) / distance;
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) {
targets.push_back(validTarget);
}
@ -167,33 +155,26 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
});
const auto hit = !targets.empty();
bitStream->Write(hit);
if (this->m_checkEnv) {
const auto blocked = false; // TODO
bitStream->Write(blocked);
}
if (hit) {
if (combatAi != nullptr) {
combatAi->LookAt(targets[0]->GetPosition());
}
if (combatAi) combatAi->LookAt(targets[0]->GetPosition());
context->foundTarget = true; // We want to continue with this behavior
const auto count = static_cast<uint32_t>(targets.size());
bitStream->Write(count);
for (auto* target : targets) {
bitStream->Write(target->GetObjectID());
}
for (auto* target : targets) {
branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch);
}
} else {
@ -214,8 +195,8 @@ void TacArcBehavior::Load() {
GetFloat("offset_z", 0.0f)
);
this->m_method = GetInt("method", 1);
this->m_upperBound = GetFloat("upper_bound", 4.4f);
this->m_lowerBound = GetFloat("lower_bound", 0.4f);
this->m_upperBound = std::abs(GetFloat("upper_bound", 4.4f));
this->m_lowerBound = std::abs(GetFloat("lower_bound", 0.4f));
this->m_usePickedTarget = GetBoolean("use_picked_target", false);
this->m_useTargetPostion = GetBoolean("use_target_position", false);
this->m_checkEnv = GetBoolean("check_env", false);

View File

@ -11,9 +11,21 @@
#include "EntityManager.h"
#include "CDClientManager.h"
#include "CDSkillBehaviorTable.h"
#include "TeamManager.h"
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) {
}
@ -22,32 +34,38 @@ BuffComponent::~BuffComponent() {
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (!bIsInitialUpdate) return;
if (m_Buffs.empty()) {
outBitStream->Write0();
} else {
outBitStream->Write1();
outBitStream->Write(!m_Buffs.empty());
if (!m_Buffs.empty()) {
outBitStream->Write<uint32_t>(m_Buffs.size());
for (const auto& buff : m_Buffs) {
outBitStream->Write<uint32_t>(buff.first);
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
for (const auto& [id, buff] : m_Buffs) {
outBitStream->Write<uint32_t>(id);
outBitStream->Write(buff.time != 0.0f);
if (buff.time != 0.0f) outBitStream->Write(static_cast<uint32_t>(buff.time * 1000.0f));
outBitStream->Write(buff.cancelOnDeath);
outBitStream->Write(buff.cancelOnZone);
outBitStream->Write(buff.cancelOnDamaged);
outBitStream->Write(buff.cancelOnRemoveBuff);
outBitStream->Write(buff.cancelOnUi);
outBitStream->Write(buff.cancelOnLogout);
outBitStream->Write(buff.cancelOnUnequip);
outBitStream->Write0(); // Cancel on Damage Absorb Ran Out. Generally false from what I can tell
outBitStream->Write0();
outBitStream->Write0();
auto* team = TeamManager::Instance()->GetTeam(buff.source);
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) {
@ -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,
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.
if (HasBuff(id)) {
m_Buffs[id].refCount++;
m_Buffs[id].time = duration;
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,
(uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, addedByTeammate, applyOnTeammates);
float tick = 0;
float stacks = 0;
@ -121,17 +177,43 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
buff.stacks = stacks;
buff.source = source;
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);
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);
if (iter == m_Buffs.end()) {
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);
m_Buffs.erase(iter);
@ -146,6 +228,7 @@ bool BuffComponent::HasBuff(int32_t id) {
void BuffComponent::ApplyBuffEffect(int32_t id) {
const auto& parameters = GetBuffParameters(id);
for (const auto& parameter : parameters) {
ApplyBuffFx(id, parameter);
if (parameter.name == "max_health") {
const auto maxHealth = parameter.value;
@ -182,6 +265,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
void BuffComponent::RemoveBuffEffect(int32_t id) {
const auto& parameters = GetBuffParameters(id);
for (const auto& parameter : parameters) {
RemoveBuffFx(id, parameter);
if (parameter.name == "max_health") {
const auto maxHealth = parameter.value;
@ -251,13 +335,25 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* buffEntry = buffElement->FirstChildElement("b");
while (buffEntry != nullptr) {
while (buffEntry) {
int32_t id = buffEntry->IntAttribute("id");
float t = buffEntry->FloatAttribute("t");
float tk = buffEntry->FloatAttribute("tk");
float tt = buffEntry->FloatAttribute("tt");
int32_t s = buffEntry->FloatAttribute("s");
LWOOBJID sr = buffEntry->Int64Attribute("sr");
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.id = id;
@ -266,6 +362,18 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
buff.stacks = s;
buff.source = sr;
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);
@ -288,15 +396,27 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
buffElement->DeleteChildren();
}
for (const auto& buff : m_Buffs) {
for (const auto& [id, buff] : m_Buffs) {
auto* buffEntry = doc->NewElement("b");
if (buff.cancelOnZone || buff.cancelOnLogout) continue;
buffEntry->SetAttribute("id", buff.first);
buffEntry->SetAttribute("t", buff.second.time);
buffEntry->SetAttribute("tk", buff.second.tick);
buffEntry->SetAttribute("s", buff.second.stacks);
buffEntry->SetAttribute("sr", buff.second.source);
buffEntry->SetAttribute("b", buff.second.behaviorID);
buffEntry->SetAttribute("id", id);
buffEntry->SetAttribute("t", buff.time);
buffEntry->SetAttribute("tk", buff.tick);
buffEntry->SetAttribute("tt", buff.tickTime);
buffEntry->SetAttribute("s", buff.stacks);
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);
}
@ -309,8 +429,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
return pair->second;
}
auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
query.bind(1, (int)buffId);
auto result = query.execQuery();
@ -321,11 +440,12 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
BuffParameter param;
param.buffId = buffId;
param.name = result.getStringField(1);
param.value = result.getFloatField(2);
param.name = result.getStringField("ParameterName");
param.value = result.getFloatField("NumberValue");
param.effectId = result.getIntField("EffectID");
if (!result.fieldIsNull(3)) {
std::istringstream stream(result.getStringField(3));
std::istringstream stream(result.getStringField("StringValue"));
std::string 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
*/
struct BuffParameter
{
struct BuffParameter {
int32_t buffId;
std::string name;
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.
*/
struct Buff
{
struct Buff {
int32_t id = 0;
float time = 0;
float tick = 0;
@ -35,6 +33,15 @@ struct Buff
int32_t stacks = 0;
LWOOBJID source = 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,
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
* @param id the id of the buff to remove
* @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`

View File

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

View File

@ -10,7 +10,7 @@
#include "InventoryComponent.h"
#include "ControllablePhysicsComponent.h"
#include "EntityManager.h"
#include "VehiclePhysicsComponent.h"
#include "HavokVehiclePhysicsComponent.h"
#include "GameMessages.h"
#include "Item.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) {

View File

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

View File

@ -6,6 +6,13 @@
#include "eReplicaComponentType.h"
struct RemoteInputInfo {
RemoteInputInfo() {
m_RemoteInputX = 0;
m_RemoteInputY = 0;
m_IsPowersliding = false;
m_IsModified = false;
}
void operator=(const RemoteInputInfo& other) {
m_RemoteInputX = other.m_RemoteInputX;
m_RemoteInputY = other.m_RemoteInputY;
@ -26,11 +33,11 @@ struct RemoteInputInfo {
/**
* Physics component for vehicles.
*/
class VehiclePhysicsComponent : public PhysicsComponent {
class HavokVehiclePhysicsComponent : public PhysicsComponent {
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;

View File

@ -19,7 +19,7 @@
#include "PossessorComponent.h"
#include "PossessableComponent.h"
#include "ModuleAssemblyComponent.h"
#include "VehiclePhysicsComponent.h"
#include "HavokVehiclePhysicsComponent.h"
#include "CharacterComponent.h"
#include "dZoneManager.h"
#include "PropertyManagementComponent.h"
@ -981,7 +981,7 @@ void InventoryComponent::HandlePossession(Item* item) {
auto* mount = Game::entityManager->CreateEntity(info, nullptr, m_Parent);
// 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);
// 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) {
for (const auto& pair : m_Missions) {
auto* mission = pair.second;
std::vector<uint32_t> acceptedAchievements;
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;
@ -154,10 +159,6 @@ void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID a
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) {
@ -282,12 +283,12 @@ bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) {
#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
// Query for achievments, using the cache
const auto& result = QueryAchievements(type, value, targets);
bool any = false;
std::vector<uint32_t> acceptedAchievements;
for (const uint32_t missionID : result) {
// Check if we already have this achievement
@ -309,7 +310,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
instance->Accept();
any = true;
acceptedAchievements.push_back(missionID);
if (progress) {
// 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
auto* missionTasksTable = CDClientManager::Instance().GetTable<CDMissionTasksTable>();
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);
});
auto any = false;
std::vector<uint32_t> acceptedAchievements;
for (const auto& task : tasks) {
if (GetMission(task.id) != nullptr) {
@ -380,14 +381,14 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
instance->Accept();
any = true;
acceptedAchievements.push_back(mission.id);
if (progress) {
instance->Progress(type, value, associate, targets, count);
}
}
return any;
return acceptedAchievements;
#endif
}
@ -499,7 +500,7 @@ bool MissionComponent::RequiresItem(const LOT lot) {
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)
* @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

View File

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

View File

@ -17,7 +17,7 @@
#include "PossessorComponent.h"
#include "eRacingTaskParam.h"
#include "Spawner.h"
#include "VehiclePhysicsComponent.h"
#include "HavokVehiclePhysicsComponent.h"
#include "dServer.h"
#include "dZoneManager.h"
#include "dConfig.h"
@ -51,7 +51,7 @@ RacingControlComponent::RacingControlComponent(Entity* parent)
m_MainWorld = 1200;
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;
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 (m_Loaded || !vehicle) {
auto* playerInstance = dynamic_cast<Player*>(player);
if(playerInstance){
if (playerInstance) {
playerInstance->SendToZone(m_MainWorld);
}
return;
@ -106,12 +106,12 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player,
if (item == nullptr) {
LOG("Failed to find item");
auto* playerInstance = dynamic_cast<Player*>(player);
if(playerInstance){
if (playerInstance) {
m_LoadedPlayers--;
playerInstance->SendToZone(m_MainWorld);
}
return;
}
// Calculate the vehicle's starting position.
@ -213,6 +213,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player,
0,
0,
0 });
m_AllPlayersReady = false;
}
// 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.
if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination);
Game::entityManager->SerializeEntity(vehicle);
});
});
auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
@ -384,11 +385,11 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
// Calculate the score, different loot depending on player count
auto playersRating = m_LoadedPlayers;
if(m_LoadedPlayers == 1 && m_SoloRacing) {
if (m_LoadedPlayers == 1 && m_SoloRacing) {
playersRating *= 2;
}
const auto score = playersRating * 10 + data->finished;
const auto score = playersRating * 10 + data->finished;
Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score);
// Giving rewards
@ -436,64 +437,82 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// BEGIN Scripted Activity
outBitStream->Write1();
outBitStream->Write(static_cast<uint32_t>(m_RacingPlayers.size()));
for (const auto& player : m_RacingPlayers) {
outBitStream->Write(player.playerID);
for (int i = 0; i < 10; i++) {
outBitStream->Write(player.data[i]);
}
outBitStream->Write(player.data[0]);
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
outBitStream->Write1(); // Dirty?
outBitStream->Write1();
outBitStream->Write(static_cast<uint16_t>(m_RacingPlayers.size()));
outBitStream->Write(!m_RacingPlayers.empty());
if (!m_RacingPlayers.empty()) {
outBitStream->Write(!m_AllPlayersReady);
if (!m_AllPlayersReady) {
int32_t numReady = 0;
for (const auto& player : m_RacingPlayers) {
outBitStream->Write1(); // Has more date
outBitStream->Write1(); // Has more player data
outBitStream->Write(player.playerID);
outBitStream->Write(player.vehicleID);
outBitStream->Write(player.playerIndex);
outBitStream->Write(player.playerLoaded);
if (player.playerLoaded) numReady++;
}
outBitStream->Write0(); // No more data
if (numReady == m_RacingPlayers.size()) m_AllPlayersReady = true;
}
outBitStream->Write(!m_RacingPlayers.empty());
if (!m_RacingPlayers.empty()) {
for (const auto& player : m_RacingPlayers) {
if (player.finished == 0) continue;
outBitStream->Write1(); // Has more date
outBitStream->Write(player.playerID);
outBitStream->Write<uint32_t>(0);
outBitStream->Write(player.finished);
}
outBitStream->Write0(); // No more data
}
outBitStream->Write1(); // Dirty?
outBitStream->Write(m_RemainingLaps);
outBitStream->Write(static_cast<uint16_t>(m_PathName.size()));
for (const auto character : m_PathName) {
outBitStream->Write(character);
outBitStream->Write(bIsInitialUpdate);
if (bIsInitialUpdate) {
outBitStream->Write(m_RemainingLaps);
outBitStream->Write(static_cast<uint16_t>(m_PathName.size()));
for (const auto character : m_PathName) {
outBitStream->Write(character);
}
}
outBitStream->Write1(); // ???
outBitStream->Write1(); // ???
outBitStream->Write(!m_RacingPlayers.empty());
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->Write(m_RaceBestLap);
outBitStream->Write(m_RaceBestTime);
outBitStream->Write0(); // No more data
}
}
RacingPlayerInfo* RacingControlComponent::GetPlayerData(LWOOBJID playerID) {
@ -569,7 +588,7 @@ void RacingControlComponent::Update(float deltaTime) {
LoadPlayerVehicle(player, positionNumber + 1, true);
m_Loaded = true;
Game::entityManager->SerializeEntity(m_Parent);
}
m_Loaded = true;
@ -757,6 +776,8 @@ void RacingControlComponent::Update(float deltaTime) {
continue;
}
if (m_Finished != 0) Game::entityManager->SerializeEntity(m_Parent);
// Loop through all the waypoints and see if the player has reached a
// new checkpoint
uint32_t respawnIndex = 0;
@ -849,8 +870,6 @@ void RacingControlComponent::Update(float deltaTime) {
if (characterComponent != nullptr) {
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);
/**
* 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:
/**
@ -251,4 +244,5 @@ private:
* Value for message box response to know if we are exiting the race via the activity dialogue
*/
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>();
if (!destComp) break;
int newImagination = destComp->GetImagination();
++m_DrainedImagination;
--newImagination;
const int32_t imaginationCostRemaining = m_TakeImagination - m_DrainedImagination;
const int32_t newImagination = destComp->GetImagination() - 1;
destComp->SetImagination(newImagination);
Game::entityManager->SerializeEntity(builder);
if (newImagination <= 0) {
if (newImagination <= 0 && imaginationCostRemaining > 0) {
CancelRebuild(builder, eQuickBuildFailReason::OUT_OF_IMAGINATION, true);
break;
}
}
if (m_Timer >= m_CompleteTime && m_DrainedImagination >= m_TakeImagination) {

View File

@ -285,7 +285,7 @@ private:
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;

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 "PetComponent.h"
#include "ModuleAssemblyComponent.h"
#include "VehiclePhysicsComponent.h"
#include "HavokVehiclePhysicsComponent.h"
#include "RenderComponent.h"
#include "PossessableComponent.h"
#include "PossessorComponent.h"
@ -944,14 +944,7 @@ void GameMessages::SendResurrect(Entity* entity) {
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;
CMSGHEADER;
@ -1144,18 +1137,16 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo
bitStream.Write(position.y);
bitStream.Write(position.z);
auto con = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS));
if (con) {
auto rot = con->GetRotation();
bitStream.Write(rot.x);
bitStream.Write(rot.y);
bitStream.Write(rot.z);
bitStream.Write(rot.w);
const bool isNotIdentity = rotation != NiQuaternion::IDENTITY;
bitStream.Write(isNotIdentity);
if (isNotIdentity) {
bitStream.Write(rotation.w);
bitStream.Write(rotation.x);
bitStream.Write(rotation.y);
bitStream.Write(rotation.z);
}
//bitStream.Write(position);
//bitStream.Write(rotation);
SystemAddress sysAddr = entity->GetSystemAddress();
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,
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) {
CBITSTREAM;
CMSGHEADER;
@ -4494,27 +4485,29 @@ void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uin
bitStream.Write(objectID);
bitStream.Write(eGameMessageType::ADD_BUFF);
bitStream.Write(false); // Added by teammate
bitStream.Write(false); // Apply on teammates
bitStream.Write(false); // Cancel on damage absorb ran out
bitStream.Write(addedByTeammate); // Added by teammate
bitStream.Write(applyOnTeammates); // Apply on teammates
bitStream.Write(cancelOnDamaged);
bitStream.Write(cancelOnDeath);
bitStream.Write(cancelOnLogout);
bitStream.Write(false); // Cancel on move
bitStream.Write(cancelOnRemoveBuff);
bitStream.Write(cancelOnUi);
bitStream.Write(cancelOnUnequip);
bitStream.Write(cancelOnZone);
bitStream.Write(false); // Ignore immunities
bitStream.Write(addImmunity);
bitStream.Write(false); // Use ref count
bitStream.Write(buffID);
bitStream.Write(msDuration);
bitStream.Write(casterID != LWOOBJID_EMPTY);
if (casterID != LWOOBJID_EMPTY) bitStream.Write(casterID);
bitStream.Write(casterID);
bitStream.Write(casterID);
bitStream.Write(buffID);
bitStream.Write(msDuration != 0);
if (msDuration != 0) bitStream.Write(msDuration);
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
SEND_PACKET;
@ -5017,6 +5010,14 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity)
if (emoteID == 0) return;
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>();
if (!missionComponent) return;
@ -5042,14 +5043,6 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity)
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) {
@ -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 (?,?,?)"));
stmt->setUInt64(1, newIdBig);
stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules));
stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules).c_str());
auto* pCharacter = character->GetCharacter();
pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT);
stmt->execute();

View File

@ -206,7 +206,7 @@ namespace GameMessages {
void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration,
bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true,
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);

View File

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

View File

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

View File

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

View File

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

View File

@ -79,8 +79,6 @@ dZoneManager::~dZoneManager() {
delete p.second;
p.second = nullptr;
}
m_Spawners.erase(p.first);
}
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)
);