mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-27 16:07:22 +00:00
53877a0bc3
* Rewrite AMF and behavior logic to use smart pointers, references, and string_views over raw pointers and std::string& * fix m_BehaviorID initialization * Fix BlockDefinition member naming * remove redundant reset()s * Replace UB forward template declarations with header include * remove unneeded comment * remove non-const ref getters * simplify default behavior id initialization * Fix invalidated use of Getter to set a value * Update AddStripMessage.cpp - change push_back to emplace_back * fix pointer to ref conversion mistake (should not have directly grabbed from the other branch commit) * deref * VERY experimental testing of forward declaration of templates - probably will revert * Revert changes (as expected) * Update BlockDefinition.h - remove extraneous semicolons * Update BlockDefinition.h - remove linebreak * Update Amf3.h member naming scheme * fix duplicated code * const iterators * const pointers * reviving this branch * update read switch cases
6331 lines
191 KiB
C++
6331 lines
191 KiB
C++
#include "GameMessages.h"
|
|
#include "User.h"
|
|
#include "Entity.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "BitStream.h"
|
|
#include "Game.h"
|
|
#include "SlashCommandHandler.h"
|
|
#include "NiPoint3.h"
|
|
#include "NiQuaternion.h"
|
|
#include "Logger.h"
|
|
#include "GeneralUtils.h"
|
|
#include "Character.h"
|
|
#include "EntityManager.h"
|
|
#include "Database.h"
|
|
#include "dServer.h"
|
|
#include "ObjectIDManager.h"
|
|
#include "CppScripts.h"
|
|
#include "UserManager.h"
|
|
#include "ZoneInstanceManager.h"
|
|
#include "WorldPackets.h"
|
|
#include "Item.h"
|
|
#include "ZCompression.h"
|
|
#include "dConfig.h"
|
|
#include "TeamManager.h"
|
|
#include "ChatPackets.h"
|
|
#include "MultiZoneEntranceComponent.h"
|
|
#include "eUnequippableActiveType.h"
|
|
#include "eMovementPlatformState.h"
|
|
#include "LeaderboardManager.h"
|
|
#include "Amf3.h"
|
|
#include "Loot.h"
|
|
#include "eRacingTaskParam.h"
|
|
#include "eMissionTaskType.h"
|
|
#include "eMissionState.h"
|
|
#include "eObjectBits.h"
|
|
#include "eTriggerEventType.h"
|
|
#include "eMatchUpdate.h"
|
|
#include "eCyclingMode.h"
|
|
#include "eCinematicEvent.h"
|
|
#include "eQuickBuildFailReason.h"
|
|
#include "eControlScheme.h"
|
|
#include "eStateChangeType.h"
|
|
#include "eConnectionType.h"
|
|
#include "ePlayerFlag.h"
|
|
|
|
#include <sstream>
|
|
#include <future>
|
|
#include <chrono>
|
|
#include "RakString.h"
|
|
|
|
#include "httplib.h" //sorry not sorry.
|
|
|
|
//CDB includes:
|
|
#include "CDClientManager.h"
|
|
#include "CDEmoteTable.h"
|
|
|
|
//Component includes:
|
|
#include "ControllablePhysicsComponent.h"
|
|
#include "CharacterComponent.h"
|
|
#include "MissionOfferComponent.h"
|
|
#include "MissionComponent.h"
|
|
#include "DestroyableComponent.h"
|
|
#include "ScriptComponent.h"
|
|
#include "QuickBuildComponent.h"
|
|
#include "VendorComponent.h"
|
|
#include "InventoryComponent.h"
|
|
#include "RocketLaunchpadControlComponent.h"
|
|
#include "PropertyEntranceComponent.h"
|
|
#include "MovingPlatformComponent.h"
|
|
#include "PetComponent.h"
|
|
#include "ModuleAssemblyComponent.h"
|
|
#include "HavokVehiclePhysicsComponent.h"
|
|
#include "RenderComponent.h"
|
|
#include "PossessableComponent.h"
|
|
#include "PossessorComponent.h"
|
|
#include "RacingControlComponent.h"
|
|
#include "RailActivatorComponent.h"
|
|
#include "LevelProgressionComponent.h"
|
|
#include "DonationVendorComponent.h"
|
|
#include "GhostComponent.h"
|
|
#include "AchievementVendorComponent.h"
|
|
|
|
// Message includes:
|
|
#include "dZoneManager.h"
|
|
#include "PropertyDataMessage.h"
|
|
#include "PropertyManagementComponent.h"
|
|
#include "PropertyVendorComponent.h"
|
|
#include "PropertySelectQueryProperty.h"
|
|
#include "TradingManager.h"
|
|
#include "ControlBehaviors.h"
|
|
#include "AMFDeserialize.h"
|
|
#include "eBlueprintSaveResponseType.h"
|
|
#include "eAnimationFlags.h"
|
|
#include "AmfSerialize.h"
|
|
#include "eReplicaComponentType.h"
|
|
#include "MessageType/Client.h"
|
|
#include "MessageType/Game.h"
|
|
#include "ePetAbilityType.h"
|
|
#include "ActivityManager.h"
|
|
#include "PlayerManager.h"
|
|
#include "eVendorTransactionResult.h"
|
|
#include "eReponseMoveItemBetweenInventoryTypeCode.h"
|
|
|
|
#include "CDComponentsRegistryTable.h"
|
|
#include "CDObjectsTable.h"
|
|
#include "eItemType.h"
|
|
|
|
void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::FIRE_EVENT_CLIENT_SIDE);
|
|
|
|
//bitStream.Write(args);
|
|
uint32_t argSize = args.size();
|
|
bitStream.Write(argSize);
|
|
for (uint32_t k = 0; k < argSize; k++) {
|
|
bitStream.Write<uint16_t>(args[k]);
|
|
}
|
|
bitStream.Write(object);
|
|
bitStream.Write0();
|
|
//bitStream.Write(param1);
|
|
bitStream.Write0();
|
|
//bitStream.Write(param2);
|
|
bitStream.Write(sender);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::TELEPORT);
|
|
|
|
bool bIgnoreY = (pos.y == 0.0f);
|
|
bool bUseNavmesh = false;
|
|
bool bSkipAllChecks = false;
|
|
//float w = 1.0f;
|
|
//float x = 0.0f;
|
|
//float y = 0.0f;
|
|
//float z = 0.0f;
|
|
|
|
bitStream.Write(bIgnoreY);
|
|
bitStream.Write(bSetRotation);
|
|
bitStream.Write(bSkipAllChecks);
|
|
bitStream.Write(pos.x);
|
|
bitStream.Write(pos.y);
|
|
bitStream.Write(pos.z);
|
|
bitStream.Write(bUseNavmesh);
|
|
|
|
bitStream.Write(rot.w != 1.0f);
|
|
if (rot.w != 1.0f) bitStream.Write(rot.w);
|
|
|
|
bitStream.Write(rot.x);
|
|
bitStream.Write(rot.y);
|
|
bitStream.Write(rot.z);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlayAnimation(Entity* entity, const std::u16string& animationName, float fPriority, float fScale) {
|
|
if (!entity) {
|
|
LOG("Trying to play animation, but entity var is nullptr!");
|
|
return;
|
|
}
|
|
|
|
//Stolen from the old DLU codebase as the new one's autogenerated code doesn't work properly for animationIDs longer than 6 characters.
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
std::string sAnimationID = GeneralUtils::UTF16ToWTF8(animationName);
|
|
|
|
uint32_t animationIDLength = sAnimationID.size();
|
|
bool bExpectAnimToExist = true;
|
|
bool bPlayImmediate = false;
|
|
bool bTriggerOnCompleteMsg = false;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAY_ANIMATION);
|
|
|
|
bitStream.Write(animationIDLength);
|
|
bitStream.Write(LUWString(animationName, animationIDLength));
|
|
|
|
bitStream.Write(bExpectAnimToExist);
|
|
|
|
bitStream.Write(bPlayImmediate);
|
|
|
|
bitStream.Write(bTriggerOnCompleteMsg);
|
|
|
|
bitStream.Write(fPriority != 0.0f);
|
|
if (fPriority != 0.0f) bitStream.Write(fPriority);
|
|
|
|
bitStream.Write(fScale != 1.0f);
|
|
if (fScale != 1.0f) bitStream.Write(fScale);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendPlayerReady(Entity* entity, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAYER_READY);
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlayerAllowedRespawn(LWOOBJID entityID, bool doNotPromptRespawn, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entityID);
|
|
bitStream.Write(MessageType::Game::SET_PLAYER_ALLOWED_RESPAWN);
|
|
bitStream.Write(doNotPromptRespawn);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendInvalidZoneTransferList(Entity* entity, const SystemAddress& sysAddr, const std::u16string& feedbackURL, const std::u16string& invalidMapTransferList, bool feedbackOnExit, bool feedbackOnInvalidTransfer) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::INVALID_ZONE_TRANSFER_LIST);
|
|
|
|
uint32_t CustomerFeedbackURLLength = feedbackURL.size();
|
|
bitStream.Write(CustomerFeedbackURLLength);
|
|
for (uint32_t k = 0; k < CustomerFeedbackURLLength; k++) {
|
|
bitStream.Write<uint16_t>(feedbackURL[k]);
|
|
}
|
|
|
|
uint32_t InvalidMapTransferListLength = invalidMapTransferList.size();
|
|
bitStream.Write(InvalidMapTransferListLength);
|
|
for (uint32_t k = 0; k < InvalidMapTransferListLength; k++) {
|
|
bitStream.Write<uint16_t>(invalidMapTransferList[k]);
|
|
}
|
|
|
|
bitStream.Write(feedbackOnExit);
|
|
bitStream.Write(feedbackOnInvalidTransfer);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendKnockback(const LWOOBJID& objectID, const LWOOBJID& caster, const LWOOBJID& originator, int knockBackTimeMS, const NiPoint3& vector) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::KNOCKBACK);
|
|
|
|
bool casterFlag = caster != LWOOBJID_EMPTY;
|
|
bool originatorFlag = originator != LWOOBJID_EMPTY;
|
|
bool knockBackTimeMSFlag = knockBackTimeMS != 0;
|
|
|
|
bitStream.Write(casterFlag);
|
|
if (casterFlag) bitStream.Write(caster);
|
|
bitStream.Write(originatorFlag);
|
|
if (originatorFlag) bitStream.Write(originator);
|
|
bitStream.Write(knockBackTimeMSFlag);
|
|
if (knockBackTimeMSFlag) bitStream.Write(knockBackTimeMS);
|
|
bitStream.Write(vector);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendStartArrangingWithItem(
|
|
Entity* entity,
|
|
const SystemAddress& sysAddr,
|
|
bool bFirstTime,
|
|
const LWOOBJID& buildAreaID,
|
|
NiPoint3 buildStartPOS,
|
|
int sourceBAG,
|
|
const LWOOBJID& sourceID,
|
|
LOT sourceLOT,
|
|
int sourceTYPE,
|
|
const LWOOBJID& targetID,
|
|
LOT targetLOT,
|
|
NiPoint3 targetPOS,
|
|
int targetTYPE
|
|
) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::START_ARRANGING_WITH_ITEM);
|
|
|
|
bitStream.Write(bFirstTime);
|
|
bitStream.Write(buildAreaID != LWOOBJID_EMPTY);
|
|
if (buildAreaID != LWOOBJID_EMPTY) bitStream.Write(buildAreaID);
|
|
bitStream.Write(buildStartPOS);
|
|
bitStream.Write(sourceBAG);
|
|
bitStream.Write(sourceID);
|
|
bitStream.Write(sourceLOT);
|
|
bitStream.Write(sourceTYPE);
|
|
bitStream.Write(targetID);
|
|
bitStream.Write(targetLOT);
|
|
bitStream.Write(targetPOS);
|
|
bitStream.Write(targetTYPE);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlayerSetCameraCyclingMode(const LWOOBJID& objectID, const SystemAddress& sysAddr,
|
|
bool bAllowCyclingWhileDeadOnly, eCyclingMode cyclingMode) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::PLAYER_SET_CAMERA_CYCLING_MODE);
|
|
|
|
bitStream.Write(bAllowCyclingWhileDeadOnly);
|
|
|
|
bitStream.Write(cyclingMode != eCyclingMode::ALLOW_CYCLE_TEAMMATES);
|
|
if (cyclingMode != eCyclingMode::ALLOW_CYCLE_TEAMMATES) {
|
|
bitStream.Write(cyclingMode);
|
|
}
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& sysAddr, std::string audioGUID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAY_ND_AUDIO_EMITTER);
|
|
bitStream.Write0(); // callback message data {lwoobjid}
|
|
bitStream.Write0(); // audio emitterid {uint32_t}
|
|
|
|
uint32_t length = audioGUID.size();
|
|
bitStream.Write(length);
|
|
for (uint32_t k = 0; k < length; k++) {
|
|
bitStream.Write<char>(audioGUID[k]);
|
|
}
|
|
|
|
bitStream.Write<uint32_t>(0); // size of NDAudioMetaEventName (then print the string like the guid)
|
|
bitStream.Write0(); // result {bool}
|
|
bitStream.Write0(); // m_TargetObjectIDForNDAudioCallbackMessages {lwoobjid}
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendStartPathing(Entity* entity) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::START_PATHING);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendResetMissions(Entity* entity, const SystemAddress& sysAddr, const int32_t missionid) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::RESET_MISSIONS);
|
|
|
|
bitStream.Write(missionid != -1);
|
|
if (missionid != -1) bitStream.Write(missionid);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint,
|
|
int iIndex, int iDesiredWaypointIndex, int nextIndex,
|
|
eMovementPlatformState movementState) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
const auto lot = entity->GetLOT();
|
|
|
|
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308) {
|
|
iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0;
|
|
iIndex = 0;
|
|
nextIndex = 0;
|
|
bStopAtDesiredWaypoint = true;
|
|
movementState = eMovementPlatformState::Stationary;
|
|
}
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLATFORM_RESYNC);
|
|
|
|
bool bReverse = false;
|
|
int eCommand = 0;
|
|
int eUnexpectedCommand = 0;
|
|
float fIdleTimeElapsed = 0.0f;
|
|
float fMoveTimeElapsed = 0.0f;
|
|
float fPercentBetweenPoints = 0.0f;
|
|
NiPoint3 ptUnexpectedLocation = NiPoint3Constant::ZERO;
|
|
NiQuaternion qUnexpectedRotation = NiQuaternionConstant::IDENTITY;
|
|
|
|
bitStream.Write(bReverse);
|
|
bitStream.Write(bStopAtDesiredWaypoint);
|
|
bitStream.Write(eCommand);
|
|
bitStream.Write(static_cast<int32_t>(movementState));
|
|
bitStream.Write(eUnexpectedCommand);
|
|
bitStream.Write(fIdleTimeElapsed);
|
|
bitStream.Write(fMoveTimeElapsed);
|
|
bitStream.Write(fPercentBetweenPoints);
|
|
bitStream.Write(iDesiredWaypointIndex);
|
|
bitStream.Write(iIndex);
|
|
bitStream.Write(nextIndex);
|
|
bitStream.Write(ptUnexpectedLocation.x);
|
|
bitStream.Write(ptUnexpectedLocation.y);
|
|
bitStream.Write(ptUnexpectedLocation.z);
|
|
|
|
bitStream.Write(qUnexpectedRotation != NiQuaternionConstant::IDENTITY);
|
|
if (qUnexpectedRotation != NiQuaternionConstant::IDENTITY) {
|
|
bitStream.Write(qUnexpectedRotation.x);
|
|
bitStream.Write(qUnexpectedRotation.y);
|
|
bitStream.Write(qUnexpectedRotation.z);
|
|
bitStream.Write(qUnexpectedRotation.w);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::RESTORE_TO_POST_LOAD_STATS);
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendServerDoneLoadingAllObjects(Entity* entity, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SERVER_DONE_LOADING_ALL_OBJECTS);
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendChatModeUpdate(const LWOOBJID& objectID, eGameMasterLevel level) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::UPDATE_CHAT_MODE);
|
|
bitStream.Write(level);
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendGMLevelBroadcast(const LWOOBJID& objectID, eGameMasterLevel level) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::SET_GM_LEVEL);
|
|
bitStream.Write1();
|
|
bitStream.Write(level);
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendAddItemToInventoryClientSync(Entity* entity, const SystemAddress& sysAddr, Item* item, const LWOOBJID& objectID, bool showFlyingLoot, int itemCount, LWOOBJID subKey, eLootSourceType lootSourceType) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::ADD_ITEM_TO_INVENTORY_CLIENT_SYNC);
|
|
bitStream.Write(item->GetBound());
|
|
bitStream.Write(item->GetInfo().isBOE);
|
|
bitStream.Write(item->GetInfo().isBOP);
|
|
|
|
bitStream.Write(lootSourceType != eLootSourceType::NONE); // Loot source
|
|
if (lootSourceType != eLootSourceType::NONE) bitStream.Write(lootSourceType);
|
|
LWONameValue extraInfo;
|
|
|
|
auto config = item->GetConfig();
|
|
|
|
for (auto* data : config) {
|
|
extraInfo.name += GeneralUtils::ASCIIToUTF16(data->GetString()) + u",";
|
|
}
|
|
|
|
if (extraInfo.name.length() > 0) extraInfo.name.pop_back(); // remove the last comma
|
|
|
|
bitStream.Write<uint32_t>(extraInfo.name.size());
|
|
if (extraInfo.name.size() > 0) {
|
|
for (uint32_t i = 0; i < extraInfo.name.size(); ++i) {
|
|
bitStream.Write<uint16_t>(extraInfo.name[i]);
|
|
}
|
|
bitStream.Write<uint16_t>(0x00);
|
|
}
|
|
|
|
bitStream.Write(item->GetLot());
|
|
|
|
bitStream.Write(subKey != LWOOBJID_EMPTY);
|
|
if (subKey != LWOOBJID_EMPTY) bitStream.Write(subKey);
|
|
|
|
auto* inventory = item->GetInventory();
|
|
const auto inventoryType = inventory->GetType();
|
|
|
|
bitStream.Write(inventoryType != eInventoryType::ITEMS);
|
|
if (inventoryType != eInventoryType::ITEMS) bitStream.Write(inventoryType);
|
|
|
|
bitStream.Write(itemCount != 1);
|
|
if (itemCount != 1) bitStream.Write(itemCount);
|
|
|
|
const auto count = item->GetCount();
|
|
|
|
bitStream.Write(count != 0); //items total
|
|
if (count != 0) bitStream.Write(count);
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(0.0f);
|
|
bitStream.Write(0.0f);
|
|
bitStream.Write(0.0f);
|
|
bitStream.Write(showFlyingLoot);
|
|
bitStream.Write(item->GetSlot());
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t iFlagID, bool bFlag, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::NOTIFY_CLIENT_FLAG_CHANGE);
|
|
bitStream.Write(bFlag);
|
|
bitStream.Write(iFlagID);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectWorldState state, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::CHANGE_OBJECT_WORLD_STATE);
|
|
bitStream.Write(state);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendOfferMission(const LWOOBJID& entity, const SystemAddress& sysAddr, int32_t missionID, const LWOOBJID& offererID) {
|
|
//You might be wondering.
|
|
//"Why are we sending it twice, once to a non-player object?
|
|
//Well, the first one (sent to the offerer) makes the client zoom into the object.
|
|
//The second, actually makes the UI pop up so you can be offered the mission.
|
|
//Why is it like this? Because LU isn't just a clown, it's the entire circus.
|
|
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(offererID);
|
|
bitStream.Write(MessageType::Game::OFFER_MISSION);
|
|
bitStream.Write(missionID);
|
|
bitStream.Write(offererID);
|
|
|
|
SEND_PACKET;
|
|
|
|
{
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity);
|
|
bitStream.Write(MessageType::Game::OFFER_MISSION);
|
|
bitStream.Write(missionID);
|
|
bitStream.Write(offererID);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
}
|
|
|
|
void GameMessages::SendNotifyMission(Entity* entity, const SystemAddress& sysAddr, int missionID, int missionState, bool sendingRewards) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::NOTIFY_MISSION);
|
|
bitStream.Write(missionID);
|
|
bitStream.Write(missionState);
|
|
bitStream.Write(sendingRewards);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyMissionTask(Entity* entity, const SystemAddress& sysAddr, int missionID, int taskMask, std::vector<float> updates) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::NOTIFY_MISSION_TASK);
|
|
|
|
bitStream.Write(missionID);
|
|
bitStream.Write(taskMask);
|
|
bitStream.Write<unsigned char>(updates.size());
|
|
|
|
for (uint32_t i = 0; i < updates.size(); ++i) {
|
|
bitStream.Write(updates[i]);
|
|
}
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::MODIFY_LEGO_SCORE);
|
|
bitStream.Write(score);
|
|
|
|
bitStream.Write(sourceType != eLootSourceType::NONE);
|
|
if (sourceType != eLootSourceType::NONE) bitStream.Write(sourceType);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFBaseValue& args) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::UI_MESSAGE_SERVER_TO_SINGLE_CLIENT);
|
|
|
|
bitStream.Write<AMFBaseValue&>(args);
|
|
uint32_t strMessageNameLength = message.size();
|
|
bitStream.Write(strMessageNameLength);
|
|
|
|
for (uint32_t k = 0; k < strMessageNameLength; k++) {
|
|
bitStream.Write<char>(message[k]);
|
|
}
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendUIMessageServerToSingleClient(const std::string& message, AMFBaseValue& args, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
LWOOBJID empty = 0;
|
|
bitStream.Write(empty);
|
|
bitStream.Write(MessageType::Game::UI_MESSAGE_SERVER_TO_ALL_CLIENTS); // This is intentional to allow the server to send a ui message to a client via their system address.
|
|
|
|
bitStream.Write<AMFBaseValue&>(args);
|
|
uint32_t strMessageNameLength = message.size();
|
|
bitStream.Write(strMessageNameLength);
|
|
|
|
for (uint32_t k = 0; k < strMessageNameLength; k++) {
|
|
bitStream.Write<char>(message[k]);
|
|
}
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendUIMessageServerToAllClients(const std::string& message, AMFBaseValue& args) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
LWOOBJID empty = 0;
|
|
bitStream.Write(empty);
|
|
bitStream.Write(MessageType::Game::UI_MESSAGE_SERVER_TO_ALL_CLIENTS);
|
|
|
|
bitStream.Write<AMFBaseValue&>(args);
|
|
uint32_t strMessageNameLength = message.size();
|
|
bitStream.Write(strMessageNameLength);
|
|
|
|
for (uint32_t k = 0; k < strMessageNameLength; k++) {
|
|
bitStream.Write<char>(message[k]);
|
|
}
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(Entity* entity, std::u16string effectName, const LWOOBJID& fromObjectID, float radius) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAY_EMBEDDED_EFFECT_ON_ALL_CLIENTS_NEAR_OBJECT);
|
|
|
|
bitStream.Write<uint32_t>(effectName.length());
|
|
for (uint32_t k = 0; k < effectName.length(); k++) {
|
|
bitStream.Write<uint16_t>(effectName[k]);
|
|
}
|
|
bitStream.Write(fromObjectID);
|
|
bitStream.Write(radius);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendPlayFXEffect(Entity* entity, int32_t effectID, const std::u16string& effectType, const std::string& name, LWOOBJID secondary, float priority, float scale, bool serialize) {
|
|
SendPlayFXEffect(entity->GetObjectID(), effectID, effectType, name, secondary, priority, scale, serialize);
|
|
}
|
|
|
|
void GameMessages::SendPlayFXEffect(const LWOOBJID& entity, int32_t effectID, const std::u16string& effectType, const std::string& name, LWOOBJID secondary, float priority, float scale, bool serialize) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity);
|
|
bitStream.Write(MessageType::Game::PLAY_FX_EFFECT);
|
|
|
|
bitStream.Write(effectID != -1);
|
|
if (effectID != -1) bitStream.Write(effectID);
|
|
|
|
bitStream.Write<uint32_t>(effectType.size());
|
|
for (const auto& k : effectType) {
|
|
bitStream.Write<uint16_t>(k);
|
|
}
|
|
|
|
bitStream.Write(scale != 1.0f);
|
|
if (scale != 1.0f) bitStream.Write(scale);
|
|
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (const auto& k : name) {
|
|
bitStream.Write(k);
|
|
}
|
|
|
|
bitStream.Write(priority != 1.0);
|
|
if (priority != 1.0) bitStream.Write(priority);
|
|
|
|
bitStream.Write(secondary != LWOOBJID_EMPTY);
|
|
if (secondary != LWOOBJID_EMPTY) bitStream.Write(secondary);
|
|
|
|
bitStream.Write(serialize);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendStopFXEffect(Entity* entity, bool killImmediate, std::string name) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::STOP_FX_EFFECT);
|
|
|
|
bitStream.Write(killImmediate);
|
|
bitStream.Write<uint32_t>(name.size());
|
|
bitStream.Write(name.c_str(), name.size());
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendBroadcastTextToChatbox(Entity* entity, const SystemAddress& sysAddr, const std::u16string& attrs, const std::u16string& wsText) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::BROADCAST_TEXT_TO_CHATBOX);
|
|
|
|
LWONameValue attribs;
|
|
attribs.name = attrs;
|
|
attribs.length = attrs.size();
|
|
|
|
bitStream.Write<uint32_t>(attribs.length);
|
|
for (uint32_t i = 0; i < attribs.length; ++i) {
|
|
bitStream.Write<uint16_t>(attribs.name[i]);
|
|
}
|
|
bitStream.Write<uint16_t>(0x00); // Null Terminator
|
|
|
|
uint32_t wsTextLength = wsText.size();
|
|
bitStream.Write(wsTextLength);
|
|
for (uint32_t k = 0; k < wsTextLength; k++) {
|
|
bitStream.Write<uint16_t>(wsText[k]);
|
|
}
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendSetCurrency(Entity* entity, int64_t currency, int lootType, const LWOOBJID& sourceID, const LOT& sourceLOT, int sourceTradeID, bool overrideCurrent, eLootSourceType sourceType) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_CURRENCY);
|
|
|
|
bitStream.Write(currency);
|
|
|
|
bitStream.Write(lootType != LOOTTYPE_NONE);
|
|
if (lootType != LOOTTYPE_NONE) bitStream.Write(lootType);
|
|
|
|
bitStream.Write(NiPoint3Constant::ZERO);
|
|
|
|
bitStream.Write(sourceLOT != LOT_NULL);
|
|
if (sourceLOT != LOT_NULL) bitStream.Write(sourceLOT);
|
|
|
|
bitStream.Write(sourceID != LWOOBJID_EMPTY);
|
|
if (sourceID != LWOOBJID_EMPTY) bitStream.Write(sourceID);
|
|
|
|
bitStream.Write(sourceTradeID != LWOOBJID_EMPTY);
|
|
if (sourceTradeID != LWOOBJID_EMPTY) bitStream.Write(sourceTradeID);
|
|
|
|
bitStream.Write(sourceType != eLootSourceType::NONE);
|
|
if (sourceType != eLootSourceType::NONE) bitStream.Write(sourceType);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendQuickBuildNotifyState(Entity* entity, eQuickBuildState prevState, eQuickBuildState state, const LWOOBJID& playerID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::REBUILD_NOTIFY_STATE);
|
|
|
|
bitStream.Write(prevState);
|
|
bitStream.Write(state);
|
|
bitStream.Write(playerID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendEnableQuickBuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::ENABLE_REBUILD);
|
|
|
|
bitStream.Write(enable);
|
|
bitStream.Write(fail);
|
|
bitStream.Write(success);
|
|
|
|
bitStream.Write(failReason != eQuickBuildFailReason::NOT_GIVEN);
|
|
if (failReason != eQuickBuildFailReason::NOT_GIVEN) bitStream.Write(failReason);
|
|
|
|
bitStream.Write(duration);
|
|
bitStream.Write(playerID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendTerminateInteraction(const LWOOBJID& objectID, eTerminateType type, const LWOOBJID& terminator) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::TERMINATE_INTERACTION);
|
|
|
|
bitStream.Write(terminator);
|
|
bitStream.Write(type);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendDieNoImplCode(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::DIE);
|
|
bitStream.Write(bClientDeath);
|
|
bitStream.Write(bSpawnLoot);
|
|
bitStream.Write(deathType);
|
|
bitStream.Write(directionRelative_AngleXZ);
|
|
bitStream.Write(directionRelative_AngleY);
|
|
bitStream.Write(directionRelative_Force);
|
|
|
|
bitStream.Write(killType != eKillType::VIOLENT);
|
|
if (killType != eKillType::VIOLENT) bitStream.Write(killType);
|
|
|
|
bitStream.Write(killerID);
|
|
bitStream.Write(lootOwnerID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendDie(Entity* entity, const LWOOBJID& killerID, const LWOOBJID& lootOwnerID, bool bDieAccepted, eKillType killType, std::u16string deathType, float directionRelative_AngleY, float directionRelative_AngleXZ, float directionRelative_Force, bool bClientDeath, bool bSpawnLoot, float coinSpawnTime) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
|
|
bitStream.Write(MessageType::Game::DIE);
|
|
|
|
bitStream.Write(bClientDeath);
|
|
bitStream.Write(bSpawnLoot);
|
|
|
|
//bitStream.Write(coinSpawnTime != -1.0f);
|
|
//if (coinSpawnTime != -1.0f) bitStream.Write(coinSpawnTime);
|
|
|
|
uint32_t deathTypeLength = deathType.size();
|
|
bitStream.Write(deathTypeLength);
|
|
for (uint32_t k = 0; k < deathTypeLength; k++) {
|
|
bitStream.Write<uint16_t>(deathType[k]);
|
|
}
|
|
|
|
bitStream.Write(directionRelative_AngleXZ);
|
|
bitStream.Write(directionRelative_AngleY);
|
|
bitStream.Write(directionRelative_Force);
|
|
|
|
bitStream.Write(killType != eKillType::VIOLENT);
|
|
if (killType != eKillType::VIOLENT) bitStream.Write(killType);
|
|
|
|
bitStream.Write(killerID);
|
|
|
|
bitStream.Write(lootOwnerID != LWOOBJID_EMPTY);
|
|
if (lootOwnerID != LWOOBJID_EMPTY) {
|
|
bitStream.Write(lootOwnerID);
|
|
}
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendSetInventorySize(Entity* entity, int invType, int size) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_INVENTORY_SIZE);
|
|
bitStream.Write(invType);
|
|
bitStream.Write(size);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetEmoteLockState(Entity* entity, bool bLock, int emoteID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_EMOTE_LOCK_STATE);
|
|
bitStream.Write(bLock);
|
|
bitStream.Write(emoteID);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks, bool doHover, int effectID, float airspeed, float maxAirspeed, float verticalVelocity, int warningEffectID) {
|
|
/* historical jamesster jetpack values
|
|
if (bIsJamessterPhysics) {
|
|
fAirspeed = 75;
|
|
fMaxAirspeed = 75;
|
|
fVertVel = 15;
|
|
}
|
|
*/
|
|
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_JET_PACK_MODE);
|
|
|
|
bitStream.Write(bypassChecks);
|
|
bitStream.Write(doHover);
|
|
bitStream.Write(use);
|
|
|
|
bitStream.Write(effectID != -1);
|
|
if (effectID != -1) bitStream.Write(effectID);
|
|
|
|
bitStream.Write(airspeed != 10);
|
|
if (airspeed != 10) bitStream.Write(airspeed);
|
|
|
|
bitStream.Write(maxAirspeed != 15);
|
|
if (maxAirspeed != 15) bitStream.Write(maxAirspeed);
|
|
|
|
bitStream.Write(verticalVelocity != 1);
|
|
if (verticalVelocity != 1) bitStream.Write(verticalVelocity);
|
|
|
|
bitStream.Write(warningEffectID != -1);
|
|
if (warningEffectID != -1) bitStream.Write(warningEffectID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendResurrect(Entity* entity) {
|
|
// Restore the players health after the animation for respawning has finished.
|
|
// This is when the health appered back in live, not immediately upon requesting respawn
|
|
// Add a half second in case someone decides to cheat and move during the death animation
|
|
// and just make sure the client has time to be ready.
|
|
constexpr float respawnTime = 3.66700005531311f + 0.5f;
|
|
entity->AddCallbackTimer(respawnTime, [=]() {
|
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
|
|
|
if (destroyableComponent != nullptr && entity->GetLOT() == 1) {
|
|
auto* levelComponent = entity->GetComponent<LevelProgressionComponent>();
|
|
if (levelComponent) {
|
|
int32_t healthToRestore = levelComponent->GetLevel() >= 45 ? 8 : 4;
|
|
if (healthToRestore > destroyableComponent->GetMaxHealth()) healthToRestore = destroyableComponent->GetMaxHealth();
|
|
destroyableComponent->SetHealth(healthToRestore);
|
|
|
|
int32_t imaginationToRestore = levelComponent->GetLevel() >= 45 ? 20 : 6;
|
|
if (imaginationToRestore > destroyableComponent->GetMaxImagination()) imaginationToRestore = destroyableComponent->GetMaxImagination();
|
|
destroyableComponent->SetImagination(imaginationToRestore);
|
|
}
|
|
}
|
|
});
|
|
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bool bRezImmediately = false;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::RESURRECT);
|
|
bitStream.Write(bRezImmediately);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendStop2DAmbientSound(Entity* entity, bool force, std::string audioGUID, bool result) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::STOP2_D_AMBIENT_SOUND);
|
|
|
|
uint32_t audioGUIDSize = audioGUID.size();
|
|
|
|
bitStream.Write(force);
|
|
bitStream.Write(audioGUIDSize);
|
|
|
|
for (uint32_t k = 0; k < audioGUIDSize; k++) {
|
|
bitStream.Write<char>(audioGUID[k]);
|
|
}
|
|
|
|
bitStream.Write(result);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlay2DAmbientSound(Entity* entity, std::string audioGUID, bool result) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAY2_D_AMBIENT_SOUND);
|
|
uint32_t audioGUIDSize = audioGUID.size();
|
|
|
|
bitStream.Write(audioGUIDSize);
|
|
for (uint32_t k = 0; k < audioGUIDSize; k++) {
|
|
bitStream.Write<char>(audioGUID[k]);
|
|
}
|
|
bitStream.Write(result);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SCRIPT_NETWORK_VAR_UPDATE);
|
|
|
|
// FIXME: this is a bad place to need to do a conversion because we have no clue whether data is utf8 or plain ascii
|
|
// an this has performance implications
|
|
const auto u16Data = GeneralUtils::ASCIIToUTF16(data);
|
|
uint32_t dataSize = static_cast<uint32_t>(u16Data.size());
|
|
|
|
bitStream.Write(dataSize);
|
|
for (auto value : u16Data) {
|
|
bitStream.Write<uint16_t>(value);
|
|
}
|
|
if (dataSize > 0) bitStream.Write<uint16_t>(0);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LOT item, int currency, NiPoint3 spawnPos, int count) {
|
|
if (Game::config->GetValue("disable_drops") == "1" || !entity) {
|
|
return;
|
|
}
|
|
|
|
bool bUsePosition = false;
|
|
NiPoint3 finalPosition;
|
|
LWOOBJID lootID = LWOOBJID_EMPTY;
|
|
LWOOBJID owner = entity->GetObjectID();
|
|
|
|
if (item != LOT_NULL && item != 0) {
|
|
lootID = ObjectIDManager::GenerateObjectID();
|
|
|
|
Loot::Info info;
|
|
info.id = lootID;
|
|
info.count = count;
|
|
info.lot = item;
|
|
entity->AddLootItem(info);
|
|
}
|
|
|
|
if (item == LOT_NULL && currency != 0) {
|
|
entity->RegisterCoinDrop(currency);
|
|
}
|
|
|
|
if (spawnPos != NiPoint3Constant::ZERO) {
|
|
bUsePosition = true;
|
|
|
|
//Calculate where the loot will go:
|
|
uint16_t degree = GeneralUtils::GenerateRandomNumber<uint16_t>(0, 360);
|
|
|
|
double rad = degree * 3.14 / 180;
|
|
double sin_v = sin(rad) * 4.2;
|
|
double cos_v = cos(rad) * 4.2;
|
|
|
|
finalPosition = NiPoint3(static_cast<float>(spawnPos.GetX() + sin_v), spawnPos.GetY(), static_cast<float>(spawnPos.GetZ() + cos_v));
|
|
}
|
|
|
|
//Write data to packet & send:
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::DROP_CLIENT_LOOT);
|
|
|
|
bitStream.Write(bUsePosition);
|
|
|
|
bitStream.Write(finalPosition != NiPoint3Constant::ZERO);
|
|
if (finalPosition != NiPoint3Constant::ZERO) bitStream.Write(finalPosition);
|
|
|
|
bitStream.Write(currency);
|
|
bitStream.Write(item);
|
|
bitStream.Write(lootID);
|
|
bitStream.Write(owner);
|
|
bitStream.Write(sourceID);
|
|
|
|
bitStream.Write(spawnPos != NiPoint3Constant::ZERO);
|
|
if (spawnPos != NiPoint3Constant::ZERO) bitStream.Write(spawnPos);
|
|
|
|
auto* team = TeamManager::Instance()->GetTeam(owner);
|
|
|
|
// Currency and powerups should not sync
|
|
if (team != nullptr && currency == 0) {
|
|
CDObjectsTable* objectsTable = CDClientManager::GetTable<CDObjectsTable>();
|
|
|
|
const CDObjects& object = objectsTable->GetByID(item);
|
|
|
|
if (object.type != "Powerup") {
|
|
for (const auto memberId : team->members) {
|
|
auto* member = Game::entityManager->GetEntity(memberId);
|
|
|
|
if (member == nullptr) continue;
|
|
|
|
SystemAddress sysAddr = member->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bool bDelayCamSwitchIfInCinematic = true;
|
|
bool bSwitchCam = true;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_PLAYER_CONTROL_SCHEME);
|
|
|
|
bitStream.Write(bDelayCamSwitchIfInCinematic);
|
|
bitStream.Write(bSwitchCam);
|
|
|
|
bitStream.Write(controlScheme != eControlScheme::SCHEME_A);
|
|
if (controlScheme != eControlScheme::SCHEME_A) bitStream.Write(controlScheme);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAYER_REACHED_RESPAWN_CHECKPOINT);
|
|
|
|
bitStream.Write(position.x);
|
|
bitStream.Write(position.y);
|
|
bitStream.Write(position.z);
|
|
|
|
const bool bIsNotIdentity = rotation != NiQuaternionConstant::IDENTITY;
|
|
bitStream.Write(bIsNotIdentity);
|
|
|
|
if (bIsNotIdentity) {
|
|
bitStream.Write(rotation.w);
|
|
bitStream.Write(rotation.x);
|
|
bitStream.Write(rotation.y);
|
|
bitStream.Write(rotation.z);
|
|
}
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendAddSkill(Entity* entity, TSkillID skillID, BehaviorSlot slotID) {
|
|
int AICombatWeight = 0;
|
|
bool bFromSkillSet = false;
|
|
int castType = 0;
|
|
float fTimeSecs = -1.0f;
|
|
int iTimesCanCast = -1;
|
|
bool temporary = true;
|
|
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::ADD_SKILL);
|
|
|
|
bitStream.Write(AICombatWeight != 0);
|
|
if (AICombatWeight != 0) bitStream.Write(AICombatWeight);
|
|
|
|
bitStream.Write(bFromSkillSet);
|
|
|
|
bitStream.Write(castType != 0);
|
|
if (castType != 0) bitStream.Write(castType);
|
|
|
|
bitStream.Write(fTimeSecs != -1.0f);
|
|
if (fTimeSecs != -1.0f) bitStream.Write(fTimeSecs);
|
|
|
|
bitStream.Write(iTimesCanCast != -1);
|
|
if (iTimesCanCast != -1) bitStream.Write(iTimesCanCast);
|
|
|
|
bitStream.Write(skillID);
|
|
|
|
bitStream.Write(slotID != BehaviorSlot::Invalid);
|
|
if (slotID != BehaviorSlot::Invalid) bitStream.Write(slotID);
|
|
|
|
bitStream.Write(temporary);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRemoveSkill(Entity* entity, TSkillID skillID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::REMOVE_SKILL);
|
|
bitStream.Write(false);
|
|
bitStream.Write(skillID);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendFinishArrangingWithItem(Entity* entity, const LWOOBJID& buildArea) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bool bFirstTime = true;
|
|
const LWOOBJID& buildAreaID = buildArea;
|
|
int newSourceBAG = 0;
|
|
const LWOOBJID& newSourceID = LWOOBJID_EMPTY;
|
|
LOT newSourceLOT = LOT_NULL;
|
|
int newSourceTYPE = 0;
|
|
const LWOOBJID& newTargetID = LWOOBJID_EMPTY;
|
|
LOT newTargetLOT = LOT_NULL;
|
|
int newTargetTYPE = 0;
|
|
NiPoint3 newTargetPOS = NiPoint3();
|
|
int oldItemBAG = 0;
|
|
const LWOOBJID& oldItemID = LWOOBJID_EMPTY;
|
|
LOT oldItemLOT = LOT_NULL;
|
|
int oldItemTYPE = 0;
|
|
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::FINISH_ARRANGING_WITH_ITEM);
|
|
|
|
bitStream.Write(buildAreaID != LWOOBJID_EMPTY);
|
|
if (buildAreaID != LWOOBJID_EMPTY) bitStream.Write(buildAreaID);
|
|
bitStream.Write(newSourceBAG);
|
|
bitStream.Write(newSourceID);
|
|
bitStream.Write(newSourceLOT);
|
|
bitStream.Write(newSourceTYPE);
|
|
bitStream.Write(newTargetID);
|
|
bitStream.Write(newTargetLOT);
|
|
bitStream.Write(newTargetTYPE);
|
|
bitStream.Write(newTargetPOS);
|
|
bitStream.Write(oldItemBAG);
|
|
bitStream.Write(oldItemID);
|
|
bitStream.Write(oldItemLOT);
|
|
bitStream.Write(oldItemTYPE);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendModularBuildEnd(Entity* entity) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::MODULAR_BUILD_END);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendVendorOpenWindow(Entity* entity, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::VENDOR_OPEN_WINDOW);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
VendorComponent* vendor = static_cast<VendorComponent*>(entity->GetComponent(eReplicaComponentType::VENDOR));
|
|
if (!vendor) return;
|
|
|
|
auto vendorItems = vendor->GetInventory();
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::VENDOR_STATUS_UPDATE);
|
|
|
|
bitStream.Write(bUpdateOnly);
|
|
bitStream.Write<uint32_t>(vendorItems.size());
|
|
|
|
|
|
for (const auto& item : vendorItems) {
|
|
bitStream.Write(item.lot);
|
|
bitStream.Write(item.sortPriority);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr, eVendorTransactionResult result) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::VENDOR_TRANSACTION_RESULT);
|
|
bitStream.Write(result);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRemoveItemFromInventory(Entity* entity, const SystemAddress& sysAddr, LWOOBJID objectID, LOT templateID, int inventoryType, uint32_t stackCount, uint32_t stackRemaining) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
// this is used for a lot more than just inventory trashing (trades, vendors, etc.) but for now since it's just used for that, that's all im going to implement
|
|
bool bConfirmed = true;
|
|
bool bDeleteItem = true;
|
|
bool bOutSuccess = false;
|
|
int eInvType = inventoryType;
|
|
int eLootTypeSource = LOOTTYPE_NONE;
|
|
bool forceDeletion = true;
|
|
LWOOBJID iLootTypeSource = LWOOBJID_EMPTY;
|
|
LWOOBJID iObjID = objectID;
|
|
LOT iObjTemplate = templateID;
|
|
LWOOBJID iRequestingObjID = LWOOBJID_EMPTY;
|
|
uint32_t iStackCount = stackCount;
|
|
uint32_t iStackRemaining = stackRemaining;
|
|
LWOOBJID iSubkey = LWOOBJID_EMPTY;
|
|
LWOOBJID iTradeID = LWOOBJID_EMPTY;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::REMOVE_ITEM_FROM_INVENTORY);
|
|
bitStream.Write(bConfirmed);
|
|
bitStream.Write(bDeleteItem);
|
|
bitStream.Write(bOutSuccess);
|
|
bitStream.Write1();
|
|
bitStream.Write(eInvType);
|
|
bitStream.Write1();
|
|
bitStream.Write(eLootTypeSource);
|
|
bitStream.Write<uint32_t>(0); //extra info
|
|
//bitStream.Write<uint16_t>(0); //extra info
|
|
bitStream.Write(forceDeletion);
|
|
bitStream.Write0();
|
|
bitStream.Write1();
|
|
bitStream.Write(iObjID);
|
|
bitStream.Write1();
|
|
bitStream.Write(iObjTemplate);
|
|
bitStream.Write0();
|
|
bitStream.Write1();
|
|
bitStream.Write(iStackCount);
|
|
bitStream.Write1();
|
|
bitStream.Write(iStackRemaining);
|
|
bitStream.Write0();
|
|
bitStream.Write0();
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendConsumeClientItem(Entity* entity, bool bSuccess, LWOOBJID item) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::CONSUME_CLIENT_ITEM);
|
|
bitStream.Write(bSuccess);
|
|
bitStream.Write(item);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendUseItemResult(Entity* entity, LOT templateID, bool useItemResult) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::USE_ITEM_RESULT);
|
|
bitStream.Write(templateID);
|
|
bitStream.Write(useItemResult);
|
|
|
|
SystemAddress sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendUseItemRequirementsResponse(LWOOBJID objectID, const SystemAddress& sysAddr, eUseItemResponse itemResponse) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::USE_ITEM_REQUIREMENTS_RESPONSE);
|
|
|
|
bitStream.Write(itemResponse);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendMoveInventoryBatch(Entity* entity, uint32_t stackCount, int srcInv, int dstInv, const LWOOBJID& iObjID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
Item* itemStack = inv->FindItemById(iObjID);
|
|
if (!itemStack) return;
|
|
|
|
bool bAllowPartial = false;
|
|
bool bOutSuccess = false;
|
|
uint32_t count = stackCount;
|
|
int dstBag = dstInv;
|
|
LOT moveLOT = itemStack->GetLot();
|
|
//LWOOBJID moveSubkey = LWOOBJID_EMPTY;
|
|
bool showFlyingLoot = false;
|
|
int srcBag = srcInv;
|
|
LWOOBJID startObjectID = iObjID;
|
|
|
|
bitStream.Write(bAllowPartial);
|
|
bitStream.Write(bOutSuccess);
|
|
if (count == 1) {
|
|
bitStream.Write0();
|
|
} else {
|
|
bitStream.Write1();
|
|
bitStream.Write(count);
|
|
}
|
|
if (dstBag == 0) {
|
|
bitStream.Write0();
|
|
} else {
|
|
bitStream.Write1();
|
|
bitStream.Write(dstBag);
|
|
}
|
|
bitStream.Write0();
|
|
bitStream.Write(showFlyingLoot);
|
|
if (srcBag == 0) {
|
|
bitStream.Write0();
|
|
} else {
|
|
bitStream.Write1();
|
|
bitStream.Write(srcBag);
|
|
}
|
|
bitStream.Write1();
|
|
bitStream.Write(startObjectID);
|
|
|
|
auto sysAddr = entity->GetSystemAddress();
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendMatchResponse(Entity* entity, const SystemAddress& sysAddr, int response) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::MATCH_RESPONSE);
|
|
bitStream.Write(response);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, std::string data, eMatchUpdate type) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::MATCH_UPDATE);
|
|
bitStream.Write<uint32_t>(data.size());
|
|
for (char character : data) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
if (data.size() > 0) bitStream.Write<uint16_t>(0);
|
|
bitStream.Write(type);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRequestActivitySummaryLeaderboardData(const LWOOBJID& objectID, const LWOOBJID& targetID,
|
|
const SystemAddress& sysAddr, const int32_t& gameID,
|
|
const int32_t& queryType, const int32_t& resultsEnd,
|
|
const int32_t& resultsStart, bool weekly) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA);
|
|
|
|
bitStream.Write(gameID != 0);
|
|
if (gameID != 0) {
|
|
bitStream.Write<int32_t>(gameID);
|
|
}
|
|
|
|
bitStream.Write(queryType != 1);
|
|
if (queryType != 1) {
|
|
bitStream.Write<int32_t>(queryType);
|
|
}
|
|
|
|
bitStream.Write(resultsEnd != 10);
|
|
if (resultsEnd != 10) {
|
|
bitStream.Write<int32_t>(resultsEnd);
|
|
}
|
|
|
|
bitStream.Write(resultsStart != 0);
|
|
if (resultsStart != 0) {
|
|
bitStream.Write<int32_t>(resultsStart);
|
|
}
|
|
|
|
bitStream.Write<LWOOBJID>(targetID);
|
|
bitStream.Write(weekly);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendActivityPause(LWOOBJID objectId, bool pause, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ACTIVITY_PAUSE);
|
|
bitStream.Write(pause);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendStartActivityTime(LWOOBJID objectId, float_t startTime, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::START_ACTIVITY_TIME);
|
|
bitStream.Write<float_t>(startTime);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRequestActivityEnter(LWOOBJID objectId, const SystemAddress& sysAddr, bool bStart, LWOOBJID userID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::REQUEST_ACTIVITY_ENTER);
|
|
bitStream.Write<bool>(bStart);
|
|
bitStream.Write<LWOOBJID>(userID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::NotifyLevelRewards(LWOOBJID objectID, const SystemAddress& sysAddr, int level, bool sending_rewards) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::NOTIFY_LEVEL_REWARDS);
|
|
|
|
bitStream.Write(level);
|
|
bitStream.Write(sending_rewards);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetShootingGalleryParams(LWOOBJID objectId, const SystemAddress& sysAddr,
|
|
float cameraFOV,
|
|
float cooldown,
|
|
float minDistance,
|
|
NiPoint3 muzzlePosOffset,
|
|
NiPoint3 playerPosOffset,
|
|
float projectileVelocity,
|
|
float timeLimit,
|
|
bool bUseLeaderboards) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_SHOOTING_GALLERY_PARAMS);
|
|
/*
|
|
bitStream.Write<float>(cameraFOV);
|
|
bitStream.Write<float>(cooldown);
|
|
bitStream.Write<float>(minDistance);
|
|
bitStream.Write<NiPoint3>(muzzlePosOffset);
|
|
bitStream.Write<NiPoint3>(playerPosOffset);
|
|
bitStream.Write<float>(projectileVelocity);
|
|
bitStream.Write<float>(timeLimit);
|
|
bitStream.Write<bool>(bUseLeaderboards);
|
|
*/
|
|
// No clue about the order here
|
|
bitStream.Write<NiPoint3>(playerPosOffset);
|
|
bitStream.Write<float>(projectileVelocity);
|
|
bitStream.Write<float>(cooldown);
|
|
bitStream.Write<NiPoint3>(muzzlePosOffset);
|
|
bitStream.Write<float>(minDistance);
|
|
bitStream.Write<float>(cameraFOV);
|
|
bitStream.Write<bool>(bUseLeaderboards);
|
|
bitStream.Write<float>(timeLimit);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const SystemAddress& sysAddr,
|
|
float addTime,
|
|
int32_t score,
|
|
LWOOBJID target,
|
|
NiPoint3 targetPos) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE);
|
|
bitStream.Write<float>(addTime);
|
|
bitStream.Write<int32_t>(score);
|
|
bitStream.Write<LWOOBJID>(target);
|
|
bitStream.Write<NiPoint3>(targetPos);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::HandleUpdateShootingGalleryRotation(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
float angle = 0.0f;
|
|
NiPoint3 facing = NiPoint3Constant::ZERO;
|
|
NiPoint3 muzzlePos = NiPoint3Constant::ZERO;
|
|
inStream.Read(angle);
|
|
inStream.Read(facing);
|
|
inStream.Read(muzzlePos);
|
|
}
|
|
|
|
|
|
void GameMessages::HandleActivitySummaryLeaderboardData(RakNet::BitStream& inStream, Entity* entity,
|
|
const SystemAddress& sysAddr) {
|
|
LOG("We got mail!");
|
|
}
|
|
|
|
void GameMessages::SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA);
|
|
|
|
leaderboard->Serialize(bitStream);
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleRequestActivitySummaryLeaderboardData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
int32_t gameID = 0;
|
|
if (inStream.ReadBit()) inStream.Read(gameID);
|
|
|
|
Leaderboard::InfoType queryType = Leaderboard::InfoType::MyStanding;
|
|
if (inStream.ReadBit()) inStream.Read<Leaderboard::InfoType>(queryType);
|
|
|
|
int32_t resultsEnd = 10;
|
|
if (inStream.ReadBit()) inStream.Read(resultsEnd);
|
|
|
|
int32_t resultsStart = 0;
|
|
if (inStream.ReadBit()) inStream.Read(resultsStart);
|
|
|
|
LWOOBJID target{};
|
|
inStream.Read(target);
|
|
|
|
bool weekly = inStream.ReadBit();
|
|
|
|
LeaderboardManager::SendLeaderboard(gameID, queryType, weekly, entity->GetObjectID(), entity->GetObjectID(), resultsStart, resultsEnd);
|
|
}
|
|
|
|
void GameMessages::HandleActivityStateChangeRequest(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID objectID;
|
|
inStream.Read<LWOOBJID>(objectID);
|
|
|
|
int32_t value1;
|
|
inStream.Read<int32_t>(value1);
|
|
|
|
int32_t value2;
|
|
inStream.Read<int32_t>(value2);
|
|
|
|
uint32_t stringValueLength;
|
|
inStream.Read<uint32_t>(stringValueLength);
|
|
|
|
std::u16string stringValue;
|
|
for (uint32_t i = 0; i < stringValueLength; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
stringValue.push_back(character);
|
|
}
|
|
|
|
auto* assosiate = Game::entityManager->GetEntity(objectID);
|
|
|
|
LOG("%s [%i, %i] from %i to %i", GeneralUtils::UTF16ToWTF8(stringValue).c_str(), value1, value2, entity->GetLOT(), assosiate != nullptr ? assosiate->GetLOT() : 0);
|
|
|
|
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY);
|
|
for (Entity* scriptEntity : scriptedActs) {
|
|
scriptEntity->OnActivityStateChangeRequest(objectID, value1, value2, stringValue);
|
|
}
|
|
|
|
entity->OnActivityStateChangeRequest(objectID, value1, value2, stringValue);
|
|
}
|
|
|
|
void GameMessages::SendStartCelebrationEffect(Entity* entity, const SystemAddress& sysAddr, int celebrationID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::START_CELEBRATION_EFFECT);
|
|
|
|
bitStream.Write<uint32_t>(0); //animation
|
|
bitStream.Write0(); //No custom bg obj
|
|
bitStream.Write0(); //custom path
|
|
bitStream.Write0(); //leadin
|
|
bitStream.Write0(); //leadout
|
|
bitStream.Write1();
|
|
bitStream.Write(celebrationID);
|
|
bitStream.Write<float>(0.0f); //duration
|
|
bitStream.Write<uint32_t>(0); //icon
|
|
bitStream.Write<uint32_t>(0); //text
|
|
bitStream.Write<uint32_t>(0); //mixer
|
|
bitStream.Write<uint32_t>(0); //cue
|
|
bitStream.Write<uint32_t>(0); //pathnodename
|
|
bitStream.Write<uint32_t>(0); //soundguid
|
|
bitStream.Write<uint32_t>(0); //subtext
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendSetRailMovement(const LWOOBJID& objectID, bool pathGoForward, std::u16string pathName,
|
|
uint32_t pathStart, const SystemAddress& sysAddr, int32_t railActivatorComponentID,
|
|
LWOOBJID railActivatorObjectID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::SET_RAIL_MOVEMENT);
|
|
|
|
bitStream.Write(pathGoForward);
|
|
|
|
bitStream.Write<uint32_t>(pathName.size());
|
|
for (auto character : pathName) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write<uint32_t>(pathStart);
|
|
|
|
const auto componentIDIsDefault = railActivatorComponentID == -1;
|
|
bitStream.Write(!componentIDIsDefault);
|
|
if (!componentIDIsDefault)
|
|
bitStream.Write<int32_t>(railActivatorComponentID);
|
|
|
|
const auto activatorObjectIDIsDefault = railActivatorObjectID == LWOOBJID_EMPTY;
|
|
bitStream.Write(!activatorObjectIDIsDefault);
|
|
if (!activatorObjectIDIsDefault)
|
|
bitStream.Write<LWOOBJID>(railActivatorObjectID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendStartRailMovement(const LWOOBJID& objectID, std::u16string pathName, std::u16string startSound,
|
|
std::u16string loopSound, std::u16string stopSound, const SystemAddress& sysAddr,
|
|
uint32_t pathStart, bool goForward, bool damageImmune, bool noAggro, bool notifyActor,
|
|
bool showNameBillboard, bool cameraLocked, bool collisionEnabled, bool useDB,
|
|
int32_t railComponentID, LWOOBJID railActivatorObjectID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::START_RAIL_MOVEMENT);
|
|
|
|
bitStream.Write(damageImmune);
|
|
bitStream.Write(noAggro);
|
|
bitStream.Write(notifyActor);
|
|
bitStream.Write(showNameBillboard);
|
|
bitStream.Write(cameraLocked);
|
|
bitStream.Write(collisionEnabled);
|
|
|
|
bitStream.Write<uint32_t>(loopSound.size());
|
|
for (auto character : loopSound) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write(goForward);
|
|
|
|
bitStream.Write<uint32_t>(pathName.size());
|
|
for (auto character : pathName) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
const auto pathStartIsDefault = pathStart == 0;
|
|
bitStream.Write(!pathStartIsDefault);
|
|
if (!pathStartIsDefault) {
|
|
bitStream.Write<uint32_t>(pathStart);
|
|
}
|
|
|
|
const auto railComponentIDIsDefault = railComponentID == -1;
|
|
bitStream.Write(!railComponentIDIsDefault);
|
|
if (!railComponentIDIsDefault) {
|
|
bitStream.Write<int32_t>(railComponentID);
|
|
}
|
|
|
|
const auto railObjectIDIsDefault = railActivatorObjectID == LWOOBJID_EMPTY;
|
|
bitStream.Write(!railObjectIDIsDefault);
|
|
if (!railObjectIDIsDefault) {
|
|
bitStream.Write<LWOOBJID>(railActivatorObjectID);
|
|
}
|
|
|
|
bitStream.Write<uint32_t>(startSound.size());
|
|
for (auto character : startSound) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write<uint32_t>(stopSound.size());
|
|
for (auto character : stopSound) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write(useDB);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyClientObject(const LWOOBJID& objectID, std::u16string name, int param1, int param2, const LWOOBJID& paramObj, std::string paramStr, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::NOTIFY_CLIENT_OBJECT);
|
|
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (auto character : name) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write(param1);
|
|
|
|
bitStream.Write(param2);
|
|
|
|
bitStream.Write(paramObj);
|
|
|
|
bitStream.Write<uint32_t>(paramStr.size());
|
|
for (auto character : paramStr) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyClientZoneObject(const LWOOBJID& objectID, const std::u16string& name, int param1,
|
|
int param2, const LWOOBJID& paramObj, const std::string& paramStr,
|
|
const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::NOTIFY_CLIENT_ZONE_OBJECT);
|
|
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (const auto& character : name) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write(param1);
|
|
bitStream.Write(param2);
|
|
bitStream.Write(paramObj);
|
|
|
|
bitStream.Write<uint32_t>(paramStr.size());
|
|
for (const auto& character : paramStr) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyClientFailedPrecondition(LWOOBJID objectId, const SystemAddress& sysAddr,
|
|
const std::u16string& failedReason, int preconditionID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_CLIENT_FAILED_PRECONDITION);
|
|
|
|
bitStream.Write<uint32_t>(failedReason.size());
|
|
for (uint16_t character : failedReason) {
|
|
bitStream.Write<uint16_t>(character);
|
|
}
|
|
|
|
bitStream.Write(preconditionID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendToggleGMInvis(LWOOBJID objectId, bool enabled, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::TOGGLE_GM_INVIS);
|
|
bitStream.Write(enabled); // does not matter?
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::SET_NAME);
|
|
|
|
bitStream.Write<uint32_t>(name.size());
|
|
|
|
for (size_t i = 0; i < name.size(); ++i)
|
|
bitStream.Write(name[i]);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendBBBSaveResponse(const LWOOBJID& objectId, const LWOOBJID& localID, unsigned char* buffer, uint32_t bufferSize, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::BBB_SAVE_RESPONSE);
|
|
|
|
bitStream.Write(localID);
|
|
|
|
//Write a fake sd0 header:
|
|
bitStream.Write<unsigned char>(0x73); //s
|
|
bitStream.Write<unsigned char>(0x64); //d
|
|
bitStream.Write<unsigned char>(0x30); //0
|
|
bitStream.Write<unsigned char>(0x01); //1
|
|
bitStream.Write<unsigned char>(0xFF); //end magic
|
|
|
|
bitStream.Write(bufferSize);
|
|
|
|
for (size_t i = 0; i < bufferSize; ++i)
|
|
bitStream.Write(buffer[i]);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
// Property
|
|
|
|
void GameMessages::SendOpenPropertyVendor(const LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::OPEN_PROPERTY_VENDOR);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendOpenPropertyManagment(const LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(PropertyManagementComponent::Instance()->GetParent()->GetObjectID());
|
|
bitStream.Write(MessageType::Game::OPEN_PROPERTY_MANAGEMENT);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendDownloadPropertyData(const LWOOBJID objectId, const PropertyDataMessage& data, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::DOWNLOAD_PROPERTY_DATA);
|
|
|
|
data.Serialize(bitStream);
|
|
|
|
LOG("(%llu) sending property data (%d)", objectId, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPropertyRentalResponse(const LWOOBJID objectId, const LWOCLONEID cloneId, const uint32_t code, const LWOOBJID propertyId, const int64_t rentDue, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PROPERTY_RENTAL_RESPONSE);
|
|
|
|
bitStream.Write(cloneId);
|
|
bitStream.Write(code);
|
|
bitStream.Write(propertyId);
|
|
bitStream.Write(rentDue);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendLockNodeRotation(Entity* entity, std::string nodeName) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::LOCK_NODE_ROTATION);
|
|
|
|
bitStream.Write<uint32_t>(nodeName.size());
|
|
for (char character : nodeName) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendSetBuildModeConfirmed(LWOOBJID objectId, const SystemAddress& sysAddr, bool start, bool warnVisitors, bool modePaused, int32_t modeValue, LWOOBJID playerId, NiPoint3 startPos) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_BUILD_MODE_CONFIRMED);
|
|
|
|
bitStream.Write(start);
|
|
bitStream.Write(warnVisitors);
|
|
bitStream.Write(modePaused);
|
|
bitStream.Write1();
|
|
bitStream.Write(modeValue);
|
|
bitStream.Write(playerId);
|
|
bitStream.Write1();
|
|
bitStream.Write(startPos);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendGetModelsOnProperty(LWOOBJID objectId, std::map<LWOOBJID, LWOOBJID> models, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::GET_MODELS_ON_PROPERTY);
|
|
|
|
bitStream.Write<uint32_t>(models.size());
|
|
|
|
for (const auto& pair : models) {
|
|
bitStream.Write(pair.first);
|
|
bitStream.Write(pair.second);
|
|
}
|
|
|
|
LOG("Sending property models to (%llu) (%d)", objectId, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendZonePropertyModelEquipped(LWOOBJID objectId, LWOOBJID playerId, LWOOBJID propertyId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ZONE_PROPERTY_MODEL_EQUIPPED);
|
|
|
|
bitStream.Write(playerId);
|
|
bitStream.Write(propertyId);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlaceModelResponse(LWOOBJID objectId, const SystemAddress& sysAddr, NiPoint3 position,
|
|
LWOOBJID plaque, int32_t response, NiQuaternion rotation) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PLACE_MODEL_RESPONSE);
|
|
|
|
bitStream.Write(position != NiPoint3Constant::ZERO);
|
|
if (position != NiPoint3Constant::ZERO) {
|
|
bitStream.Write(position);
|
|
}
|
|
|
|
bitStream.Write(plaque != LWOOBJID_EMPTY);
|
|
if (plaque != LWOOBJID_EMPTY) {
|
|
bitStream.Write(plaque);
|
|
}
|
|
|
|
bitStream.Write(response != 0);
|
|
if (response != 0) {
|
|
bitStream.Write(response);
|
|
}
|
|
|
|
bitStream.Write(rotation != NiQuaternionConstant::IDENTITY);
|
|
if (rotation != NiQuaternionConstant::IDENTITY) {
|
|
bitStream.Write(response);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
|
|
}
|
|
|
|
void GameMessages::SendUGCEquipPreCreateBasedOnEditMode(LWOOBJID objectId, const SystemAddress& sysAddr, int modelCount, LWOOBJID model) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::HANDLE_UGC_POST_CREATE_BASED_ON_EDIT_MODE);
|
|
|
|
bitStream.Write(modelCount);
|
|
bitStream.Write(model);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendUGCEquipPostDeleteBasedOnEditMode(LWOOBJID objectId, const SystemAddress& sysAddr, LWOOBJID inventoryItem, int itemTotal) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::HANDLE_UGC_POST_DELETE_BASED_ON_EDIT_MODE);
|
|
|
|
bitStream.Write(inventoryItem);
|
|
|
|
bitStream.Write(itemTotal != 0);
|
|
if (itemTotal != 0) {
|
|
bitStream.Write(itemTotal);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleSetPropertyAccess(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint8_t accessType{};
|
|
int32_t renew{};
|
|
|
|
bool accessTypeIsDefault{};
|
|
inStream.Read(accessTypeIsDefault);
|
|
if (accessTypeIsDefault != 0) inStream.Read(accessType);
|
|
|
|
bool renewIsDefault{};
|
|
inStream.Read(renewIsDefault);
|
|
if (renewIsDefault != 0) inStream.Read(renew);
|
|
|
|
LOG("Set privacy option to: %i", accessType);
|
|
|
|
if (PropertyManagementComponent::Instance() == nullptr) return;
|
|
|
|
PropertyManagementComponent::Instance()->SetPrivacyOption(static_cast<PropertyPrivacyOption>(accessType));
|
|
}
|
|
|
|
void GameMessages::HandleUnUseModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool unknown{};
|
|
LWOOBJID objIdToAddToInventory{};
|
|
inStream.Read(unknown);
|
|
inStream.Read(objIdToAddToInventory);
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
if (inventoryComponent) {
|
|
auto* inventory = inventoryComponent->GetInventory(eInventoryType::MODELS_IN_BBB);
|
|
auto* item = inventory->FindItemById(objIdToAddToInventory);
|
|
if (item) {
|
|
inventoryComponent->MoveItemToInventory(item, eInventoryType::MODELS, 1);
|
|
} else {
|
|
LOG("item id %llu not found in MODELS_IN_BBB inventory, likely because it does not exist", objIdToAddToInventory);
|
|
}
|
|
}
|
|
|
|
if (unknown) {
|
|
CBITSTREAM;
|
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::BLUEPRINT_SAVE_RESPONSE);
|
|
bitStream.Write<LWOOBJID>(LWOOBJID_EMPTY); //always zero so that a check on the client passes
|
|
bitStream.Write(eBlueprintSaveResponseType::PlacementFailed); // Sending a non-zero error code here prevents the client from deleting its in progress build for some reason?
|
|
bitStream.Write<uint32_t>(0);
|
|
SEND_PACKET;
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool isProperty{};
|
|
LWOOBJID objectId{};
|
|
LWOOBJID playerId{};
|
|
LWOOBJID worldId{};
|
|
uint32_t nameLength{};
|
|
std::u16string name{};
|
|
uint32_t descriptionLength{};
|
|
std::u16string description{};
|
|
|
|
inStream.Read(isProperty);
|
|
inStream.Read(objectId);
|
|
inStream.Read(playerId);
|
|
inStream.Read(worldId);
|
|
|
|
inStream.Read(descriptionLength);
|
|
for (uint32_t i = 0; i < descriptionLength; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
description.push_back(character);
|
|
}
|
|
|
|
inStream.Read(nameLength);
|
|
for (uint32_t i = 0; i < nameLength; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
name.push_back(character);
|
|
}
|
|
|
|
PropertyManagementComponent::Instance()->UpdatePropertyDetails(GeneralUtils::UTF16ToWTF8(name), GeneralUtils::UTF16ToWTF8(description));
|
|
}
|
|
|
|
void GameMessages::HandleQueryPropertyData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LOG("Entity (%i) requesting data", entity->GetLOT());
|
|
|
|
/*
|
|
auto entites = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROPERTY_VENDOR);
|
|
|
|
entity = entites[0];
|
|
*/
|
|
|
|
auto* propertyVendorComponent = static_cast<PropertyVendorComponent*>(entity->GetComponent(eReplicaComponentType::PROPERTY_VENDOR));
|
|
|
|
if (propertyVendorComponent != nullptr) {
|
|
propertyVendorComponent->OnQueryPropertyData(entity, sysAddr);
|
|
}
|
|
|
|
/*
|
|
entites = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROPERTY_MANAGEMENT);
|
|
|
|
entity = entites[0];
|
|
*/
|
|
|
|
auto* propertyManagerComponent = static_cast<PropertyManagementComponent*>(entity->GetComponent(eReplicaComponentType::PROPERTY_MANAGEMENT));
|
|
|
|
if (propertyManagerComponent != nullptr) {
|
|
propertyManagerComponent->OnQueryPropertyData(entity, sysAddr);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleSetBuildMode(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool start{};
|
|
int32_t distanceType = -1;
|
|
bool modePaused{};
|
|
int modeValue = 1;
|
|
LWOOBJID playerId{};
|
|
NiPoint3 startPosition = NiPoint3Constant::ZERO;
|
|
|
|
inStream.Read(start);
|
|
|
|
if (inStream.ReadBit())
|
|
inStream.Read(distanceType);
|
|
|
|
inStream.Read(modePaused);
|
|
|
|
if (inStream.ReadBit())
|
|
inStream.Read(modeValue);
|
|
|
|
inStream.Read(playerId);
|
|
|
|
if (inStream.ReadBit())
|
|
inStream.Read(startPosition);
|
|
|
|
auto* player = Game::entityManager->GetEntity(playerId);
|
|
|
|
if (startPosition == NiPoint3Constant::ZERO) {
|
|
startPosition = player->GetPosition();
|
|
}
|
|
|
|
player->GetCharacter()->SetBuildMode(start);
|
|
|
|
LOG("Sending build mode confirm (%i): (%d) (%i) (%d) (%i) (%llu)", entity->GetLOT(), start, distanceType, modePaused, modeValue, playerId);
|
|
|
|
SendSetBuildModeConfirmed(entity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS, start, false, modePaused, modeValue, playerId, startPosition);
|
|
}
|
|
|
|
void GameMessages::HandleStartBuildingWithItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
if (!entity->HasComponent(eReplicaComponentType::PROPERTY_MANAGEMENT)) {
|
|
return;
|
|
}
|
|
|
|
bool firstTime{};
|
|
bool success{};
|
|
int32_t sourceBag{};
|
|
LWOOBJID sourceId{};
|
|
LOT sourceLot{};
|
|
int32_t sourceType{};
|
|
LWOOBJID targetId{};
|
|
LOT targetLot{};
|
|
NiPoint3 targetPosition{};
|
|
int32_t targetType{};
|
|
|
|
inStream.Read(firstTime);
|
|
inStream.Read(success);
|
|
inStream.Read(sourceBag);
|
|
inStream.Read(sourceId);
|
|
inStream.Read(sourceLot);
|
|
inStream.Read(sourceType);
|
|
inStream.Read(targetId);
|
|
inStream.Read(targetLot);
|
|
inStream.Read(targetPosition);
|
|
inStream.Read(targetType);
|
|
|
|
if (sourceType == 1) {
|
|
sourceType = 4;
|
|
}
|
|
|
|
LOG("Handling start building with item (%i): (%d) (%d) (%i) (%llu) (%i) (%i) (%llu) (%i) (%i)", entity->GetLOT(), firstTime, success, sourceBag, sourceId, sourceLot, sourceType, targetId, targetLot, targetType);
|
|
|
|
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
|
|
|
auto* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
|
|
SendStartArrangingWithItem(
|
|
player,
|
|
sysAddr,
|
|
firstTime,
|
|
entity->GetObjectID(),
|
|
player->GetPosition(),
|
|
sourceBag,
|
|
sourceId,
|
|
sourceLot,
|
|
sourceType,
|
|
targetId,
|
|
targetLot,
|
|
targetPosition,
|
|
targetType
|
|
);
|
|
}
|
|
|
|
void GameMessages::HandlePropertyEditorBegin(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
PropertyManagementComponent::Instance()->OnStartBuilding();
|
|
|
|
Game::zoneManager->GetZoneControlObject()->OnZonePropertyEditBegin();
|
|
}
|
|
|
|
void GameMessages::HandlePropertyEditorEnd(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
PropertyManagementComponent::Instance()->OnFinishBuilding();
|
|
|
|
Game::zoneManager->GetZoneControlObject()->OnZonePropertyEditEnd();
|
|
}
|
|
|
|
void GameMessages::HandlePropertyContentsFromClient(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
|
|
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
|
|
SendGetModelsOnProperty(player->GetObjectID(), PropertyManagementComponent::Instance()->GetModels(), UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
|
|
void GameMessages::HandlePropertyModelEquipped(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelEquipped();
|
|
}
|
|
|
|
void GameMessages::HandlePlacePropertyModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID model;
|
|
|
|
inStream.Read(model);
|
|
|
|
PropertyManagementComponent::Instance()->UpdateModelPosition(model, NiPoint3Constant::ZERO, NiQuaternionConstant::IDENTITY);
|
|
}
|
|
|
|
void GameMessages::HandleUpdatePropertyModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID model;
|
|
NiPoint3 position;
|
|
NiQuaternion rotation = NiQuaternionConstant::IDENTITY;
|
|
|
|
inStream.Read(model);
|
|
inStream.Read(position);
|
|
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(rotation);
|
|
}
|
|
|
|
PropertyManagementComponent::Instance()->UpdateModelPosition(model, position, rotation);
|
|
}
|
|
|
|
void GameMessages::HandleDeletePropertyModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID model = LWOOBJID_EMPTY;
|
|
int deleteReason = 0;
|
|
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(model);
|
|
}
|
|
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(deleteReason);
|
|
|
|
}
|
|
|
|
PropertyManagementComponent::Instance()->DeleteModel(model, deleteReason);
|
|
}
|
|
|
|
void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID previousItemID = LWOOBJID_EMPTY;
|
|
inStream.Read(previousItemID);
|
|
|
|
LOG("Load item request for: %lld", previousItemID);
|
|
LWOOBJID newId = previousItemID;
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
if (inventoryComponent) {
|
|
auto* inventory = inventoryComponent->GetInventory(eInventoryType::MODELS);
|
|
auto* itemToMove = inventory->FindItemById(previousItemID);
|
|
|
|
if (itemToMove) {
|
|
LOT previousLot = itemToMove->GetLot();
|
|
inventoryComponent->MoveItemToInventory(itemToMove, eInventoryType::MODELS_IN_BBB, 1, false);
|
|
|
|
auto* destinationInventory = inventoryComponent->GetInventory(eInventoryType::MODELS_IN_BBB);
|
|
if (destinationInventory) {
|
|
auto* movedItem = destinationInventory->FindItemByLot(previousLot);
|
|
if (movedItem) newId = movedItem->GetId();
|
|
}
|
|
} else {
|
|
LOG("item id %llu not found in MODELS inventory, likely because it does not exist", previousItemID);
|
|
}
|
|
}
|
|
|
|
// Second argument always true (successful) for now
|
|
SendBlueprintLoadItemResponse(sysAddr, true, previousItemID, newId);
|
|
}
|
|
|
|
void GameMessages::SendBlueprintLoadItemResponse(const SystemAddress& sysAddr, bool success, LWOOBJID oldItemId, LWOOBJID newItemId) {
|
|
CBITSTREAM;
|
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::BLUEPRINT_LOAD_RESPONSE_ITEMID);
|
|
bitStream.Write<uint8_t>(success);
|
|
bitStream.Write<LWOOBJID>(oldItemId);
|
|
bitStream.Write<LWOOBJID>(newItemId);
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSmash(Entity* entity, float force, float ghostOpacity, LWOOBJID killerID, bool ignoreObjectVisibility) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SMASH);
|
|
|
|
bitStream.Write(ignoreObjectVisibility);
|
|
bitStream.Write(force);
|
|
bitStream.Write(ghostOpacity);
|
|
bitStream.Write(killerID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendUnSmash(Entity* entity, LWOOBJID builderID, float duration) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::UN_SMASH);
|
|
|
|
bitStream.Write(builderID != LWOOBJID_EMPTY);
|
|
if (builderID != LWOOBJID_EMPTY) bitStream.Write(builderID);
|
|
|
|
bitStream.Write(duration != 3.0f);
|
|
if (duration != 3.0f) bitStream.Write(duration);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::HandleControlBehaviors(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
AMFDeserialize reader;
|
|
std::unique_ptr<AMFArrayValue> amfArguments{ static_cast<AMFArrayValue*>(reader.Read(inStream).release()) };
|
|
if (amfArguments->GetValueType() != eAmf::Array) return;
|
|
|
|
uint32_t commandLength{};
|
|
inStream.Read(commandLength);
|
|
|
|
std::string command;
|
|
command.reserve(commandLength);
|
|
for (uint32_t i = 0; i < commandLength; ++i) {
|
|
unsigned char character;
|
|
inStream.Read(character);
|
|
command.push_back(character);
|
|
}
|
|
|
|
auto* const owner = PropertyManagementComponent::Instance()->GetOwner();
|
|
if (!owner) return;
|
|
|
|
ControlBehaviors::Instance().ProcessCommand(entity, *amfArguments, command, owner);
|
|
}
|
|
|
|
void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
/*
|
|
___ ___
|
|
/\ /\___ _ __ ___ / __\ ___ / \_ __ __ _ __ _ ___ _ __ ___
|
|
/ /_/ / _ \ '__/ _ \ /__\/// _ \ / /\ / '__/ _` |/ _` |/ _ \| '_ \/ __|
|
|
/ __ / __/ | | __/ / \/ \ __/ / /_//| | | (_| | (_| | (_) | | | \__ \
|
|
\/ /_/ \___|_| \___| \_____/\___| /___,' |_| \__,_|\__, |\___/|_| |_|___/
|
|
|___/
|
|
___ _
|
|
/ __\ _____ ____ _ _ __ ___ / \
|
|
/__\/// _ \ \ /\ / / _` | '__/ _ \/ /
|
|
/ \/ \ __/\ V V / (_| | | | __/\_/
|
|
\_____/\___| \_/\_/ \__,_|_| \___\/
|
|
<>=======()
|
|
(/\___ /|\\ ()==========<>_
|
|
\_/ | \\ //|\ ______/ \)
|
|
\_| \\ // | \_/
|
|
\|\/|\_ // /\/
|
|
(oo)\ \_// /
|
|
//_/\_\/ / |
|
|
@@/ |=\ \ |
|
|
\_=\_ \ |
|
|
\==\ \|\_ snd
|
|
__(\===\( )\
|
|
(((~) __(_/ |
|
|
(((~) \ /
|
|
______/ /
|
|
'------'
|
|
*/
|
|
LWOOBJID localId;
|
|
|
|
inStream.Read(localId);
|
|
|
|
uint32_t sd0Size;
|
|
inStream.Read(sd0Size);
|
|
std::shared_ptr<char[]> sd0Data(new char[sd0Size]);
|
|
|
|
if (sd0Data == nullptr) return;
|
|
|
|
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(sd0Data.get()), sd0Size);
|
|
|
|
uint32_t timeTaken;
|
|
inStream.Read(timeTaken);
|
|
|
|
/*
|
|
Disabled this, as it's kinda silly to do this roundabout way of storing plaintext lxfml, then recompressing
|
|
it to send it back to the client.
|
|
|
|
On DLU we had agreed that bricks wouldn't be taken anyway, but if your server decides otherwise, feel free to
|
|
comment this back out and add the needed code to get the bricks used from lxfml and take them from the inventory.
|
|
|
|
Note, in the live client it'll still display the bricks going out as they're being used, but on relog/world change,
|
|
they reappear as we didn't take them.
|
|
|
|
TODO Apparently the bricks are supposed to be taken via MoveInventoryBatch?
|
|
*/
|
|
|
|
////Decompress the SD0 from the client so we can process the lxfml properly
|
|
//uint8_t* outData = new uint8_t[327680];
|
|
//int32_t error;
|
|
//int32_t size = ZCompression::Decompress(inData, lxfmlSize, outData, 327680, error);
|
|
|
|
//if (size == -1) {
|
|
// LOG("Failed to decompress LXFML: (%i)", error);
|
|
// return;
|
|
//}
|
|
//
|
|
//std::string lxfml(reinterpret_cast<char*>(outData), size); //std::string version of the decompressed data!
|
|
|
|
//Now, the cave of dragons:
|
|
|
|
//We runs this in async because the http library here is blocking, meaning it'll halt the thread.
|
|
//But we don't want the server to go unresponsive, because then the client would disconnect.
|
|
|
|
//We need to get a new ID for our model first:
|
|
ObjectIDManager::RequestPersistentID([=](uint32_t newID) {
|
|
if (!entity || !entity->GetCharacter() || !entity->GetCharacter()->GetParentUser()) return;
|
|
LWOOBJID newIDL = newID;
|
|
GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER);
|
|
GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT);
|
|
|
|
uint32_t blueprintIDSmall = ObjectIDManager::GenerateRandomObjectID();
|
|
LWOOBJID blueprintID = blueprintIDSmall;
|
|
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
|
|
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
|
|
|
|
//We need to get the propertyID: (stolen from Wincent's propertyManagementComp)
|
|
const auto& worldId = Game::zoneManager->GetZone()->GetZoneID();
|
|
|
|
const auto zoneId = worldId.GetMapID();
|
|
const auto cloneId = worldId.GetCloneID();
|
|
|
|
auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
|
|
LWOOBJID propertyId = LWOOBJID_EMPTY;
|
|
if (propertyInfo) propertyId = propertyInfo->id;
|
|
|
|
//Insert into ugc:
|
|
std::string str(sd0Data.get(), sd0Size);
|
|
std::istringstream sd0DataStream(str);
|
|
Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetCharacter()->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID());
|
|
|
|
//Insert into the db as a BBB model:
|
|
IPropertyContents::Model model;
|
|
model.id = newIDL;
|
|
model.ugcId = blueprintIDSmall;
|
|
model.position = NiPoint3Constant::ZERO;
|
|
model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
|
|
model.lot = 14;
|
|
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_14_name");
|
|
|
|
/*
|
|
Commented out until UGC server would be updated to use a sd0 file instead of lxfml stream.
|
|
(or you uncomment the lxfml decomp stuff above)
|
|
*/
|
|
|
|
////Send off to UGC for processing, if enabled:
|
|
//if (Game::config->GetValue("ugc_remote") == "1") {
|
|
// std::string ugcIP = Game::config->GetValue("ugc_ip");
|
|
// int ugcPort = std::stoi(Game::config->GetValue("ugc_port"));
|
|
|
|
// httplib::Client cli(ugcIP, ugcPort); //connect to UGC HTTP server using our config above ^
|
|
|
|
// //Send out a request:
|
|
// std::string request = "/3dservices/UGCC150/150" + std::to_string(blueprintID) + ".lxfml";
|
|
// cli.Put(request.c_str(), lxfml.c_str(), "text/lxfml");
|
|
|
|
// //When the "put" above returns, it means that the UGC HTTP server is done processing our model &
|
|
// //the nif, hkx and checksum files are ready to be downloaded from cache.
|
|
//}
|
|
|
|
//Tell the client their model is saved: (this causes us to actually pop out of our current state):
|
|
CBITSTREAM;
|
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::BLUEPRINT_SAVE_RESPONSE);
|
|
bitStream.Write(localId);
|
|
bitStream.Write(eBlueprintSaveResponseType::EverythingWorked);
|
|
bitStream.Write<uint32_t>(1);
|
|
bitStream.Write(blueprintID);
|
|
|
|
bitStream.Write<uint32_t>(sd0Size);
|
|
|
|
bitStream.WriteAlignedBytes(reinterpret_cast<unsigned char*>(sd0Data.get()), sd0Size);
|
|
|
|
SEND_PACKET;
|
|
|
|
//Now we have to construct this object:
|
|
|
|
EntityInfo info;
|
|
info.lot = 14;
|
|
info.pos = {};
|
|
info.rot = {};
|
|
info.spawner = nullptr;
|
|
info.spawnerID = entity->GetObjectID();
|
|
info.spawnerNodeID = 0;
|
|
|
|
info.settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
|
|
info.settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
|
info.settings.push_back(new LDFData<int>(u"modelType", 2));
|
|
info.settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
|
|
info.settings.push_back(new LDFData<LWOOBJID>(u"userModelID", newIDL));
|
|
|
|
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
|
|
if (newEntity) {
|
|
Game::entityManager->ConstructEntity(newEntity);
|
|
|
|
//Make sure the propMgmt doesn't delete our model after the server dies
|
|
//Trying to do this after the entity is constructed. Shouldn't really change anything but
|
|
//there was an issue with builds not appearing since it was placed above ConstructEntity.
|
|
PropertyManagementComponent::Instance()->AddModel(newEntity->GetObjectID(), newIDL);
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool includeNullAddress{};
|
|
bool includeNullDescription{};
|
|
bool playerOwn{};
|
|
bool updateUi{};
|
|
int32_t numResults{};
|
|
int32_t reputation{};
|
|
int32_t sortMethod{};
|
|
int32_t startIndex{};
|
|
uint32_t filterTextLength{};
|
|
std::string filterText{};
|
|
|
|
inStream.Read(includeNullAddress);
|
|
inStream.Read(includeNullDescription);
|
|
inStream.Read(playerOwn);
|
|
inStream.Read(updateUi);
|
|
inStream.Read(numResults);
|
|
inStream.Read(reputation);
|
|
inStream.Read(sortMethod);
|
|
inStream.Read(startIndex);
|
|
inStream.Read(filterTextLength);
|
|
|
|
for (auto i = 0u; i < filterTextLength; i++) {
|
|
char c;
|
|
inStream.Read(c);
|
|
filterText.push_back(c);
|
|
}
|
|
|
|
auto* player = PlayerManager::GetPlayer(sysAddr);
|
|
|
|
auto* entranceComponent = entity->GetComponent<PropertyEntranceComponent>();
|
|
|
|
if (entranceComponent == nullptr) return;
|
|
|
|
entranceComponent->OnPropertyEntranceSync(player,
|
|
includeNullAddress,
|
|
includeNullDescription,
|
|
playerOwn,
|
|
updateUi,
|
|
numResults,
|
|
reputation,
|
|
sortMethod,
|
|
startIndex,
|
|
filterText,
|
|
sysAddr
|
|
);
|
|
}
|
|
|
|
void GameMessages::HandleEnterProperty(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint32_t index{};
|
|
bool returnToZone{};
|
|
|
|
inStream.Read(index);
|
|
inStream.Read(returnToZone);
|
|
|
|
auto* player = PlayerManager::GetPlayer(sysAddr);
|
|
|
|
auto* entranceComponent = entity->GetComponent<PropertyEntranceComponent>();
|
|
if (entranceComponent != nullptr) {
|
|
entranceComponent->OnEnterProperty(player, index, returnToZone, sysAddr);
|
|
return;
|
|
}
|
|
|
|
auto multiZoneEntranceComponent = entity->GetComponent<MultiZoneEntranceComponent>();
|
|
if (multiZoneEntranceComponent != nullptr) {
|
|
multiZoneEntranceComponent->OnSelectWorld(player, index);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleSetConsumableItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LOT lot;
|
|
|
|
inStream.Read(lot);
|
|
|
|
auto* inventory = entity->GetComponent<InventoryComponent>();
|
|
|
|
if (inventory == nullptr) return;
|
|
|
|
inventory->SetConsumable(lot);
|
|
}
|
|
|
|
void GameMessages::SendPlayCinematic(LWOOBJID objectId, std::u16string pathName, const SystemAddress& sysAddr,
|
|
bool allowGhostUpdates, bool bCloseMultiInteract, bool bSendServerNotify, bool bUseControlledObjectForAudioListener,
|
|
eEndBehavior endBehavior, bool hidePlayerDuringCine, float leadIn, bool leavePlayerLockedWhenFinished,
|
|
bool lockPlayer, bool result, bool skipIfSamePath, float startTimeAdvance) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PLAY_CINEMATIC);
|
|
|
|
bitStream.Write(allowGhostUpdates);
|
|
bitStream.Write(bCloseMultiInteract);
|
|
bitStream.Write(bSendServerNotify);
|
|
bitStream.Write(bUseControlledObjectForAudioListener);
|
|
|
|
bitStream.Write(endBehavior != eEndBehavior::RETURN);
|
|
if (endBehavior != eEndBehavior::RETURN) bitStream.Write(endBehavior);
|
|
|
|
bitStream.Write(hidePlayerDuringCine);
|
|
|
|
bitStream.Write(leadIn != -1);
|
|
if (leadIn != -1) bitStream.Write(leadIn);
|
|
|
|
bitStream.Write(leavePlayerLockedWhenFinished);
|
|
bitStream.Write(lockPlayer);
|
|
|
|
bitStream.Write<uint32_t>(pathName.size());
|
|
for (char16_t c : pathName) {
|
|
bitStream.Write(c);
|
|
}
|
|
|
|
bitStream.Write(result);
|
|
|
|
bitStream.Write(skipIfSamePath);
|
|
|
|
bitStream.Write(startTimeAdvance);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendEndCinematic(LWOOBJID objectId, std::u16string pathName, const SystemAddress& sysAddr,
|
|
float leadOut, bool leavePlayerLocked) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::END_CINEMATIC);
|
|
|
|
bitStream.Write(leadOut != -1);
|
|
if (leadOut != -1) bitStream.Write(leadOut);
|
|
|
|
bitStream.Write(leavePlayerLocked);
|
|
|
|
bitStream.Write<uint32_t>(pathName.size());
|
|
for (uint32_t i = 0; i < pathName.size(); i++) {
|
|
bitStream.Write(pathName[i]);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleCinematicUpdate(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
eCinematicEvent event;
|
|
if (!inStream.ReadBit()) {
|
|
event = eCinematicEvent::STARTED;
|
|
} else {
|
|
inStream.Read<eCinematicEvent>(event);
|
|
}
|
|
|
|
float_t overallTime;
|
|
if (!inStream.ReadBit()) {
|
|
overallTime = -1.0f;
|
|
} else {
|
|
inStream.Read<float_t>(overallTime);
|
|
}
|
|
|
|
uint32_t pathNameLength;
|
|
inStream.Read(pathNameLength);
|
|
|
|
std::u16string pathName;
|
|
for (size_t i = 0; i < pathNameLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
pathName.push_back(character);
|
|
}
|
|
|
|
float_t pathTime;
|
|
if (!inStream.ReadBit()) {
|
|
pathTime = -1.0f;
|
|
} else {
|
|
inStream.Read<float_t>(pathTime);
|
|
}
|
|
|
|
int32_t waypoint;
|
|
if (!inStream.ReadBit()) {
|
|
waypoint = -1;
|
|
} else {
|
|
inStream.Read<int32_t>(waypoint);
|
|
}
|
|
|
|
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT);
|
|
for (Entity* scriptEntity : scriptedActs) {
|
|
scriptEntity->OnCinematicUpdate(scriptEntity, entity, event, pathName, pathTime, overallTime, waypoint);
|
|
}
|
|
}
|
|
|
|
void GameMessages::SendSetStunned(LWOOBJID objectId, eStateChangeType stateChangeType, const SystemAddress& sysAddr,
|
|
LWOOBJID originator, bool bCantAttack, bool bCantEquip,
|
|
bool bCantInteract, bool bCantJump, bool bCantMove, bool bCantTurn,
|
|
bool bCantUseItem, bool bDontTerminateInteract, bool bIgnoreImmunity,
|
|
bool bCantAttackOutChangeWasApplied, bool bCantEquipOutChangeWasApplied,
|
|
bool bCantInteractOutChangeWasApplied, bool bCantJumpOutChangeWasApplied,
|
|
bool bCantMoveOutChangeWasApplied, bool bCantTurnOutChangeWasApplied,
|
|
bool bCantUseItemOutChangeWasApplied) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_STUNNED);
|
|
|
|
bitStream.Write(originator != LWOOBJID_EMPTY);
|
|
if (originator != LWOOBJID_EMPTY) bitStream.Write(originator);
|
|
|
|
bitStream.Write(stateChangeType);
|
|
|
|
bitStream.Write(bCantAttack);
|
|
bitStream.Write(bCantAttackOutChangeWasApplied);
|
|
|
|
bitStream.Write(bCantEquip);
|
|
bitStream.Write(bCantEquipOutChangeWasApplied);
|
|
|
|
bitStream.Write(bCantInteract);
|
|
bitStream.Write(bCantInteractOutChangeWasApplied);
|
|
|
|
bitStream.Write(bCantJump);
|
|
bitStream.Write(bCantJumpOutChangeWasApplied);
|
|
|
|
bitStream.Write(bCantMove);
|
|
bitStream.Write(bCantMoveOutChangeWasApplied);
|
|
|
|
bitStream.Write(bCantTurn);
|
|
bitStream.Write(bCantTurnOutChangeWasApplied);
|
|
|
|
bitStream.Write(bCantUseItem);
|
|
bitStream.Write(bCantUseItemOutChangeWasApplied);
|
|
|
|
bitStream.Write(bDontTerminateInteract);
|
|
|
|
bitStream.Write(bIgnoreImmunity);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, const SystemAddress& sysAddr,
|
|
LWOOBJID originator,
|
|
bool bImmuneToStunAttack,
|
|
bool bImmuneToStunEquip,
|
|
bool bImmuneToStunInteract,
|
|
bool bImmuneToStunJump,
|
|
bool bImmuneToStunMove,
|
|
bool bImmuneToStunTurn,
|
|
bool bImmuneToStunUseItem) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(target);
|
|
bitStream.Write(MessageType::Game::SET_STUN_IMMUNITY);
|
|
|
|
bitStream.Write(originator != LWOOBJID_EMPTY);
|
|
if (originator != LWOOBJID_EMPTY) bitStream.Write(originator);
|
|
|
|
bitStream.Write(state);
|
|
|
|
bitStream.Write(bImmuneToStunAttack);
|
|
bitStream.Write(bImmuneToStunEquip);
|
|
bitStream.Write(bImmuneToStunInteract);
|
|
bitStream.Write(bImmuneToStunJump);
|
|
bitStream.Write(bImmuneToStunMove);
|
|
bitStream.Write(bImmuneToStunTurn);
|
|
bitStream.Write(bImmuneToStunUseItem);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendSetStatusImmunity(LWOOBJID objectId, eStateChangeType state, const SystemAddress& sysAddr,
|
|
bool bImmuneToBasicAttack,
|
|
bool bImmuneToDamageOverTime,
|
|
bool bImmuneToKnockback,
|
|
bool bImmuneToInterrupt,
|
|
bool bImmuneToSpeed,
|
|
bool bImmuneToImaginationGain,
|
|
bool bImmuneToImaginationLoss,
|
|
bool bImmuneToQuickbuildInterrupt,
|
|
bool bImmuneToPullToPoint) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_STATUS_IMMUNITY);
|
|
|
|
bitStream.Write(state);
|
|
|
|
bitStream.Write(bImmuneToBasicAttack);
|
|
bitStream.Write(bImmuneToDamageOverTime);
|
|
bitStream.Write(bImmuneToKnockback);
|
|
bitStream.Write(bImmuneToInterrupt);
|
|
bitStream.Write(bImmuneToSpeed);
|
|
bitStream.Write(bImmuneToImaginationGain);
|
|
bitStream.Write(bImmuneToImaginationLoss);
|
|
bitStream.Write(bImmuneToQuickbuildInterrupt);
|
|
bitStream.Write(bImmuneToPullToPoint);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendOrientToAngle(LWOOBJID objectId, bool bRelativeToCurrent, float fAngle, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ORIENT_TO_ANGLE);
|
|
|
|
bitStream.Write(bRelativeToCurrent);
|
|
bitStream.Write(fAngle);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendAddRunSpeedModifier(LWOOBJID objectId, LWOOBJID caster, uint32_t modifier, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ADD_RUN_SPEED_MODIFIER);
|
|
|
|
bitStream.Write(caster != LWOOBJID_EMPTY);
|
|
if (caster != LWOOBJID_EMPTY) bitStream.Write(caster);
|
|
|
|
bitStream.Write(modifier != 500);
|
|
if (modifier != 500) bitStream.Write(modifier);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRemoveRunSpeedModifier(LWOOBJID objectId, uint32_t modifier, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::REMOVE_RUN_SPEED_MODIFIER);
|
|
|
|
bitStream.Write(modifier != 500);
|
|
if (modifier != 500) bitStream.Write(modifier);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPropertyEntranceBegin(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PROPERTY_ENTRANCE_BEGIN);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPropertySelectQuery(LWOOBJID objectId, int32_t navOffset, bool thereAreMore, int32_t cloneId, bool hasFeaturedProperty, bool wasFriends, const std::vector<PropertySelectQueryProperty>& entries, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PROPERTY_SELECT_QUERY);
|
|
|
|
bitStream.Write(navOffset);
|
|
bitStream.Write(thereAreMore);
|
|
bitStream.Write(cloneId);
|
|
bitStream.Write(hasFeaturedProperty);
|
|
bitStream.Write(wasFriends);
|
|
|
|
bitStream.Write<uint32_t>(entries.size());
|
|
|
|
for (auto& entry : entries) {
|
|
entry.Serialize(bitStream);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std::u16string name, const SystemAddress& sysAddr, int param1, int param2) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_OBJECT);
|
|
|
|
bitStream.Write(objIDSender);
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (const auto character : name) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write(param1);
|
|
bitStream.Write(param2);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleVerifyAck(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bDifferent;
|
|
std::string sBitStream;
|
|
uint32_t uiHandle = 0;
|
|
|
|
bDifferent = inStream.ReadBit();
|
|
|
|
uint32_t sBitStreamLength = 0;
|
|
inStream.Read(sBitStreamLength);
|
|
for (uint64_t k = 0; k < sBitStreamLength; k++) {
|
|
uint8_t character;
|
|
inStream.Read(character);
|
|
sBitStream.push_back(character);
|
|
}
|
|
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(uiHandle);
|
|
}
|
|
}
|
|
|
|
void GameMessages::SendTeamPickupItem(LWOOBJID objectId, LWOOBJID lootID, LWOOBJID lootOwnerID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::TEAM_PICKUP_ITEM);
|
|
|
|
bitStream.Write(lootID);
|
|
bitStream.Write(lootOwnerID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
//Trading:
|
|
|
|
void GameMessages::SendServerTradeInvite(LWOOBJID objectId, bool bNeedInvitePopUp, LWOOBJID i64Requestor, std::u16string wsName, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SERVER_TRADE_INVITE);
|
|
|
|
bitStream.Write(bNeedInvitePopUp);
|
|
bitStream.Write(i64Requestor);
|
|
bitStream.Write<uint32_t>(wsName.size());
|
|
for (const auto character : wsName) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendServerTradeInitialReply(LWOOBJID objectId, LWOOBJID i64Invitee, int32_t resultType, std::u16string wsName, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SERVER_TRADE_INITIAL_REPLY);
|
|
|
|
bitStream.Write(i64Invitee);
|
|
bitStream.Write(resultType);
|
|
bitStream.Write<uint32_t>(wsName.size());
|
|
for (const auto character : wsName) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendServerTradeFinalReply(LWOOBJID objectId, bool bResult, LWOOBJID i64Invitee, std::u16string wsName, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SERVER_TRADE_FINAL_REPLY);
|
|
|
|
bitStream.Write(bResult);
|
|
bitStream.Write(i64Invitee);
|
|
bitStream.Write<uint32_t>(wsName.size());
|
|
for (const auto character : wsName) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendServerTradeAccept(LWOOBJID objectId, bool bFirst, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SERVER_TRADE_ACCEPT);
|
|
|
|
bitStream.Write(bFirst);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendServerTradeCancel(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SERVER_TRADE_CANCEL);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendServerTradeUpdate(LWOOBJID objectId, uint64_t coins, const std::vector<TradeItem>& items, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SERVER_TRADE_UPDATE);
|
|
|
|
bitStream.Write(false);
|
|
bitStream.Write(coins);
|
|
bitStream.Write<uint32_t>(items.size());
|
|
|
|
for (const auto& item : items) {
|
|
bitStream.Write(item.itemId);
|
|
bitStream.Write(item.itemId);
|
|
|
|
bitStream.Write(item.itemLot);
|
|
bitStream.Write0();
|
|
bitStream.Write1();
|
|
bitStream.Write(item.itemCount);
|
|
bitStream.Write0();
|
|
bitStream.Write0();
|
|
bitStream.Write0();
|
|
bitStream.Write0();
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleClientTradeRequest(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
// Check if the player has restricted trade access
|
|
auto* character = entity->GetCharacter();
|
|
|
|
if (character->HasPermission(ePermissionMap::RestrictedTradeAccess)) {
|
|
// Send a message to the player
|
|
ChatPackets::SendSystemMessage(
|
|
sysAddr,
|
|
u"This character has restricted trade access."
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
bool bNeedInvitePopUp = inStream.ReadBit();
|
|
LWOOBJID i64Invitee;
|
|
|
|
inStream.Read(i64Invitee);
|
|
|
|
auto* invitee = Game::entityManager->GetEntity(i64Invitee);
|
|
|
|
if (invitee != nullptr && invitee->IsPlayer()) {
|
|
character = invitee->GetCharacter();
|
|
|
|
if (character->HasPermission(ePermissionMap::RestrictedTradeAccess)) {
|
|
// Send a message to the player
|
|
ChatPackets::SendSystemMessage(
|
|
sysAddr,
|
|
u"The character you are trying to trade with has restricted trade access."
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
LOG("Trade request to (%llu)", i64Invitee);
|
|
|
|
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
|
|
|
if (trade != nullptr) {
|
|
if (!trade->IsParticipant(i64Invitee)) {
|
|
TradingManager::Instance()->CancelTrade(entity->GetObjectID(), trade->GetTradeId());
|
|
|
|
TradingManager::Instance()->NewTrade(entity->GetObjectID(), i64Invitee);
|
|
}
|
|
} else {
|
|
TradingManager::Instance()->NewTrade(entity->GetObjectID(), i64Invitee);
|
|
}
|
|
|
|
SendServerTradeInvite(
|
|
i64Invitee,
|
|
bNeedInvitePopUp,
|
|
entity->GetObjectID(),
|
|
GeneralUtils::UTF8ToUTF16(entity->GetCharacter()->GetName()),
|
|
invitee->GetSystemAddress()
|
|
);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleClientTradeCancel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
|
|
|
if (trade == nullptr) return;
|
|
|
|
LOG("Trade canceled from (%llu)", entity->GetObjectID());
|
|
|
|
TradingManager::Instance()->CancelTrade(entity->GetObjectID(), trade->GetTradeId());
|
|
}
|
|
|
|
void GameMessages::HandleClientTradeAccept(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bFirst = inStream.ReadBit();
|
|
|
|
LOG("Trade accepted from (%llu) -> (%d)", entity->GetObjectID(), bFirst);
|
|
|
|
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
|
|
|
if (trade == nullptr) return;
|
|
|
|
trade->SetAccepted(entity->GetObjectID(), bFirst);
|
|
}
|
|
|
|
void GameMessages::HandleClientTradeUpdate(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint64_t currency;
|
|
uint32_t itemCount;
|
|
|
|
inStream.Read(currency);
|
|
inStream.Read(itemCount);
|
|
|
|
LOG("Trade update from (%llu) -> (%llu), (%i)", entity->GetObjectID(), currency, itemCount);
|
|
|
|
std::vector<TradeItem> items{};
|
|
|
|
for (size_t i = 0; i < itemCount; i++) {
|
|
LWOOBJID itemId;
|
|
LWOOBJID itemId2;
|
|
|
|
inStream.Read(itemId);
|
|
inStream.Read(itemId2);
|
|
|
|
LOT lot = 0;
|
|
LWOOBJID unknown1 = 0;
|
|
uint32_t unknown2 = 0;
|
|
uint16_t slot = 0;
|
|
uint32_t unknown3 = 0;
|
|
uint32_t ldfSize = 0;
|
|
bool unknown4;
|
|
|
|
inStream.Read(lot);
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(unknown1);
|
|
}
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(unknown2);
|
|
}
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(slot);
|
|
}
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(unknown3);
|
|
}
|
|
if (inStream.ReadBit()) // No
|
|
{
|
|
inStream.Read(ldfSize);
|
|
bool compressed = inStream.ReadBit();
|
|
if (compressed) {
|
|
uint32_t ldfCompressedSize = 0;
|
|
inStream.Read(ldfCompressedSize);
|
|
inStream.IgnoreBytes(ldfCompressedSize);
|
|
} else {
|
|
inStream.IgnoreBytes(ldfSize);
|
|
}
|
|
}
|
|
unknown4 = inStream.ReadBit();
|
|
|
|
items.push_back({ itemId, lot, unknown2 });
|
|
|
|
LOG("Trade item from (%llu) -> (%llu)/(%llu), (%i), (%llu), (%i), (%i)", entity->GetObjectID(), itemId, itemId2, lot, unknown1, unknown2, unknown3);
|
|
}
|
|
|
|
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
|
|
|
if (trade == nullptr) return;
|
|
|
|
trade->SetCoins(entity->GetObjectID(), currency);
|
|
trade->SetItems(entity->GetObjectID(), items);
|
|
trade->SendUpdateToOther(entity->GetObjectID());
|
|
}
|
|
|
|
//Pets:
|
|
|
|
void GameMessages::SendNotifyPetTamingMinigame(LWOOBJID objectId, LWOOBJID petId, LWOOBJID playerTamingId, bool bForceTeleport, ePetTamingNotifyType notifyType, NiPoint3 petsDestPos, NiPoint3 telePos, NiQuaternion teleRot, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_PET_TAMING_MINIGAME);
|
|
|
|
bitStream.Write(petId);
|
|
bitStream.Write(playerTamingId);
|
|
bitStream.Write(bForceTeleport);
|
|
bitStream.Write(notifyType);
|
|
bitStream.Write(petsDestPos);
|
|
bitStream.Write(telePos);
|
|
|
|
const bool hasDefault = teleRot != NiQuaternionConstant::IDENTITY;
|
|
bitStream.Write(hasDefault);
|
|
if (hasDefault) bitStream.Write(teleRot);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyTamingModelLoadedOnServer(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_TAMING_MODEL_LOADED_ON_SERVER);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendNotifyPetTamingPuzzleSelected(LWOOBJID objectId, const std::vector<Brick>& bricks, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_TAMING_PUZZLE_SELECTED);
|
|
|
|
bitStream.Write<uint32_t>(bricks.size());
|
|
for (const auto& brick : bricks) {
|
|
bitStream.Write(brick.designerID);
|
|
bitStream.Write(brick.materialID);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPetTamingTryBuildResult(LWOOBJID objectId, bool bSuccess, int32_t iNumCorrect, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PET_TAMING_TRY_BUILD_RESULT);
|
|
|
|
bitStream.Write(bSuccess);
|
|
bitStream.Write(iNumCorrect != 0);
|
|
if (iNumCorrect != 0) bitStream.Write(iNumCorrect);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPetResponse(LWOOBJID objectId, LWOOBJID objIDPet, int32_t iPetCommandType, int32_t iResponse, int32_t iTypeID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PET_RESPONSE);
|
|
|
|
bitStream.Write(objIDPet);
|
|
bitStream.Write(iPetCommandType);
|
|
bitStream.Write(iResponse);
|
|
bitStream.Write(iTypeID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendAddPetToPlayer(LWOOBJID objectId, int32_t iElementalType, std::u16string name, LWOOBJID petDBID, LOT petLOT, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ADD_PET_TO_PLAYER);
|
|
|
|
bitStream.Write(iElementalType);
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (const auto character : name) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write(petDBID);
|
|
bitStream.Write(petLOT);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRegisterPetID(LWOOBJID objectId, LWOOBJID objID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::REGISTER_PET_ID);
|
|
|
|
bitStream.Write(objID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRegisterPetDBID(LWOOBJID objectId, LWOOBJID petDBID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::REGISTER_PET_DBID);
|
|
|
|
bitStream.Write(petDBID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive, eUnequippableActiveType iType, LWOOBJID itemID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::MARK_INVENTORY_ITEM_AS_ACTIVE);
|
|
|
|
bitStream.Write(bActive);
|
|
|
|
bitStream.Write(iType != eUnequippableActiveType::INVALID);
|
|
if (iType != eUnequippableActiveType::INVALID) bitStream.Write(iType);
|
|
|
|
bitStream.Write(itemID != LWOOBJID_EMPTY);
|
|
if (itemID != LWOOBJID_EMPTY) bitStream.Write(itemID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::CLIENT_EXIT_TAMING_MINIGAME);
|
|
|
|
bitStream.Write(bVoluntaryExit);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendShowPetActionButton(const LWOOBJID objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SHOW_PET_ACTION_BUTTON);
|
|
|
|
bitStream.Write(petAbility);
|
|
bitStream.Write(bShow);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PLAY_EMOTE);
|
|
|
|
bitStream.Write(emoteID);
|
|
bitStream.Write(target);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendRemoveBuff(Entity* entity, bool fromUnEquip, bool removeImmunity, uint32_t buffId) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::REMOVE_BUFF);
|
|
|
|
bitStream.Write(false); // bFromRemoveBehavior but setting this to true makes the GM not do anything on the client?
|
|
bitStream.Write(fromUnEquip);
|
|
bitStream.Write(removeImmunity);
|
|
bitStream.Write(buffId);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::BOUNCER_ACTIVE_STATUS);
|
|
|
|
bitStream.Write(bActive);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendSetPetName(LWOOBJID objectId, std::u16string name, LWOOBJID petDBID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_PET_NAME);
|
|
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (const auto character : name) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write(petDBID != LWOOBJID_EMPTY);
|
|
if (petDBID != LWOOBJID_EMPTY) bitStream.Write(petDBID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendSetPetNameModerated(LWOOBJID objectId, LWOOBJID petDBID, int32_t nModerationStatus, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_PET_NAME_MODERATED);
|
|
|
|
bitStream.Write(petDBID != LWOOBJID_EMPTY);
|
|
if (petDBID != LWOOBJID_EMPTY) bitStream.Write(petDBID);
|
|
|
|
bitStream.Write(nModerationStatus);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendPetNameChanged(LWOOBJID objectId, int32_t moderationStatus, std::u16string name, std::u16string ownerName, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::PET_NAME_CHANGED);
|
|
|
|
bitStream.Write(moderationStatus);
|
|
|
|
bitStream.Write<uint32_t>(name.size());
|
|
for (const auto character : name) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write<uint32_t>(ownerName.size());
|
|
for (const auto character : ownerName) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::HandleClientExitTamingMinigame(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bVoluntaryExit = inStream.ReadBit();
|
|
|
|
auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
petComponent->ClientExitTamingMinigame(bVoluntaryExit);
|
|
}
|
|
|
|
void GameMessages::HandleStartServerPetMinigameTimer(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
petComponent->StartTimer();
|
|
}
|
|
|
|
void GameMessages::HandlePetTamingTryBuild(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint32_t brickCount;
|
|
std::vector<Brick> bricks;
|
|
bool clientFailed;
|
|
|
|
inStream.Read(brickCount);
|
|
|
|
bricks.reserve(brickCount);
|
|
|
|
for (uint32_t i = 0; i < brickCount; i++) {
|
|
Brick brick;
|
|
|
|
inStream.Read(brick);
|
|
|
|
bricks.push_back(brick);
|
|
}
|
|
|
|
clientFailed = inStream.ReadBit();
|
|
|
|
auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
petComponent->TryBuild(bricks.size(), clientFailed);
|
|
}
|
|
|
|
void GameMessages::HandleNotifyTamingBuildSuccess(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
NiPoint3 position;
|
|
|
|
inStream.Read(position);
|
|
|
|
auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
petComponent->NotifyTamingBuildSuccess(position);
|
|
}
|
|
|
|
void GameMessages::HandleRequestSetPetName(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint32_t nameLength;
|
|
std::u16string name;
|
|
|
|
inStream.Read(nameLength);
|
|
|
|
for (size_t i = 0; i < nameLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
name.push_back(character);
|
|
}
|
|
|
|
auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
petComponent->RequestSetPetName(name);
|
|
}
|
|
|
|
void GameMessages::HandleCommandPet(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
NiPoint3 genericPosInfo;
|
|
LWOOBJID objIdSource;
|
|
int32_t iPetCommandType;
|
|
int32_t iTypeID;
|
|
bool overrideObey;
|
|
|
|
inStream.Read(genericPosInfo);
|
|
inStream.Read(objIdSource);
|
|
inStream.Read(iPetCommandType);
|
|
inStream.Read(iTypeID);
|
|
overrideObey = inStream.ReadBit();
|
|
|
|
auto* petComponent = entity->GetComponent<PetComponent>();
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
petComponent->Command(genericPosInfo, objIdSource, iPetCommandType, iTypeID, overrideObey);
|
|
}
|
|
|
|
void GameMessages::HandleDespawnPet(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bDeletePet;
|
|
|
|
bDeletePet = inStream.ReadBit();
|
|
|
|
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
|
|
|
if (petComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (bDeletePet) {
|
|
petComponent->Release();
|
|
} else {
|
|
petComponent->Deactivate();
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleMessageBoxResponse(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
int32_t iButton;
|
|
uint32_t identifierLength;
|
|
std::u16string identifier;
|
|
uint32_t userDataLength;
|
|
std::u16string userData;
|
|
|
|
inStream.Read(iButton);
|
|
|
|
inStream.Read(identifierLength);
|
|
for (size_t i = 0; i < identifierLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
identifier.push_back(character);
|
|
}
|
|
|
|
inStream.Read(userDataLength);
|
|
for (size_t i = 0; i < userDataLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
userData.push_back(character);
|
|
}
|
|
|
|
LOG("Button: %d; LOT: %u identifier: %s; userData: %s", iButton, entity->GetLOT(), GeneralUtils::UTF16ToWTF8(identifier).c_str(), GeneralUtils::UTF16ToWTF8(userData).c_str());
|
|
|
|
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
|
|
|
if (user == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* userEntity = user->GetLastUsedChar()->GetEntity();
|
|
|
|
if (userEntity == nullptr) {
|
|
return;
|
|
}
|
|
|
|
entity->OnMessageBoxResponse(userEntity, iButton, identifier, userData);
|
|
|
|
auto* scriptedActivityComponent = entity->GetComponent<ScriptedActivityComponent>();
|
|
|
|
if (scriptedActivityComponent != nullptr) {
|
|
scriptedActivityComponent->HandleMessageBoxResponse(userEntity, GeneralUtils::UTF16ToWTF8(identifier));
|
|
}
|
|
|
|
auto* racingControlComponent = entity->GetComponent<RacingControlComponent>();
|
|
|
|
if (racingControlComponent != nullptr) {
|
|
racingControlComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier));
|
|
}
|
|
|
|
for (auto* shootingGallery : Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY)) {
|
|
shootingGallery->OnMessageBoxResponse(userEntity, iButton, identifier, userData);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleChoiceBoxRespond(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
int32_t iButton;
|
|
uint32_t buttonIdentifierLength;
|
|
std::u16string buttonIdentifier;
|
|
uint32_t identifierLength;
|
|
std::u16string identifier;
|
|
|
|
inStream.Read(buttonIdentifierLength);
|
|
for (size_t i = 0; i < buttonIdentifierLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
buttonIdentifier.push_back(character);
|
|
}
|
|
|
|
inStream.Read(iButton);
|
|
|
|
inStream.Read(identifierLength);
|
|
for (size_t i = 0; i < identifierLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
identifier.push_back(character);
|
|
}
|
|
|
|
LOG("Button: %d; LOT: %u buttonIdentifier: %s; userData: %s", iButton, entity->GetLOT(), GeneralUtils::UTF16ToWTF8(buttonIdentifier).c_str(), GeneralUtils::UTF16ToWTF8(identifier).c_str());
|
|
|
|
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
|
|
|
if (user == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* userEntity = user->GetLastUsedChar()->GetEntity();
|
|
|
|
if (userEntity == nullptr) {
|
|
return;
|
|
}
|
|
|
|
entity->OnChoiceBoxResponse(userEntity, iButton, buttonIdentifier, identifier);
|
|
}
|
|
|
|
void GameMessages::SendDisplayZoneSummary(LWOOBJID objectId, const SystemAddress& sysAddr, bool isPropertyMap, bool isZoneStart, LWOOBJID sender) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::DISPLAY_ZONE_SUMMARY);
|
|
|
|
bitStream.Write(isPropertyMap);
|
|
bitStream.Write(isZoneStart);
|
|
bitStream.Write(sender != LWOOBJID_EMPTY);
|
|
if (sender != LWOOBJID_EMPTY) bitStream.Write(sender);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
//UI
|
|
|
|
void GameMessages::SendNotifyNotEnoughInvSpace(LWOOBJID objectId, uint32_t freeSlotsNeeded, eInventoryType inventoryType, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::VEHICLE_NOTIFY_FINISHED_RACE);
|
|
|
|
bitStream.Write(freeSlotsNeeded);
|
|
bitStream.Write(inventoryType != 0);
|
|
if (inventoryType != 0) bitStream.Write(inventoryType);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendDisplayMessageBox(LWOOBJID objectId, bool bShow, LWOOBJID callbackClient, const std::u16string& identifier, int32_t imageID, const std::u16string& text, const std::u16string& userData, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::DISPLAY_MESSAGE_BOX);
|
|
|
|
bitStream.Write(bShow);
|
|
bitStream.Write(callbackClient);
|
|
|
|
bitStream.Write<uint32_t>(identifier.size());
|
|
for (const auto character : identifier) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write(imageID);
|
|
|
|
bitStream.Write<uint32_t>(text.size());
|
|
for (const auto character : text) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write<uint32_t>(userData.size());
|
|
for (const auto character : userData) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendDisplayChatBubble(LWOOBJID objectId, const std::u16string& text, const SystemAddress& sysAddr) {
|
|
// MessageType::Game::DISPLAY_CHAT_BUBBLE
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::DISPLAY_CHAT_BUBBLE);
|
|
|
|
bitStream.Write<uint32_t>(text.size());
|
|
for (const auto character : text) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendChangeIdleFlags(LWOOBJID objectId, eAnimationFlags flagsOn, eAnimationFlags flagsOff, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::CHANGE_IDLE_FLAGS);
|
|
bitStream.Write<bool>(flagsOff != eAnimationFlags::IDLE_NONE);
|
|
if (flagsOff != eAnimationFlags::IDLE_NONE) bitStream.Write(flagsOff);
|
|
bitStream.Write<bool>(flagsOn != eAnimationFlags::IDLE_NONE);
|
|
if (flagsOn != eAnimationFlags::IDLE_NONE) bitStream.Write(flagsOn);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
// Mounts
|
|
|
|
void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_MOUNT_INVENTORY_ID);
|
|
bitStream.Write(objectID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
|
|
void GameMessages::HandleDismountComplete(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
// Get the objectID from the bitstream
|
|
LWOOBJID objectId{};
|
|
inStream.Read(objectId);
|
|
|
|
// If we aren't possessing somethings, the don't do anything
|
|
if (objectId != LWOOBJID_EMPTY) {
|
|
auto* possessorComponent = entity->GetComponent<PossessorComponent>();
|
|
auto* mount = Game::entityManager->GetEntity(objectId);
|
|
// make sure we have the things we need and they aren't null
|
|
if (possessorComponent && mount) {
|
|
if (!possessorComponent->GetIsDismounting()) return;
|
|
possessorComponent->SetIsDismounting(false);
|
|
possessorComponent->SetPossessable(LWOOBJID_EMPTY);
|
|
possessorComponent->SetPossessableType(ePossessionType::NO_POSSESSION);
|
|
|
|
// character related things
|
|
auto* character = entity->GetComponent<CharacterComponent>();
|
|
if (character) {
|
|
// If we had an active item turn it off
|
|
if (possessorComponent->GetMountItemID() != LWOOBJID_EMPTY) GameMessages::SendMarkInventoryItemAsActive(entity->GetObjectID(), false, eUnequippableActiveType::MOUNT, possessorComponent->GetMountItemID(), entity->GetSystemAddress());
|
|
possessorComponent->SetMountItemID(LWOOBJID_EMPTY);
|
|
}
|
|
|
|
// Set that the controllabel phsyics comp is teleporting
|
|
auto* controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
|
|
if (controllablePhysicsComponent) controllablePhysicsComponent->SetIsTeleporting(true);
|
|
|
|
// Call dismoint on the possessable comp to let it handle killing the possessable
|
|
auto* possessableComponent = mount->GetComponent<PossessableComponent>();
|
|
if (possessableComponent) possessableComponent->Dismount();
|
|
|
|
// Update the entity that was possessing
|
|
Game::entityManager->SerializeEntity(entity);
|
|
|
|
// We aren't mounted so remove the stun
|
|
GameMessages::SendSetStunned(entity->GetObjectID(), eStateChangeType::POP, UNASSIGNED_SYSTEM_ADDRESS, LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleAcknowledgePossession(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
Game::entityManager->SerializeEntity(entity);
|
|
LWOOBJID objectId{};
|
|
inStream.Read(objectId);
|
|
auto* mount = Game::entityManager->GetEntity(objectId);
|
|
if (mount) Game::entityManager->SerializeEntity(mount);
|
|
}
|
|
|
|
//Racing
|
|
|
|
void GameMessages::HandleModuleAssemblyQueryData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
auto* moduleAssemblyComponent = entity->GetComponent<ModuleAssemblyComponent>();
|
|
|
|
LOG("Got Query from %i", entity->GetLOT());
|
|
|
|
if (moduleAssemblyComponent != nullptr) {
|
|
LOG("Returning assembly %s", GeneralUtils::UTF16ToWTF8(moduleAssemblyComponent->GetAssemblyPartsLOTs()).c_str());
|
|
|
|
SendModuleAssemblyDBDataForClient(entity->GetObjectID(), moduleAssemblyComponent->GetSubKey(), moduleAssemblyComponent->GetAssemblyPartsLOTs(), UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleModularAssemblyNIFCompleted(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID objectID;
|
|
|
|
inStream.Read(objectID);
|
|
}
|
|
|
|
|
|
void GameMessages::HandleVehicleSetWheelLockState(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bExtraFriction = inStream.ReadBit();
|
|
bool bLocked = inStream.ReadBit();
|
|
}
|
|
|
|
|
|
void GameMessages::HandleRacingClientReady(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID playerID;
|
|
|
|
inStream.Read(playerID);
|
|
|
|
auto* player = Game::entityManager->GetEntity(playerID);
|
|
|
|
if (player == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* racingControlComponent = Game::zoneManager->GetZoneControlObject()->GetComponent<RacingControlComponent>();
|
|
|
|
if (racingControlComponent == nullptr) {
|
|
return;
|
|
}
|
|
|
|
racingControlComponent->OnRacingClientReady(player);
|
|
}
|
|
|
|
|
|
void GameMessages::HandleRequestDie(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bClientDeath;
|
|
bool bSpawnLoot;
|
|
std::u16string deathType;
|
|
float directionRelativeAngleXZ;
|
|
float directionRelativeAngleY;
|
|
float directionRelativeForce;
|
|
eKillType killType = eKillType::VIOLENT;
|
|
LWOOBJID killerID;
|
|
LWOOBJID lootOwnerID = LWOOBJID_EMPTY;
|
|
|
|
bClientDeath = inStream.ReadBit();
|
|
bSpawnLoot = inStream.ReadBit();
|
|
|
|
uint32_t deathTypeLength = 0;
|
|
inStream.Read(deathTypeLength);
|
|
|
|
for (size_t i = 0; i < deathTypeLength; i++) {
|
|
char16_t character;
|
|
inStream.Read(character);
|
|
|
|
deathType.push_back(character);
|
|
}
|
|
|
|
inStream.Read(directionRelativeAngleXZ);
|
|
inStream.Read(directionRelativeAngleY);
|
|
inStream.Read(directionRelativeForce);
|
|
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(killType);
|
|
}
|
|
|
|
inStream.Read(killerID);
|
|
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read(lootOwnerID);
|
|
}
|
|
|
|
auto* zoneController = Game::zoneManager->GetZoneControlObject();
|
|
|
|
auto* racingControlComponent = zoneController->GetComponent<RacingControlComponent>();
|
|
|
|
LOG("Got die request: %i", entity->GetLOT());
|
|
|
|
if (racingControlComponent != nullptr) {
|
|
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
|
|
|
if (possessableComponent != nullptr) {
|
|
entity = Game::entityManager->GetEntity(possessableComponent->GetPossessor());
|
|
|
|
if (entity == nullptr) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
racingControlComponent->OnRequestDie(entity);
|
|
} else {
|
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
|
|
|
if (!destroyableComponent) return;
|
|
|
|
destroyableComponent->Smash(killerID, killType, deathType);
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleVehicleNotifyServerAddPassiveBoostAction(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
//SendVehicleAddPassiveBoostAction(entity->GetObjectID(), sysAddr);
|
|
}
|
|
|
|
|
|
void GameMessages::HandleVehicleNotifyServerRemovePassiveBoostAction(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
//SendVehicleRemovePassiveBoostAction(entity->GetObjectID(), sysAddr);
|
|
}
|
|
|
|
|
|
void GameMessages::HandleRacingPlayerInfoResetFinished(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID playerID;
|
|
|
|
inStream.Read(playerID);
|
|
|
|
auto* player = Game::entityManager->GetEntity(playerID);
|
|
|
|
if (player == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* zoneController = Game::zoneManager->GetZoneControlObject();
|
|
|
|
auto* racingControlComponent = zoneController->GetComponent<RacingControlComponent>();
|
|
|
|
LOG("Got finished: %i", entity->GetLOT());
|
|
|
|
if (racingControlComponent != nullptr) {
|
|
racingControlComponent->OnRacingPlayerInfoResetFinished(player);
|
|
}
|
|
}
|
|
|
|
void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::UPDATE_REPUTATION);
|
|
|
|
bitStream.Write(reputation);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleUpdatePropertyPerformanceCost(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
float performanceCost = 0.0f;
|
|
|
|
if (inStream.ReadBit()) inStream.Read(performanceCost);
|
|
|
|
if (performanceCost == 0.0f) return;
|
|
|
|
auto zone = Game::zoneManager->GetZone();
|
|
if (!zone) {
|
|
LOG("If you see this message, something is very wrong.");
|
|
return;
|
|
}
|
|
|
|
Database::Get()->UpdatePerformanceCost(zone->GetZoneID(), performanceCost);
|
|
}
|
|
|
|
void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID pickupObjID = LWOOBJID_EMPTY;
|
|
LWOOBJID pickupSpawnerID = LWOOBJID_EMPTY;
|
|
int32_t pickupSpawnerIndex = -1;
|
|
NiPoint3 vehiclePosition = NiPoint3Constant::ZERO;
|
|
|
|
if (inStream.ReadBit()) inStream.Read(pickupObjID);
|
|
if (inStream.ReadBit()) inStream.Read(pickupSpawnerID);
|
|
if (inStream.ReadBit()) inStream.Read(pickupSpawnerIndex);
|
|
if (inStream.ReadBit()) inStream.Read(vehiclePosition);
|
|
|
|
auto* pickup = Game::entityManager->GetEntity(pickupObjID);
|
|
|
|
if (pickup == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
|
|
|
if (possessableComponent != nullptr) {
|
|
entity = Game::entityManager->GetEntity(possessableComponent->GetPossessor());
|
|
|
|
if (entity == nullptr) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (characterComponent != nullptr) {
|
|
characterComponent->UpdatePlayerStatistic(RacingImaginationPowerUpsCollected);
|
|
}
|
|
|
|
pickup->OnFireEventServerSide(entity, "powerup");
|
|
|
|
pickup->Kill(entity);
|
|
}
|
|
|
|
|
|
void GameMessages::SendModuleAssemblyDBDataForClient(LWOOBJID objectId, LWOOBJID assemblyID, const std::u16string& data, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::MODULE_ASSEMBLY_DB_DATA_FOR_CLIENT);
|
|
|
|
bitStream.Write(assemblyID);
|
|
|
|
bitStream.Write<uint32_t>(data.size());
|
|
for (auto character : data) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendNotifyVehicleOfRacingObject(LWOOBJID objectId, LWOOBJID racingObjectID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_VEHICLE_OF_RACING_OBJECT);
|
|
|
|
bitStream.Write(racingObjectID != LWOOBJID_EMPTY);
|
|
if (racingObjectID != LWOOBJID_EMPTY) bitStream.Write(racingObjectID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendRacingPlayerLoaded(LWOOBJID objectId, LWOOBJID playerID, LWOOBJID vehicleID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::RACING_PLAYER_LOADED);
|
|
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(vehicleID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendVehicleUnlockInput(LWOOBJID objectId, bool bLockWheels, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::VEHICLE_UNLOCK_INPUT);
|
|
|
|
bitStream.Write(bLockWheels);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendVehicleSetWheelLockState(LWOOBJID objectId, bool bExtraFriction, bool bLocked, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::VEHICLE_SET_WHEEL_LOCK_STATE);
|
|
|
|
bitStream.Write(bExtraFriction);
|
|
bitStream.Write(bLocked);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendRacingSetPlayerResetInfo(LWOOBJID objectId, int32_t currentLap, uint32_t furthestResetPlane, LWOOBJID playerID, NiPoint3 respawnPos, uint32_t upcomingPlane, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::RACING_SET_PLAYER_RESET_INFO);
|
|
|
|
bitStream.Write(currentLap);
|
|
bitStream.Write(furthestResetPlane);
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(respawnPos);
|
|
bitStream.Write(upcomingPlane);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendRacingResetPlayerToLastReset(LWOOBJID objectId, LWOOBJID playerID, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::RACING_RESET_PLAYER_TO_LAST_RESET);
|
|
|
|
bitStream.Write(playerID);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendVehicleStopBoost(Entity* targetEntity, const SystemAddress& playerSysAddr, bool affectPassive) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(targetEntity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::VEHICLE_STOP_BOOST);
|
|
|
|
bitStream.Write(affectPassive);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendSetResurrectRestoreValues(Entity* targetEntity, int32_t armorRestore, int32_t healthRestore, int32_t imaginationRestore) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(targetEntity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SET_RESURRECT_RESTORE_VALUES);
|
|
|
|
bitStream.Write(armorRestore != -1);
|
|
if (armorRestore != -1) bitStream.Write(armorRestore);
|
|
|
|
bitStream.Write(healthRestore != -1);
|
|
if (healthRestore != -1) bitStream.Write(healthRestore);
|
|
|
|
bitStream.Write(imaginationRestore != -1);
|
|
if (imaginationRestore != -1) bitStream.Write(imaginationRestore);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendNotifyRacingClient(LWOOBJID objectId, int32_t eventType, int32_t param1, LWOOBJID paramObj, std::u16string paramStr, LWOOBJID singleClient, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::NOTIFY_RACING_CLIENT);
|
|
|
|
bitStream.Write(eventType != 0);
|
|
if (eventType != 0) bitStream.Write(eventType);
|
|
|
|
bitStream.Write(param1);
|
|
|
|
bitStream.Write(paramObj);
|
|
|
|
bitStream.Write<uint32_t>(paramStr.size());
|
|
for (auto character : paramStr) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write(singleClient);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendActivityEnter(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ACTIVITY_ENTER);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendActivityStart(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ACTIVITY_START);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendActivityExit(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ACTIVITY_EXIT);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendActivityStop(LWOOBJID objectId, bool bExit, bool bUserCancel, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ACTIVITY_STOP);
|
|
|
|
bitStream.Write(bExit);
|
|
bitStream.Write(bUserCancel);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendVehicleAddPassiveBoostAction(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::VEHICLE_ADD_PASSIVE_BOOST_ACTION);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendVehicleRemovePassiveBoostAction(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::VEHICLE_REMOVE_PASSIVE_BOOST_ACTION);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendVehicleNotifyFinishedRace(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::VEHICLE_NOTIFY_FINISHED_RACE);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
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 addedByTeammate, bool applyOnTeammates,
|
|
const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(MessageType::Game::ADD_BUFF);
|
|
|
|
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(casterID != LWOOBJID_EMPTY);
|
|
if (casterID != LWOOBJID_EMPTY) 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;
|
|
}
|
|
|
|
|
|
// NT
|
|
|
|
void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bAllowPartial{};
|
|
int32_t destSlot = -1;
|
|
int32_t iStackCount = 1;
|
|
eInventoryType invTypeDst = ITEMS;
|
|
eInventoryType invTypeSrc = ITEMS;
|
|
LWOOBJID itemID = LWOOBJID_EMPTY;
|
|
bool showFlyingLoot{};
|
|
LWOOBJID subkey = LWOOBJID_EMPTY;
|
|
LOT itemLOT = 0;
|
|
|
|
bAllowPartial = inStream.ReadBit();
|
|
if (inStream.ReadBit()) inStream.Read(destSlot);
|
|
if (inStream.ReadBit()) inStream.Read(iStackCount);
|
|
if (inStream.ReadBit()) inStream.Read(invTypeDst);
|
|
if (inStream.ReadBit()) inStream.Read(invTypeSrc);
|
|
if (inStream.ReadBit()) inStream.Read(itemID);
|
|
showFlyingLoot = inStream.ReadBit();
|
|
if (inStream.ReadBit()) inStream.Read(subkey);
|
|
if (inStream.ReadBit()) inStream.Read(itemLOT);
|
|
|
|
if (invTypeDst == invTypeSrc) {
|
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
|
return;
|
|
}
|
|
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
|
|
if (inventoryComponent) {
|
|
if (itemID != LWOOBJID_EMPTY) {
|
|
auto* item = inventoryComponent->FindItemById(itemID);
|
|
|
|
if (!item) {
|
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
if (item->GetLot() == 6086) { // Thinking hat
|
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT);
|
|
return;
|
|
}
|
|
|
|
auto* destInv = inventoryComponent->GetInventory(invTypeDst);
|
|
if (destInv && destInv->GetEmptySlots() == 0) {
|
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL);
|
|
return;
|
|
}
|
|
|
|
// Despawn the pet if we are moving that pet to the vault.
|
|
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
|
|
if (petComponent && petComponent->GetDatabaseId() == item->GetSubKey()) {
|
|
inventoryComponent->DespawnPet();
|
|
}
|
|
|
|
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
|
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS);
|
|
}
|
|
} else {
|
|
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
|
}
|
|
}
|
|
|
|
void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES);
|
|
|
|
bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS);
|
|
if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination);
|
|
|
|
bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS);
|
|
if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource);
|
|
|
|
bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
|
|
if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SHOW_ACTIVITY_COUNTDOWN);
|
|
|
|
bitStream.Write(bPlayAdditionalSound);
|
|
|
|
bitStream.Write(bPlayCountdownSound);
|
|
|
|
bitStream.Write<uint32_t>(sndName.size());
|
|
for (auto character : sndName) {
|
|
bitStream.Write(character);
|
|
}
|
|
|
|
bitStream.Write(stateToPlaySoundOn);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------- Handlers ------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
void GameMessages::HandleToggleGhostReferenceOverride(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bOverride = false;
|
|
|
|
inStream.Read(bOverride);
|
|
|
|
auto* player = PlayerManager::GetPlayer(sysAddr);
|
|
|
|
if (player != nullptr) {
|
|
auto* ghostComponent = entity->GetComponent<GhostComponent>();
|
|
if (ghostComponent) ghostComponent->SetGhostOverride(bOverride);
|
|
|
|
Game::entityManager->UpdateGhosting(player);
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
NiPoint3 position;
|
|
|
|
inStream.Read(position);
|
|
|
|
auto* player = PlayerManager::GetPlayer(sysAddr);
|
|
|
|
if (player != nullptr) {
|
|
auto* ghostComponent = entity->GetComponent<GhostComponent>();
|
|
if (ghostComponent) ghostComponent->SetGhostOverridePoint(position);
|
|
|
|
Game::entityManager->UpdateGhosting(player);
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleBuyFromVendor(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bConfirmed{}; // This doesn't appear to do anything. Further research is needed.
|
|
bool countIsDefault{};
|
|
int count = 1;
|
|
LOT item;
|
|
|
|
inStream.Read(bConfirmed);
|
|
inStream.Read(countIsDefault);
|
|
if (countIsDefault) inStream.Read(count);
|
|
inStream.Read(item);
|
|
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!player) return;
|
|
|
|
// handle buying normal items
|
|
auto* vendorComponent = entity->GetComponent<VendorComponent>();
|
|
if (vendorComponent) {
|
|
vendorComponent->Buy(player, item, count);
|
|
return;
|
|
}
|
|
|
|
// handle buying achievement items
|
|
auto* achievementVendorComponent = entity->GetComponent<AchievementVendorComponent>();
|
|
if (achievementVendorComponent) {
|
|
achievementVendorComponent->Buy(player, item, count);
|
|
return;
|
|
}
|
|
|
|
// Handle buying properties
|
|
auto* propertyVendorComponent = entity->GetComponent<PropertyVendorComponent>();
|
|
if (propertyVendorComponent) {
|
|
propertyVendorComponent->OnBuyFromVendor(player, bConfirmed, item, count);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleSellToVendor(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool countIsDefault{};
|
|
int count = 1;
|
|
LWOOBJID iObjID;
|
|
|
|
inStream.Read(countIsDefault);
|
|
if (countIsDefault) inStream.Read(count);
|
|
inStream.Read(iObjID);
|
|
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!player) return;
|
|
Character* character = player->GetCharacter();
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(player->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
VendorComponent* vend = static_cast<VendorComponent*>(entity->GetComponent(eReplicaComponentType::VENDOR));
|
|
if (!vend) return;
|
|
|
|
Item* item = inv->FindItemById(iObjID);
|
|
if (!item) return;
|
|
|
|
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
|
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
|
|
|
|
int itemCompID = compRegistryTable->GetByIDAndType(item->GetLot(), eReplicaComponentType::ITEM);
|
|
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
|
|
|
|
// Items with a base value of 0 or max int are special items that should not be sold if they're not sub items
|
|
if (itemComp.baseValue == 0 || itemComp.baseValue == UINT_MAX) {
|
|
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::SELL_FAIL);
|
|
return;
|
|
}
|
|
|
|
float sellScalar = vend->GetSellScalar();
|
|
if (Inventory::IsValidItem(itemComp.currencyLOT)) {
|
|
const auto altCurrency = static_cast<uint32_t>(itemComp.altCurrencyCost * sellScalar) * count;
|
|
inv->AddItem(itemComp.currencyLOT, std::floor(altCurrency), eLootSourceType::VENDOR); // Return alt currencies like faction tokens.
|
|
}
|
|
|
|
inv->MoveItemToInventory(item, eInventoryType::VENDOR_BUYBACK, count, true, false, true);
|
|
character->SetCoins(std::floor(character->GetCoins() + (static_cast<uint32_t>(itemComp.baseValue * sellScalar) * count)), eLootSourceType::VENDOR);
|
|
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::SELL_SUCCESS);
|
|
}
|
|
|
|
void GameMessages::HandleBuybackFromVendor(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool confirmed = false;
|
|
bool countIsDefault{};
|
|
int count = 1;
|
|
LWOOBJID iObjID;
|
|
|
|
inStream.Read(confirmed);
|
|
inStream.Read(countIsDefault);
|
|
if (countIsDefault) inStream.Read(count);
|
|
inStream.Read(iObjID);
|
|
|
|
//if (!confirmed) return; they always built in this confirmed garbage... but never used it?
|
|
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!player) return;
|
|
Character* character = player->GetCharacter();
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(player->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
VendorComponent* vend = static_cast<VendorComponent*>(entity->GetComponent(eReplicaComponentType::VENDOR));
|
|
if (!vend) return;
|
|
|
|
Item* item = inv->FindItemById(iObjID);
|
|
if (!item) return;
|
|
|
|
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
|
CDItemComponentTable* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
|
|
|
|
int itemCompID = compRegistryTable->GetByIDAndType(item->GetLot(), eReplicaComponentType::ITEM);
|
|
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
|
|
|
|
float sellScalar = vend->GetSellScalar();
|
|
|
|
const auto cost = static_cast<uint32_t>(std::floor(((itemComp.baseValue * sellScalar) * count)));
|
|
|
|
if (character->GetCoins() < cost) {
|
|
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_FAIL);
|
|
return;
|
|
}
|
|
|
|
if (Inventory::IsValidItem(itemComp.currencyLOT)) {
|
|
const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * sellScalar) * count;
|
|
if (inv->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) {
|
|
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_FAIL);
|
|
return;
|
|
}
|
|
inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
|
|
}
|
|
|
|
//inv->RemoveItem(count, -1, iObjID);
|
|
inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false);
|
|
character->SetCoins(character->GetCoins() - cost, eLootSourceType::VENDOR);
|
|
//Game::entityManager->SerializeEntity(player); // so inventory updates
|
|
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_SUCCESS);
|
|
}
|
|
|
|
void GameMessages::HandleParseChatMessage(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
std::u16string wsString;
|
|
int iClientState;
|
|
inStream.Read(iClientState);
|
|
|
|
uint32_t wsStringLength;
|
|
inStream.Read(wsStringLength);
|
|
for (uint32_t i = 0; i < wsStringLength; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
wsString.push_back(character);
|
|
}
|
|
|
|
if (wsString[0] == L'/') {
|
|
SlashCommandHandler::HandleChatCommand(wsString, entity, sysAddr);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleFireEventServerSide(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint32_t argsLength{};
|
|
std::u16string args{};
|
|
bool param1IsDefault{};
|
|
int param1 = -1;
|
|
bool param2IsDefault{};
|
|
int param2 = -1;
|
|
bool param3IsDefault{};
|
|
int param3 = -1;
|
|
LWOOBJID senderID{};
|
|
|
|
inStream.Read(argsLength);
|
|
for (uint32_t i = 0; i < argsLength; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
args.push_back(character);
|
|
}
|
|
inStream.Read(param1IsDefault);
|
|
if (param1IsDefault) inStream.Read(param1);
|
|
inStream.Read(param2IsDefault);
|
|
if (param2IsDefault) inStream.Read(param2);
|
|
inStream.Read(param3IsDefault);
|
|
if (param3IsDefault) inStream.Read(param3);
|
|
inStream.Read(senderID);
|
|
|
|
auto* sender = Game::entityManager->GetEntity(senderID);
|
|
auto* player = PlayerManager::GetPlayer(sysAddr);
|
|
|
|
if (!player) {
|
|
return;
|
|
}
|
|
|
|
// This should probably get it's own "ServerEvents" system or something at some point
|
|
if (args == u"ZonePlayer") {
|
|
// Should probably check to make sure they're using a launcher at some point before someone makes a hack that lets you testmap
|
|
|
|
LWOCLONEID cloneId = 0;
|
|
LWOMAPID mapId = 0;
|
|
|
|
auto* rocketPad = entity->GetComponent<RocketLaunchpadControlComponent>();
|
|
|
|
if (rocketPad == nullptr) return;
|
|
|
|
cloneId = rocketPad->GetSelectedCloneId(player->GetObjectID());
|
|
|
|
if (param2) {
|
|
mapId = rocketPad->GetDefaultZone();
|
|
} else {
|
|
mapId = param3;
|
|
}
|
|
|
|
if (mapId == 0) {
|
|
mapId = rocketPad->GetSelectedMapId(player->GetObjectID());
|
|
}
|
|
|
|
if (mapId == 0) {
|
|
mapId = Game::zoneManager->GetZoneID().GetMapID(); // Fallback to sending the player back to the same zone.
|
|
}
|
|
|
|
LOG("Player %llu has requested zone transfer to (%i, %i).", sender->GetObjectID(), static_cast<int>(mapId), static_cast<int>(cloneId));
|
|
|
|
auto* character = player->GetCharacter();
|
|
|
|
if (mapId <= 0) {
|
|
return;
|
|
}
|
|
|
|
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, mapId, cloneId, false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
|
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
|
|
|
if (character) {
|
|
character->SetZoneID(zoneID);
|
|
character->SetZoneInstance(zoneInstance);
|
|
character->SetZoneClone(zoneClone);
|
|
}
|
|
|
|
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
|
|
return;
|
|
});
|
|
}
|
|
|
|
entity->OnFireEventServerSide(sender, GeneralUtils::UTF16ToWTF8(args), param1, param2, param3);
|
|
}
|
|
|
|
void GameMessages::HandleRequestPlatformResync(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
if (entity->GetLOT() == 6267 || entity->GetLOT() == 16141) return;
|
|
GameMessages::SendPlatformResync(entity, sysAddr);
|
|
}
|
|
|
|
void GameMessages::HandleQuickBuildCancel(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool bEarlyRelease;
|
|
LWOOBJID userID;
|
|
|
|
inStream.Read(bEarlyRelease);
|
|
inStream.Read(userID);
|
|
|
|
auto* quickBuildComponent = static_cast<QuickBuildComponent*>(entity->GetComponent(eReplicaComponentType::QUICK_BUILD));;
|
|
if (!quickBuildComponent) return;
|
|
|
|
quickBuildComponent->CancelQuickBuild(Game::entityManager->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY);
|
|
}
|
|
|
|
void GameMessages::HandleRequestUse(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool bIsMultiInteractUse = false;
|
|
unsigned int multiInteractID;
|
|
int multiInteractType;
|
|
bool secondary;
|
|
LWOOBJID objectID;
|
|
|
|
inStream.Read(bIsMultiInteractUse);
|
|
inStream.Read(multiInteractID);
|
|
inStream.Read(multiInteractType);
|
|
inStream.Read(objectID);
|
|
inStream.Read(secondary);
|
|
|
|
Entity* interactedObject = Game::entityManager->GetEntity(objectID);
|
|
|
|
if (interactedObject == nullptr) {
|
|
LOG("Object %llu tried to interact, but doesn't exist!", objectID);
|
|
|
|
return;
|
|
}
|
|
|
|
if (interactedObject->GetLOT() == 9524) {
|
|
entity->GetCharacter()->SetBuildMode(true);
|
|
}
|
|
|
|
if (bIsMultiInteractUse) {
|
|
if (multiInteractType == 0) {
|
|
auto* missionOfferComponent = static_cast<MissionOfferComponent*>(interactedObject->GetComponent(eReplicaComponentType::MISSION_OFFER));
|
|
|
|
if (missionOfferComponent != nullptr) {
|
|
missionOfferComponent->OfferMissions(entity, multiInteractID);
|
|
}
|
|
} else {
|
|
interactedObject->OnUse(entity);
|
|
}
|
|
} else {
|
|
interactedObject->OnUse(entity);
|
|
}
|
|
|
|
//Perform use task if possible:
|
|
auto missionComponent = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION));
|
|
|
|
if (missionComponent == nullptr) return;
|
|
|
|
missionComponent->Progress(eMissionTaskType::TALK_TO_NPC, interactedObject->GetLOT(), interactedObject->GetObjectID());
|
|
missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID());
|
|
}
|
|
|
|
void GameMessages::HandlePlayEmote(RakNet::BitStream& inStream, Entity* entity) {
|
|
int emoteID;
|
|
LWOOBJID targetID;
|
|
|
|
inStream.Read(emoteID);
|
|
inStream.Read(targetID);
|
|
|
|
LOG_DEBUG("Emote (%i) (%llu)", emoteID, targetID);
|
|
|
|
//TODO: If targetID != 0, and we have one of the "perform emote" missions, complete them.
|
|
|
|
if (emoteID == 0) return;
|
|
std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote
|
|
|
|
CDEmoteTableTable* emotes = CDClientManager::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;
|
|
|
|
if (targetID != LWOOBJID_EMPTY) {
|
|
auto* targetEntity = Game::entityManager->GetEntity(targetID);
|
|
|
|
LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr);
|
|
|
|
if (targetEntity != nullptr) {
|
|
targetEntity->OnEmoteReceived(emoteID, entity);
|
|
missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID);
|
|
}
|
|
} else {
|
|
LOG_DEBUG("Target ID is empty, using backup");
|
|
const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT);
|
|
|
|
const auto& referencePoint = entity->GetPosition();
|
|
|
|
for (auto* scripted : scriptedEntities) {
|
|
if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue;
|
|
|
|
scripted->OnEmoteReceived(emoteID, entity);
|
|
missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID());
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
LWOOBJID modelID;
|
|
|
|
inStream.Read(modelID);
|
|
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(modelID);
|
|
|
|
if (item == nullptr) {
|
|
return;
|
|
}
|
|
|
|
item->Disassemble(TEMP_MODELS);
|
|
|
|
std::unique_ptr<sql::PreparedStatement> stmt(Database::Get()->CreatePreppedStmt("DELETE FROM ugc_modular_build where ugc_id = ?"));
|
|
stmt->setUInt64(1, item->GetSubKey());
|
|
stmt->execute();
|
|
|
|
item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD);
|
|
}
|
|
|
|
void GameMessages::HandleSetFlag(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool bFlag{};
|
|
int32_t iFlagID{};
|
|
|
|
inStream.Read(bFlag);
|
|
inStream.Read(iFlagID);
|
|
|
|
auto character = entity->GetCharacter();
|
|
if (character) character->SetPlayerFlag(iFlagID, bFlag);
|
|
}
|
|
|
|
void GameMessages::HandleRespondToMission(RakNet::BitStream& inStream, Entity* entity) {
|
|
int missionID{};
|
|
LWOOBJID playerID{};
|
|
LWOOBJID receiverID{};
|
|
bool isDefaultReward{};
|
|
LOT reward = LOT_NULL;
|
|
|
|
inStream.Read(missionID);
|
|
inStream.Read(playerID);
|
|
inStream.Read(receiverID);
|
|
inStream.Read(isDefaultReward);
|
|
if (isDefaultReward) inStream.Read(reward);
|
|
|
|
MissionComponent* missionComponent = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION));
|
|
if (!missionComponent) {
|
|
LOG("Unable to get mission component for entity %llu to handle RespondToMission", playerID);
|
|
return;
|
|
}
|
|
|
|
Mission* mission = missionComponent->GetMission(missionID);
|
|
if (mission) {
|
|
mission->SetReward(reward);
|
|
} else {
|
|
LOG("Unable to get mission %i for entity %llu to update reward in RespondToMission", missionID, playerID);
|
|
}
|
|
|
|
Entity* offerer = Game::entityManager->GetEntity(receiverID);
|
|
|
|
if (offerer == nullptr) {
|
|
LOG("Unable to get receiver entity %llu for RespondToMission", receiverID);
|
|
return;
|
|
}
|
|
|
|
offerer->GetScript()->OnRespondToMission(offerer, missionID, Game::entityManager->GetEntity(playerID), reward);
|
|
}
|
|
|
|
void GameMessages::HandleMissionDialogOK(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool bIsComplete{};
|
|
eMissionState iMissionState{};
|
|
int missionID{};
|
|
LWOOBJID responder{};
|
|
Entity* player = nullptr;
|
|
|
|
inStream.Read(bIsComplete);
|
|
inStream.Read(iMissionState);
|
|
inStream.Read(missionID);
|
|
inStream.Read(responder);
|
|
player = Game::entityManager->GetEntity(responder);
|
|
|
|
if (entity) entity->GetScript()->OnMissionDialogueOK(entity, player, missionID, iMissionState);
|
|
|
|
// Get the player's mission component
|
|
MissionComponent* missionComponent = static_cast<MissionComponent*>(player->GetComponent(eReplicaComponentType::MISSION));
|
|
if (!missionComponent) {
|
|
LOG("Unable to get mission component for entity %llu to handle MissionDialogueOK", player->GetObjectID());
|
|
return;
|
|
}
|
|
|
|
if (iMissionState == eMissionState::AVAILABLE || iMissionState == eMissionState::COMPLETE_AVAILABLE) {
|
|
missionComponent->AcceptMission(missionID);
|
|
} else if (iMissionState == eMissionState::READY_TO_COMPLETE || iMissionState == eMissionState::COMPLETE_READY_TO_COMPLETE) {
|
|
missionComponent->CompleteMission(missionID);
|
|
}
|
|
|
|
if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1"
|
|
|| !player->GetCharacter()
|
|
|| !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return;
|
|
player->AddCallbackTimer(0.5f, [player]() {
|
|
if (!player) return;
|
|
GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress());
|
|
});
|
|
}
|
|
|
|
void GameMessages::HandleRequestLinkedMission(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID playerId{};
|
|
int missionId{};
|
|
bool bMissionOffered{};
|
|
|
|
inStream.Read(playerId);
|
|
inStream.Read(missionId);
|
|
inStream.Read(bMissionOffered);
|
|
|
|
auto* player = Game::entityManager->GetEntity(playerId);
|
|
|
|
auto* missionOfferComponent = static_cast<MissionOfferComponent*>(entity->GetComponent(eReplicaComponentType::MISSION_OFFER));
|
|
|
|
if (missionOfferComponent != nullptr) {
|
|
missionOfferComponent->OfferMissions(player, 0);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleHasBeenCollected(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID playerID;
|
|
inStream.Read(playerID);
|
|
|
|
Entity* player = Game::entityManager->GetEntity(playerID);
|
|
if (!player || !entity || entity->GetCollectibleID() == 0) return;
|
|
|
|
MissionComponent* missionComponent = static_cast<MissionComponent*>(player->GetComponent(eReplicaComponentType::MISSION));
|
|
if (missionComponent) {
|
|
missionComponent->Progress(eMissionTaskType::COLLECTION, entity->GetLOT(), entity->GetObjectID());
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleNotifyServerLevelProcessingComplete(RakNet::BitStream& inStream, Entity* entity) {
|
|
auto* levelComp = entity->GetComponent<LevelProgressionComponent>();
|
|
if (!levelComp) return;
|
|
auto* character = entity->GetComponent<CharacterComponent>();
|
|
if (!character) return;
|
|
|
|
//Update our character's level in memory:
|
|
levelComp->SetLevel(levelComp->GetLevel() + 1);
|
|
|
|
levelComp->HandleLevelUp();
|
|
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
|
|
if (inventoryComponent != nullptr) {
|
|
auto* inventory = inventoryComponent->GetInventory(ITEMS);
|
|
|
|
if (inventory != nullptr && Game::config->GetValue("disable_extra_backpack") != "1") {
|
|
inventory->SetSize(inventory->GetSize() + 2);
|
|
}
|
|
}
|
|
|
|
//Play the level up effect:
|
|
GameMessages::SendPlayFXEffect(entity, 7074, u"create", "7074", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
|
|
|
|
//Send a notification in chat:
|
|
std::stringstream wss;
|
|
wss << "level=1:";
|
|
wss << levelComp->GetLevel();
|
|
wss << "\n";
|
|
wss << "name=0:";
|
|
wss << character->GetName();
|
|
|
|
// FIXME: only really need utf8 conversion for the name, so move that up?
|
|
std::u16string attrs = GeneralUtils::UTF8ToUTF16(wss.str());
|
|
std::u16string wsText = u"UI_LEVEL_PROGRESSION_LEVELUP_MESSAGE";
|
|
|
|
GameMessages::SendBroadcastTextToChatbox(entity, UNASSIGNED_SYSTEM_ADDRESS, attrs, wsText);
|
|
}
|
|
|
|
void GameMessages::HandlePickupCurrency(RakNet::BitStream& inStream, Entity* entity) {
|
|
unsigned int currency;
|
|
inStream.Read(currency);
|
|
|
|
if (currency == 0) return;
|
|
|
|
auto* ch = entity->GetCharacter();
|
|
if (entity->CanPickupCoins(currency)) {
|
|
ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleRequestDie(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID killerID;
|
|
LWOOBJID lootOwnerID;
|
|
bool bDieAccepted = false;
|
|
eKillType killType;
|
|
std::u16string deathType;
|
|
float directionRelative_AngleY;
|
|
float directionRelative_AngleXZ;
|
|
float directionRelative_Force;
|
|
bool bClientDeath = false;
|
|
bool bSpawnLoot = true;
|
|
float coinSpawnTime = -1.0f;
|
|
|
|
inStream.Read(bClientDeath);
|
|
inStream.Read(bDieAccepted);
|
|
inStream.Read(bSpawnLoot);
|
|
|
|
bool coinSpawnTimeIsDefault{};
|
|
inStream.Read(coinSpawnTimeIsDefault);
|
|
if (coinSpawnTimeIsDefault != 0) inStream.Read(coinSpawnTime);
|
|
|
|
/*uint32_t deathTypeLength = deathType.size();
|
|
inStream.Read(deathTypeLength);
|
|
for (uint32_t k = 0; k < deathTypeLength; k++) {
|
|
inStream.Read<uint16_t>(deathType[k]);
|
|
}*/
|
|
|
|
inStream.Read(directionRelative_AngleXZ);
|
|
inStream.Read(directionRelative_AngleY);
|
|
inStream.Read(directionRelative_Force);
|
|
|
|
bool killTypeIsDefault{};
|
|
inStream.Read(killTypeIsDefault);
|
|
if (killTypeIsDefault != 0) inStream.Read(killType);
|
|
|
|
inStream.Read(lootOwnerID);
|
|
inStream.Read(killerID);
|
|
}
|
|
|
|
void GameMessages::HandleEquipItem(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool immediate;
|
|
LWOOBJID objectID;
|
|
inStream.Read(immediate);
|
|
inStream.Read(immediate); //twice?
|
|
inStream.Read(objectID);
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
Item* item = inv->FindItemById(objectID);
|
|
if (!item) return;
|
|
|
|
item->Equip();
|
|
|
|
Game::entityManager->SerializeEntity(entity);
|
|
}
|
|
|
|
void GameMessages::HandleUnequipItem(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool immediate;
|
|
LWOOBJID objectID;
|
|
inStream.Read(immediate);
|
|
inStream.Read(immediate);
|
|
inStream.Read(immediate);
|
|
inStream.Read(objectID);
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(objectID);
|
|
|
|
if (!item) return;
|
|
|
|
item->UnEquip();
|
|
|
|
Game::entityManager->SerializeEntity(entity);
|
|
}
|
|
|
|
void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
// this is used for a lot more than just inventory trashing (trades, vendors, etc.) but for now since it's just used for that, that's all im going to implement
|
|
bool bConfirmed = false;
|
|
bool bDeleteItem = true;
|
|
bool bOutSuccess = false;
|
|
bool eInvTypeIsDefault = false;
|
|
int eInvType = INVENTORY_MAX;
|
|
bool eLootTypeSourceIsDefault = false;
|
|
int eLootTypeSource = LOOTTYPE_NONE;
|
|
LWONameValue extraInfo;
|
|
bool forceDeletion = true;
|
|
bool iLootTypeSourceIsDefault = false;
|
|
LWOOBJID iLootTypeSource = LWOOBJID_EMPTY;
|
|
bool iObjIDIsDefault = false;
|
|
LWOOBJID iObjID = LWOOBJID_EMPTY;
|
|
bool iObjTemplateIsDefault = false;
|
|
LOT iObjTemplate = LOT_NULL;
|
|
bool iRequestingObjIDIsDefault = false;
|
|
LWOOBJID iRequestingObjID = LWOOBJID_EMPTY;
|
|
bool iStackCountIsDefault = false;
|
|
uint32_t iStackCount = 1;
|
|
bool iStackRemainingIsDefault = false;
|
|
uint32_t iStackRemaining = 0;
|
|
bool iSubkeyIsDefault = false;
|
|
LWOOBJID iSubkey = LWOOBJID_EMPTY;
|
|
bool iTradeIDIsDefault = false;
|
|
LWOOBJID iTradeID = LWOOBJID_EMPTY;
|
|
|
|
inStream.Read(bConfirmed);
|
|
inStream.Read(bDeleteItem);
|
|
inStream.Read(bOutSuccess);
|
|
inStream.Read(eInvTypeIsDefault);
|
|
if (eInvTypeIsDefault) inStream.Read(eInvType);
|
|
inStream.Read(eLootTypeSourceIsDefault);
|
|
if (eLootTypeSourceIsDefault) inStream.Read(eLootTypeSource);
|
|
inStream.Read(extraInfo.length);
|
|
if (extraInfo.length > 0) {
|
|
for (uint32_t i = 0; i < extraInfo.length; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
extraInfo.name.push_back(character);
|
|
}
|
|
uint16_t nullTerm;
|
|
inStream.Read(nullTerm);
|
|
}
|
|
inStream.Read(forceDeletion);
|
|
inStream.Read(iLootTypeSourceIsDefault);
|
|
if (iLootTypeSourceIsDefault) inStream.Read(iLootTypeSource);
|
|
inStream.Read(iObjIDIsDefault);
|
|
if (iObjIDIsDefault) inStream.Read(iObjID);
|
|
inStream.Read(iObjTemplateIsDefault);
|
|
if (iObjTemplateIsDefault) inStream.Read(iObjTemplate);
|
|
inStream.Read(iRequestingObjIDIsDefault);
|
|
if (iRequestingObjIDIsDefault) inStream.Read(iRequestingObjID);
|
|
inStream.Read(iStackCountIsDefault);
|
|
if (iStackCountIsDefault) inStream.Read(iStackCount);
|
|
inStream.Read(iStackRemainingIsDefault);
|
|
if (iStackRemainingIsDefault) inStream.Read(iStackRemaining);
|
|
inStream.Read(iSubkeyIsDefault);
|
|
if (iSubkeyIsDefault) inStream.Read(iSubkey);
|
|
inStream.Read(iTradeIDIsDefault);
|
|
if (iTradeIDIsDefault) inStream.Read(iTradeID);
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(iObjID);
|
|
|
|
if (item == nullptr) {
|
|
return;
|
|
}
|
|
|
|
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
|
|
|
|
if (bConfirmed) {
|
|
const auto itemType = static_cast<eItemType>(item->GetInfo().itemType);
|
|
if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) {
|
|
item->DisassembleModel(iStackCount);
|
|
}
|
|
auto lot = item->GetLot();
|
|
item->SetCount(item->GetCount() - iStackCount, true);
|
|
Game::entityManager->SerializeEntity(entity);
|
|
|
|
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
|
|
|
if (missionComponent != nullptr) {
|
|
missionComponent->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", -iStackCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(target);
|
|
bitStream.Write(MessageType::Game::SET_GRAVITY_SCALE);
|
|
|
|
bitStream.Write(effectScale);
|
|
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleMoveItemInInventory(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool destInvTypeIsDefault = false;
|
|
int32_t destInvType = eInventoryType::INVALID;
|
|
LWOOBJID iObjID;
|
|
int inventoryType;
|
|
int responseCode;
|
|
int slot;
|
|
inStream.Read(destInvTypeIsDefault);
|
|
if (destInvTypeIsDefault) { inStream.Read(destInvType); }
|
|
inStream.Read(iObjID);
|
|
inStream.Read(inventoryType);
|
|
inStream.Read(responseCode);
|
|
inStream.Read(slot);
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(iObjID);
|
|
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
inv->MoveStack(item, static_cast<eInventoryType>(destInvType), slot);
|
|
Game::entityManager->SerializeEntity(entity);
|
|
}
|
|
|
|
void GameMessages::HandleMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
eInventoryType inventoryTypeA;
|
|
eInventoryType inventoryTypeB;
|
|
LWOOBJID objectID;
|
|
bool showFlyingLoot = true;
|
|
bool stackCountIsDefault = false;
|
|
uint32_t stackCount = 1;
|
|
bool templateIDIsDefault = false;
|
|
LOT templateID = LOT_NULL;
|
|
|
|
inStream.Read(inventoryTypeA);
|
|
inStream.Read(inventoryTypeB);
|
|
inStream.Read(objectID);
|
|
inStream.Read(showFlyingLoot);
|
|
inStream.Read(stackCountIsDefault);
|
|
if (stackCountIsDefault) inStream.Read(stackCount);
|
|
inStream.Read(templateIDIsDefault);
|
|
if (templateIDIsDefault) inStream.Read(templateID);
|
|
|
|
auto inv = entity->GetComponent<InventoryComponent>();
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(objectID);
|
|
|
|
if (!item) {
|
|
// Attempt to find the item by lot in inventory A since A is the source inventory.
|
|
item = inv->FindItemByLot(templateID, static_cast<eInventoryType>(inventoryTypeA));
|
|
if (!item) {
|
|
// As a final resort, try to find the item in its default inventory based on type.
|
|
item = inv->FindItemByLot(templateID);
|
|
if (!item) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entity->GetCharacter()) {
|
|
if (entity->GetCharacter()->GetBuildMode()) {
|
|
showFlyingLoot = false;
|
|
}
|
|
}
|
|
|
|
inv->MoveItemToInventory(item, inventoryTypeB, stackCount, showFlyingLoot);
|
|
Game::entityManager->SerializeEntity(entity);
|
|
}
|
|
|
|
void GameMessages::HandleBuildModeSet(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool bStart = false;
|
|
|
|
inStream.Read(bStart);
|
|
// there's more here but we don't need it (for now?)
|
|
|
|
LOG("Set build mode to (%d) for (%llu)", bStart, entity->GetObjectID());
|
|
|
|
if (entity->GetCharacter()) {
|
|
entity->GetCharacter()->SetBuildMode(bStart);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleModularBuildFinish(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
|
|
uint8_t count; // 3 for rockets, 7 for cars
|
|
|
|
inStream.Read(count);
|
|
|
|
auto* temp = inv->GetInventory(TEMP_MODELS);
|
|
std::vector<LOT> modList;
|
|
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
|
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
|
if (count >= 3) {
|
|
std::u16string modules;
|
|
|
|
for (uint32_t k = 0; k < count; k++) {
|
|
uint32_t mod;
|
|
inStream.Read(mod);
|
|
modList.push_back(mod);
|
|
auto modToStr = GeneralUtils::to_u16string(mod);
|
|
modules += u"1:" + (modToStr);
|
|
if (k + 1 != count) modules += u"+";
|
|
|
|
if (temp->GetLotCount(mod) > 0) {
|
|
inv->RemoveItem(mod, 1, TEMP_MODELS);
|
|
} else {
|
|
inv->RemoveItem(mod, 1);
|
|
}
|
|
|
|
// Doing this check for 1 singular mission that needs to know when you've swapped every part out during a car modular build.
|
|
// since all 8129's are the same, skip checking that
|
|
if (mod != 8129) {
|
|
if (oldPartList.find(GeneralUtils::UTF16ToWTF8(modToStr)) != std::string::npos) everyPieceSwapped = false;
|
|
|
|
}
|
|
}
|
|
|
|
ObjectIDManager::RequestPersistentID([=](uint32_t newId) {
|
|
LOG("Build finished");
|
|
GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build
|
|
GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it
|
|
|
|
//inv->UnequipItem(inv->GetItemStackByLOT(6086, eInventoryType::ITEMS)); // take off the thinking cap
|
|
//Game::entityManager->SerializeEntity(entity);
|
|
|
|
const auto moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", modules);
|
|
|
|
std::vector<LDFBaseData*> config;
|
|
config.push_back(moduleAssembly);
|
|
|
|
LWOOBJID newIdBig = newId;
|
|
GeneralUtils::SetBit(newIdBig, eObjectBits::CHARACTER);
|
|
|
|
if (count == 3) {
|
|
inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
|
} else if (count == 7) {
|
|
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
|
}
|
|
|
|
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).c_str());
|
|
auto* pCharacter = character->GetCharacter();
|
|
pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT);
|
|
stmt->execute();
|
|
|
|
auto* missionComponent = character->GetComponent<MissionComponent>();
|
|
|
|
if (entity->GetLOT() != 9980 || Game::server->GetZoneID() != 1200) {
|
|
if (missionComponent != nullptr) {
|
|
missionComponent->Progress(eMissionTaskType::SCRIPT, entity->GetLOT(), entity->GetObjectID());
|
|
if (count >= 7 && everyPieceSwapped) missionComponent->Progress(eMissionTaskType::RACING, LWOOBJID_EMPTY, static_cast<LWOOBJID>(eRacingTaskParam::MODULAR_BUILDING));
|
|
}
|
|
}
|
|
|
|
ScriptComponent* script = static_cast<ScriptComponent*>(entity->GetComponent(eReplicaComponentType::SCRIPT));
|
|
|
|
entity->GetScript()->OnModularBuildExit(entity, character, count >= 3, modList);
|
|
|
|
// Move remaining temp models back to models
|
|
std::vector<Item*> items;
|
|
|
|
for (const auto& pair : temp->GetItems()) {
|
|
items.push_back(pair.second);
|
|
}
|
|
|
|
for (auto* item : items) {
|
|
inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleDoneArrangingWithItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
/**
|
|
[int] - newSourceBAG
|
|
[LWOOBJID] - newSourceID
|
|
[LOT] - newSourceLOT
|
|
[int] - newSourceTYPE
|
|
[LWOOBJID] - newTargetID
|
|
[LOT] - newTargetLOT
|
|
[int] - newTargetTYPE
|
|
[NiPoint3] - newTtargetPOS
|
|
[int] - oldItemBAG
|
|
[LWOOBJID] - oldItemID
|
|
[LOT] - oldItemLOT
|
|
[int] - oldItemTYPE
|
|
*/
|
|
int newSourceBAG = 0;
|
|
LWOOBJID newSourceID = 0;
|
|
LOT newSourceLOT = 0;
|
|
int newSourceTYPE = 0;
|
|
LWOOBJID newTargetID = 0;
|
|
LOT newTargetLOT = 0;
|
|
int newTargetTYPE = 0;
|
|
NiPoint3 newTargetPOS;
|
|
int oldItemBAG = 0;
|
|
LWOOBJID oldItemID = 0;
|
|
LOT oldItemLOT = 0;
|
|
int oldItemTYPE = 0;
|
|
|
|
inStream.Read(newSourceBAG);
|
|
inStream.Read(newSourceID);
|
|
inStream.Read(newSourceLOT);
|
|
inStream.Read(newSourceTYPE);
|
|
inStream.Read(newTargetID);
|
|
inStream.Read(newTargetLOT);
|
|
inStream.Read(newTargetTYPE);
|
|
inStream.Read(newTargetPOS);
|
|
inStream.Read(oldItemBAG);
|
|
inStream.Read(oldItemID);
|
|
inStream.Read(oldItemLOT);
|
|
inStream.Read(oldItemTYPE);
|
|
|
|
/*
|
|
LOG("GameMessages",
|
|
"\nnewSourceBAG: %d\nnewSourceID: %llu\nnewSourceLOT: %d\nnewSourceTYPE: %d\nnewTargetID: %llu\nnewTargetLOT: %d\nnewTargetTYPE: %d\nnewTargetPOS: %f, %f, %f\noldItemBAG: %d\noldItemID: %llu\noldItemLOT: %d\noldItemTYPE: %d",
|
|
newSourceBAG, newSourceID, newSourceLOT, newSourceTYPE, newTargetID, newTargetLOT, newTargetTYPE, newTargetPOS.x, newTargetPOS.y, newTargetPOS.z, oldItemBAG, oldItemID, oldItemLOT, oldItemTYPE
|
|
);
|
|
*/
|
|
|
|
if (PropertyManagementComponent::Instance() != nullptr) {
|
|
const auto& buildAreas = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::BUILD_BORDER);
|
|
|
|
const auto& entities = Game::entityManager->GetEntitiesInGroup("PropertyPlaque");
|
|
|
|
Entity* buildArea;
|
|
|
|
if (!buildAreas.empty()) {
|
|
buildArea = buildAreas[0];
|
|
} else if (!entities.empty()) {
|
|
buildArea = entities[0];
|
|
|
|
LOG("Using PropertyPlaque");
|
|
} else {
|
|
LOG("No build area found");
|
|
|
|
return;
|
|
}
|
|
|
|
LOG("Build area found: %llu", buildArea->GetObjectID());
|
|
|
|
GameMessages::SendStartArrangingWithItem(
|
|
character,
|
|
character->GetSystemAddress(),
|
|
false,
|
|
buildArea->GetObjectID(),
|
|
character->GetPosition(),
|
|
newSourceBAG,
|
|
newSourceID,
|
|
newSourceLOT,
|
|
newSourceTYPE,
|
|
newTargetID,
|
|
newTargetLOT,
|
|
newTargetPOS,
|
|
newTargetTYPE
|
|
);
|
|
}
|
|
|
|
LOG("Done Arranging");
|
|
|
|
//GenericInventory* models = inv->GetGenericInventory(MODELS);
|
|
//GenericInventory* tempModels = inv->GetGenericInventory(TEMP_MODELS);
|
|
|
|
auto* inventory = inv->GetInventory(TEMP_MODELS);
|
|
|
|
std::vector<Item*> items;
|
|
|
|
for (const auto& pair : inventory->GetItems()) {
|
|
items.push_back(pair.second);
|
|
}
|
|
|
|
for (auto* item : items) {
|
|
inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false, false);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleModularBuildMoveAndEquip(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
|
|
LOG("Build and move");
|
|
|
|
LOT templateID;
|
|
|
|
inStream.Read(templateID);
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemByLot(templateID, TEMP_MODELS);
|
|
|
|
if (item == nullptr) {
|
|
return;
|
|
}
|
|
|
|
inv->MoveItemToInventory(item, eInventoryType::MODELS, 1, false, true);
|
|
}
|
|
|
|
void GameMessages::HandlePickupItem(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID lootObjectID;
|
|
LWOOBJID playerID;
|
|
inStream.Read(lootObjectID);
|
|
inStream.Read(playerID);
|
|
|
|
entity->PickupItem(lootObjectID);
|
|
|
|
auto* team = TeamManager::Instance()->GetTeam(entity->GetObjectID());
|
|
|
|
if (team != nullptr) {
|
|
for (const auto memberId : team->members) {
|
|
auto* member = Game::entityManager->GetEntity(memberId);
|
|
|
|
if (member == nullptr || memberId == playerID) continue;
|
|
|
|
SendTeamPickupItem(lootObjectID, lootObjectID, playerID, member->GetSystemAddress());
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleResurrect(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool immediate = inStream.ReadBit();
|
|
|
|
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
|
|
if (zoneControl) {
|
|
zoneControl->GetScript()->OnPlayerResurrected(zoneControl, entity);
|
|
}
|
|
|
|
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
|
for (Entity* scriptEntity : scriptedActs) {
|
|
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
|
scriptEntity->GetScript()->OnPlayerResurrected(scriptEntity, entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandlePushEquippedItemsState(RakNet::BitStream& inStream, Entity* entity) {
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
inv->PushEquippedItems();
|
|
}
|
|
|
|
void GameMessages::HandlePopEquippedItemsState(RakNet::BitStream& inStream, Entity* entity) {
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
inv->PopEquippedItems();
|
|
Game::entityManager->SerializeEntity(entity); // so it updates on client side
|
|
}
|
|
|
|
|
|
void GameMessages::HandleClientItemConsumed(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID itemConsumed;
|
|
|
|
inStream.Read(itemConsumed);
|
|
|
|
auto* inventory = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
|
|
if (inventory == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* item = inventory->FindItemById(itemConsumed);
|
|
if (item == nullptr) {
|
|
return;
|
|
}
|
|
LOT itemLot = item->GetLot();
|
|
|
|
item->Consume();
|
|
|
|
auto* missions = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION));
|
|
if (missions != nullptr) {
|
|
missions->Progress(eMissionTaskType::USE_ITEM, itemLot);
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleUseNonEquipmentItem(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID itemConsumed;
|
|
|
|
inStream.Read(itemConsumed);
|
|
|
|
auto* inv = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(itemConsumed);
|
|
|
|
if (item) item->UseNonEquip(item);
|
|
}
|
|
|
|
void GameMessages::HandleMatchRequest(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID activator;
|
|
//std::map<LWOOBJID, LWONameValue> additionalPlayers;
|
|
uint32_t playerChoicesLen;
|
|
std::string playerChoices;
|
|
int type;
|
|
int value;
|
|
|
|
inStream.Read(activator);
|
|
inStream.Read(playerChoicesLen);
|
|
for (uint32_t i = 0; i < playerChoicesLen; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
playerChoices.push_back(character);
|
|
}
|
|
if (playerChoicesLen > 0) {
|
|
uint16_t nullTerm;
|
|
inStream.Read(nullTerm);
|
|
}
|
|
inStream.Read(type);
|
|
inStream.Read(value);
|
|
|
|
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
|
if (type == 0) { // join
|
|
if (value != 0) {
|
|
for (Entity* scriptedAct : scriptedActs) {
|
|
ScriptedActivityComponent* comp = static_cast<ScriptedActivityComponent*>(scriptedAct->GetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY));
|
|
if (!comp) continue;
|
|
if (comp->GetActivityID() == value) {
|
|
comp->PlayerJoin(entity);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
}
|
|
} else if (type == 1) { // ready/unready
|
|
for (Entity* scriptedAct : scriptedActs) {
|
|
ScriptedActivityComponent* comp = static_cast<ScriptedActivityComponent*>(scriptedAct->GetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY));
|
|
if (!comp) continue;
|
|
if (comp->PlayerIsInQueue(entity)) {
|
|
comp->PlayerReady(entity, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleGetHotPropertyData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
SendGetHotPropertyData(inStream, entity, sysAddr);
|
|
}
|
|
|
|
void GameMessages::SendGetHotPropertyData(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
/**
|
|
* [u32] - Number of properties
|
|
* [objid] - property id
|
|
* [objid] - property owner id
|
|
* [wstring] - property owner name
|
|
* [u64] - total reputation
|
|
* [i32] - property template id
|
|
* [wstring] - property name
|
|
* [wstring] - property description
|
|
* [float] - performance cost
|
|
* [timestamp] - time last published
|
|
* [cloneid] - clone id
|
|
*
|
|
*/
|
|
// TODO This needs to be implemented when reputation is implemented for getting hot properties.
|
|
/**
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SEND_HOT_PROPERTY_DATA);
|
|
std::vector<int32_t> t = {25166, 25188, 25191, 25194};
|
|
bitStream.Write<uint32_t>(4);
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
bitStream.Write<LWOOBJID>(entity->GetObjectID());
|
|
bitStream.Write<LWOOBJID>(entity->GetObjectID());
|
|
bitStream.Write<uint32_t>(1);
|
|
bitStream.Write<uint16_t>('c');
|
|
bitStream.Write<uint64_t>(42069);
|
|
bitStream.Write<int32_t>(t[i]);
|
|
bitStream.Write<uint32_t>(1);
|
|
bitStream.Write<uint16_t>('c');
|
|
bitStream.Write<uint32_t>(1);
|
|
bitStream.Write<uint16_t>('c');
|
|
bitStream.Write<float>(420.69f);
|
|
bitStream.Write<uint64_t>(1658376385);
|
|
bitStream.Write<int32_t>(25166);
|
|
}
|
|
SEND_PACKET*/
|
|
}
|
|
|
|
void GameMessages::HandleReportBug(RakNet::BitStream& inStream, Entity* entity) {
|
|
//Definitely not stolen from autogenerated code, no sir:
|
|
IBugReports::Info reportInfo;
|
|
|
|
//Reading:
|
|
uint32_t messageLength;
|
|
inStream.Read(messageLength);
|
|
|
|
for (uint32_t i = 0; i < (messageLength); ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
reportInfo.body.push_back(static_cast<char>(character));
|
|
}
|
|
|
|
auto character = entity->GetCharacter();
|
|
if (character) reportInfo.characterId = character->GetID();
|
|
|
|
uint32_t clientVersionLength;
|
|
inStream.Read(clientVersionLength);
|
|
for (unsigned int k = 0; k < clientVersionLength; k++) {
|
|
unsigned char character;
|
|
inStream.Read(character);
|
|
reportInfo.clientVersion.push_back(character);
|
|
}
|
|
|
|
uint32_t nOtherPlayerIDLength;
|
|
inStream.Read(nOtherPlayerIDLength);
|
|
for (unsigned int k = 0; k < nOtherPlayerIDLength; k++) {
|
|
unsigned char character;
|
|
inStream.Read(character);
|
|
reportInfo.otherPlayer.push_back(character);
|
|
}
|
|
|
|
uint32_t selectionLength;
|
|
inStream.Read(selectionLength);
|
|
for (unsigned int k = 0; k < selectionLength; k++) {
|
|
unsigned char character;
|
|
inStream.Read(character);
|
|
reportInfo.selection.push_back(character);
|
|
}
|
|
|
|
Database::Get()->InsertNewBugReport(reportInfo);
|
|
}
|
|
|
|
void
|
|
GameMessages::HandleClientRailMovementReady(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR);
|
|
for (const auto* possibleRail : possibleRails) {
|
|
const auto* rail = possibleRail->GetComponent<RailActivatorComponent>();
|
|
if (rail != nullptr) {
|
|
rail->OnRailMovementReady(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleCancelRailMovement(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
const auto immediate = inStream.ReadBit();
|
|
|
|
const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR);
|
|
for (const auto* possibleRail : possibleRails) {
|
|
auto* rail = possibleRail->GetComponent<RailActivatorComponent>();
|
|
if (rail != nullptr) {
|
|
rail->OnCancelRailMovement(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandlePlayerRailArrivedNotification(RakNet::BitStream& inStream, Entity* entity,
|
|
const SystemAddress& sysAddr) {
|
|
uint32_t pathNameLength;
|
|
inStream.Read(pathNameLength);
|
|
|
|
std::u16string pathName;
|
|
for (auto k = 0; k < pathNameLength; k++) {
|
|
uint16_t c;
|
|
inStream.Read(c);
|
|
pathName.push_back(c);
|
|
}
|
|
|
|
int32_t waypointNumber;
|
|
inStream.Read(waypointNumber);
|
|
|
|
const auto possibleRails = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR);
|
|
for (auto* possibleRail : possibleRails) {
|
|
if (possibleRail) possibleRail->GetScript()->OnPlayerRailArrived(possibleRail, entity, pathName, waypointNumber);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleModifyPlayerZoneStatistic(RakNet::BitStream& inStream, Entity* entity) {
|
|
const auto set = inStream.ReadBit();
|
|
const auto statisticsName = GeneralUtils::ReadWString(inStream);
|
|
|
|
int32_t value;
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read<int32_t>(value);
|
|
} else {
|
|
value = 0;
|
|
}
|
|
|
|
LWOMAPID zone;
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read<LWOMAPID>(zone);
|
|
} else {
|
|
zone = LWOMAPID_INVALID;
|
|
}
|
|
|
|
// Notify the character component that something's changed
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (characterComponent != nullptr) {
|
|
characterComponent->HandleZoneStatisticsUpdate(zone, statisticsName, value);
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleUpdatePlayerStatistic(RakNet::BitStream& inStream, Entity* entity) {
|
|
int32_t updateID;
|
|
inStream.Read<int32_t>(updateID);
|
|
|
|
int64_t updateValue;
|
|
if (inStream.ReadBit()) {
|
|
inStream.Read<int64_t>(updateValue);
|
|
} else {
|
|
updateValue = 1;
|
|
}
|
|
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (characterComponent != nullptr) {
|
|
characterComponent->UpdatePlayerStatistic(static_cast<StatisticID>(updateID), static_cast<uint64_t>(std::max(updateValue, static_cast<int64_t>(0))));
|
|
}
|
|
}
|
|
|
|
void GameMessages::HandleDeactivateBubbleBuff(RakNet::BitStream& inStream, Entity* entity) {
|
|
auto controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
|
|
if (controllablePhysicsComponent) controllablePhysicsComponent->DeactivateBubbleBuff();
|
|
}
|
|
|
|
void GameMessages::HandleActivateBubbleBuff(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool specialAnimations;
|
|
if (!inStream.Read(specialAnimations)) return;
|
|
|
|
std::u16string type = GeneralUtils::ReadWString(inStream);
|
|
auto bubbleType = eBubbleType::DEFAULT;
|
|
if (type == u"skunk") bubbleType = eBubbleType::SKUNK;
|
|
else if (type == u"energy") bubbleType = eBubbleType::ENERGY;
|
|
|
|
auto controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
|
|
if (controllablePhysicsComponent) controllablePhysicsComponent->ActivateBubbleBuff(bubbleType, specialAnimations);
|
|
}
|
|
|
|
void GameMessages::SendActivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::ACTIVATE_BUBBLE_BUFF_FROM_SERVER);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::SendDeactivateBubbleBuffFromServer(LWOOBJID objectId, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::DEACTIVATE_BUBBLE_BUFF_FROM_SERVER);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleZoneSummaryDismissed(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID player_id;
|
|
inStream.Read<LWOOBJID>(player_id);
|
|
auto target = Game::entityManager->GetEntity(player_id);
|
|
entity->TriggerEvent(eTriggerEventType::ZONE_SUMMARY_DISMISSED, target);
|
|
};
|
|
|
|
void GameMessages::SendSetNamebillboardState(const SystemAddress& sysAddr, LWOOBJID objectId) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SET_NAME_BILLBOARD_STATE);
|
|
|
|
// Technically these bits would be written, however the client does not
|
|
// contain a deserialize method to actually deserialize, so we are leaving it out.
|
|
// As such this GM only turns the billboard off.
|
|
|
|
// bitStream.Write(overrideDefault);
|
|
// bitStream.Write(state);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
|
|
else SEND_PACKET
|
|
}
|
|
|
|
void GameMessages::SendShowBillboardInteractIcon(const SystemAddress& sysAddr, LWOOBJID objectId) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(objectId);
|
|
bitStream.Write(MessageType::Game::SHOW_BILLBOARD_INTERACT_ICON);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
|
|
else SEND_PACKET
|
|
}
|
|
|
|
void GameMessages::HandleRequestActivityExit(RakNet::BitStream& inStream, Entity* entity) {
|
|
bool canceled = false;
|
|
inStream.Read(canceled);
|
|
if (!canceled) return;
|
|
|
|
LWOOBJID player_id = LWOOBJID_EMPTY;
|
|
inStream.Read(player_id);
|
|
auto player = Game::entityManager->GetEntity(player_id);
|
|
if (!entity || !player) return;
|
|
entity->RequestActivityExit(entity, player_id, canceled);
|
|
}
|
|
|
|
void GameMessages::HandleAddDonationItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
uint32_t count = 1;
|
|
bool hasCount = false;
|
|
inStream.Read(hasCount);
|
|
if (hasCount) inStream.Read(count);
|
|
LWOOBJID itemId = LWOOBJID_EMPTY;
|
|
inStream.Read(itemId);
|
|
if (!itemId) return;
|
|
|
|
auto* donationVendorComponent = entity->GetComponent<DonationVendorComponent>();
|
|
if (!donationVendorComponent) return;
|
|
if (donationVendorComponent->GetActivityID() == 0) {
|
|
LOG("WARNING: Trying to dontate to a vendor with no activity");
|
|
return;
|
|
}
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!player) return;
|
|
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
|
if (!characterComponent) return;
|
|
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
|
if (!inventoryComponent) return;
|
|
Item* item = inventoryComponent->FindItemById(itemId);
|
|
if (!item) return;
|
|
if (item->GetCount() < count) return;
|
|
characterComponent->SetCurrentInteracting(entity->GetObjectID());
|
|
inventoryComponent->MoveItemToInventory(item, eInventoryType::DONATION, count, true, false, true);
|
|
}
|
|
|
|
void GameMessages::HandleRemoveDonationItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
bool confirmed = false;
|
|
inStream.Read(confirmed);
|
|
uint32_t count = 1;
|
|
bool hasCount = false;
|
|
inStream.Read(hasCount);
|
|
if (hasCount) inStream.Read(count);
|
|
LWOOBJID itemId = LWOOBJID_EMPTY;
|
|
inStream.Read(itemId);
|
|
if (!itemId) return;
|
|
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!player) return;
|
|
|
|
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
|
if (!inventoryComponent) return;
|
|
|
|
Item* item = inventoryComponent->FindItemById(itemId);
|
|
if (!item) return;
|
|
if (item->GetCount() < count) return;
|
|
inventoryComponent->MoveItemToInventory(item, eInventoryType::BRICKS, count, true, false, true);
|
|
}
|
|
|
|
void GameMessages::HandleConfirmDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity) {
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
if (!inventoryComponent) return;
|
|
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
|
if (!missionComponent) return;
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (!characterComponent || !characterComponent->GetCurrentInteracting()) return;
|
|
auto* donationEntity = Game::entityManager->GetEntity(characterComponent->GetCurrentInteracting());
|
|
if (!donationEntity) return;
|
|
auto* donationVendorComponent = donationEntity->GetComponent<DonationVendorComponent>();
|
|
if (!donationVendorComponent) return;
|
|
if (donationVendorComponent->GetActivityID() == 0) {
|
|
LOG("WARNING: Trying to dontate to a vendor with no activity");
|
|
return;
|
|
}
|
|
auto* inventory = inventoryComponent->GetInventory(eInventoryType::DONATION);
|
|
if (!inventory) return;
|
|
auto items = inventory->GetItems();
|
|
if (!items.empty()) {
|
|
uint32_t count = 0;
|
|
for (auto& [itemID, item] : items) {
|
|
count += item->GetCount();
|
|
item->RemoveFromInventory();
|
|
}
|
|
missionComponent->Progress(eMissionTaskType::DONATION, 0, LWOOBJID_EMPTY, "", count);
|
|
LeaderboardManager::SaveScore(entity->GetObjectID(), donationVendorComponent->GetActivityID(), count);
|
|
donationVendorComponent->SubmitDonation(count);
|
|
Game::entityManager->SerializeEntity(donationEntity);
|
|
}
|
|
characterComponent->SetCurrentInteracting(LWOOBJID_EMPTY);
|
|
}
|
|
|
|
void GameMessages::HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity) {
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
if (!inventoryComponent) return;
|
|
auto* inventory = inventoryComponent->GetInventory(eInventoryType::DONATION);
|
|
if (!inventory) return;
|
|
auto items = inventory->GetItems();
|
|
for (auto& [itemID, item] : items) {
|
|
inventoryComponent->MoveItemToInventory(item, eInventoryType::BRICKS, item->GetCount(), false, false, true);
|
|
}
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (!characterComponent) return;
|
|
characterComponent->SetCurrentInteracting(LWOOBJID_EMPTY);
|
|
}
|
|
|
|
void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string text) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::SLASH_COMMAND_TEXT_FEEDBACK);
|
|
bitStream.Write<uint32_t>(text.size());
|
|
bitStream.Write(text);
|
|
auto sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void GameMessages::HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
std::string action;
|
|
std::u16string groupName;
|
|
InventoryComponent::GroupUpdate groupUpdate;
|
|
bool locked{}; // All groups are locked by default
|
|
|
|
uint32_t size{};
|
|
if (!inStream.Read(size)) return;
|
|
action.resize(size);
|
|
if (!inStream.Read(action.data(), size)) return;
|
|
|
|
if (!inStream.Read(size)) return;
|
|
groupUpdate.groupId.resize(size);
|
|
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
|
|
|
|
if (!inStream.Read(size)) return;
|
|
groupName.resize(size);
|
|
if (!inStream.Read(reinterpret_cast<char*>(groupName.data()), size * 2)) return;
|
|
|
|
if (!inStream.Read(groupUpdate.inventory)) return;
|
|
if (!inStream.Read(locked)) return;
|
|
|
|
groupUpdate.groupName = GeneralUtils::UTF16ToWTF8(groupName);
|
|
|
|
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD;
|
|
else if (action == "MODIFY") groupUpdate.command = InventoryComponent::GroupUpdateCommand::MODIFY;
|
|
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE;
|
|
else {
|
|
LOG("Invalid action %s", action.c_str());
|
|
return;
|
|
}
|
|
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
|
|
}
|
|
|
|
void GameMessages::HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
std::string action;
|
|
InventoryComponent::GroupUpdate groupUpdate;
|
|
|
|
uint32_t size{};
|
|
if (!inStream.Read(size)) return;
|
|
action.resize(size);
|
|
if (!inStream.Read(action.data(), size)) return;
|
|
|
|
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD_LOT;
|
|
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE_LOT;
|
|
else {
|
|
LOG("Invalid action %s", action.c_str());
|
|
return;
|
|
}
|
|
|
|
if (!inStream.Read(size)) return;
|
|
groupUpdate.groupId.resize(size);
|
|
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
|
|
|
|
if (!inStream.Read(groupUpdate.inventory)) return;
|
|
if (!inStream.Read(groupUpdate.lot)) return;
|
|
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
|
|
}
|
|
|
|
void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::FORCE_CAMERA_TARGET_CYCLE);
|
|
bitStream.Write(bForceCycling);
|
|
bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES);
|
|
if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode);
|
|
bitStream.Write(optionalTargetID);
|
|
|
|
auto sysAddr = entity->GetSystemAddress();
|
|
SEND_PACKET;
|
|
}
|