mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-04 06:32:00 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/main' into speed
This commit is contained in:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
set(DCHATSERVER_SOURCES
 | 
			
		||||
	"ChatIgnoreList.cpp"
 | 
			
		||||
	"ChatPacketHandler.cpp"
 | 
			
		||||
	"PlayerContainer.cpp"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										173
									
								
								dChatServer/ChatIgnoreList.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								dChatServer/ChatIgnoreList.cpp
									
									
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								dChatServer/ChatIgnoreList.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								dChatServer/ChatIgnoreList.h
									
									
									
									
									
										Normal 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__
 | 
			
		||||
@@ -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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								dDatabase/GameDatabase/ITables/IIgnoreList.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								dDatabase/GameDatabase/ITables/IIgnoreList.h
									
									
									
									
									
										Normal 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__
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
 | 
			
		||||
	"CharXml.cpp"
 | 
			
		||||
	"CommandLog.cpp"
 | 
			
		||||
	"Friends.cpp"
 | 
			
		||||
	"IgnoreList.cpp"
 | 
			
		||||
	"Leaderboard.cpp"
 | 
			
		||||
	"Mail.cpp"
 | 
			
		||||
	"MigrationHistory.cpp"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp
									
									
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,4 +31,6 @@ public:
 | 
			
		||||
	void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
 | 
			
		||||
 | 
			
		||||
	void Load() override;
 | 
			
		||||
private:
 | 
			
		||||
	bool m_ApplyOnTeammates;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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); 
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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, ',')) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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`
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								dGame/dComponents/MiniGameControlComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								dGame/dComponents/MiniGameControlComponent.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
#include "MiniGameControlComponent.h"
 | 
			
		||||
 | 
			
		||||
void MiniGameControlComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
 | 
			
		||||
	outBitStream->Write<uint32_t>(0x40000000);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								dGame/dComponents/MiniGameControlComponent.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								dGame/dComponents/MiniGameControlComponent.h
									
									
									
									
									
										Normal 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__
 | 
			
		||||
							
								
								
									
										5
									
								
								dGame/dComponents/MinigameComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								dGame/dComponents/MinigameComponent.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
#include "MinigameComponent.h"
 | 
			
		||||
 | 
			
		||||
void MinigameComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
 | 
			
		||||
	outBitStream->Write<uint32_t>(0x40000000);
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -164,6 +164,8 @@ public:
 | 
			
		||||
 | 
			
		||||
	LWOCLONEID GetCloneId() { return clone_Id; };
 | 
			
		||||
 | 
			
		||||
	LWOOBJID GetId() const noexcept { return propertyId; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	/**
 | 
			
		||||
	 * This
 | 
			
		||||
 
 | 
			
		||||
@@ -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";
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
#include "ZoneControlComponent.h"
 | 
			
		||||
 | 
			
		||||
void ZoneControlComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
 | 
			
		||||
	outBitStream->Write<uint32_t>(0x40000000);
 | 
			
		||||
}
 | 
			
		||||
@@ -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__
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ void BasePropertyServer::BasePlayerLoaded(Entity* self, Entity* player) {
 | 
			
		||||
				missionComponent->Progress(
 | 
			
		||||
					eMissionTaskType::VISIT_PROPERTY,
 | 
			
		||||
					mapID.GetMapID(),
 | 
			
		||||
					mapID.GetCloneID()
 | 
			
		||||
					PropertyManagementComponent::Instance()->GetId()
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -79,8 +79,6 @@ dZoneManager::~dZoneManager() {
 | 
			
		||||
			delete p.second;
 | 
			
		||||
			p.second = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m_Spawners.erase(p.first);
 | 
			
		||||
	}
 | 
			
		||||
	if (m_WorldConfig) delete m_WorldConfig;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								migrations/dlu/13_ignore_list.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								migrations/dlu/13_ignore_list.sql
									
									
									
									
									
										Normal 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)
 | 
			
		||||
);
 | 
			
		||||
		Reference in New Issue
	
	Block a user