mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-03 11:23:43 +00:00
Converts the combat messages to NetGameMsgs in CombatMessages.{h,cpp}:
Die, Resurrect, SetResurrectRestoreValues, SetPlayerAllowedRespawn
(SendToClient, as before), Knockback, SetStunned, SetStunImmunity,
SetStatusImmunity, AddBuff, RemoveBuff, AddRunSpeedModifier,
RemoveRunSpeedModifier and DeactivateBubbleBuffFromServer, and the
received RequestDie, RequestSmashPlayer, RequestResurrect, Resurrect,
ActivateBubbleBuff and DeactivateBubbleBuff. Smash and UnSmash move
here from GameMessages.h (Smash's ghostCapacity is ghostOpacity, the
client's name) and gain a Deserialize.
The callers (DestroyableComponent, BuffComponent,
ControllablePhysicsComponent, QuickBuildComponent, RacingControl,
RailActivator and the scripts) build the structs. SendResurrect's
respawn timer moves to DestroyableComponent::Resurrect. The received
messages are registered in GameMessageHandler's map and their switch
cases are deleted, along with the unused HandleRequestDie overload and
the Send* functions nothing called (SendSmash, SendUnSmash, the run
speed modifiers, SendActivateBubbleBuffFromServer). Handler logic is
unchanged.
No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions over an input grid
(every optional field, bool patterns, empty and long strings), received
messages compared with the old handlers' read sequences and truncated
payloads rejected, hand computed golden bytes, round trips, and a
deliberate width mutation made the tests fail.
Known difference from the client, not changed here: DLU writes
SetStatusImmunity's nine flags in its own order; the client reads them
alphabetically (GameMessage::SetStatusImmunity::Serialize @ 00d8f140).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1802 lines
54 KiB
C++
1802 lines
54 KiB
C++
#include "GameMessages.h"
|
|
#include "EffectsMessages.h"
|
|
#include "InventoryMessages.h"
|
|
#include "DashboardNotify.h"
|
|
#include "PlayerReports.h"
|
|
#include "EconomyLedger.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 "ClientPackets.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 "ServiceType.h"
|
|
#include "ePlayerFlag.h"
|
|
|
|
#include <sstream>
|
|
#include <future>
|
|
#include <chrono>
|
|
#include <ranges>
|
|
#include "RakString.h"
|
|
|
|
//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 "PropertyManagementComponent.h"
|
|
#include "PropertyVendorComponent.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"
|
|
#include "Lxfml.h"
|
|
#include "Sd0.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::SendPlayerReady(Entity* entity, const SystemAddress& sysAddr) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::PLAYER_READY);
|
|
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::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::SendStartPathing(Entity* entity) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(entity->GetObjectID());
|
|
bitStream.Write(MessageType::Game::START_PATHING);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, bool bStopAtDesiredWaypoint,
|
|
int iIndex, int iDesiredWaypointIndex, int nextIndex,
|
|
eMovementPlatformState movementState, bool special) {
|
|
CBITSTREAM;
|
|
CMSGHEADER;
|
|
|
|
const auto objID = entity->GetObjectID();
|
|
const auto lot = entity->GetLOT();
|
|
|
|
if (lot == 12341 || lot == 5027 || lot == 5028 || lot == 14335 || lot == 14447 || lot == 14449 || lot == 11306 || lot == 11308 || lot == 9483) {
|
|
iDesiredWaypointIndex = (lot == 11306 || lot == 11308) ? 1 : 0;
|
|
iIndex = lot == 9483 ? 1 : 0;
|
|
nextIndex = lot == 9483 && !special ? 1 : 0;
|
|
bStopAtDesiredWaypoint = true;
|
|
movementState = lot == 9483 && !special ? eMovementPlatformState::Stopped : 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 = QuatUtils::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 != QuatUtils::IDENTITY);
|
|
if (qUnexpectedRotation != QuatUtils::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::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::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::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::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::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::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 != QuatUtils::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::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::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();
|
|
|
|
// The client won't accept more than 10 results even if we wanted it to
|
|
LeaderboardManager::SendLeaderboard(gameID, queryType, weekly, entity->GetObjectID(), entity->GetObjectID(), 10);
|
|
}
|
|
|
|
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::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::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;
|
|
}
|
|
|
|
// Property
|
|
|
|
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::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::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);
|
|
if (sBitStreamLength > MAX_MESSAGE_LENGTH) return;
|
|
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;
|
|
}
|
|
|
|
//Pets:
|
|
|
|
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
|
|
|
|
// 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 != LWOOBJID_EMPTY);
|
|
if (objectID != LWOOBJID_EMPTY) bitStream.Write(objectID);
|
|
|
|
SEND_PACKET_BROADCAST;
|
|
}
|
|
|
|
void GameMessages::UseSkillSet::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(bRemove);
|
|
bitStream.Write(possessedId != LWOOBJID_EMPTY);
|
|
if (possessedId != LWOOBJID_EMPTY) bitStream.Write(possessedId);
|
|
bitStream.Write(setId != -1);
|
|
if (setId != -1) bitStream.Write(setId);
|
|
}
|
|
|
|
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::MarkInventoryItemAsActive markActive;
|
|
markActive.target = entity->GetObjectID();
|
|
markActive.bActive = false;
|
|
markActive.iType = eUnequippableActiveType::MOUNT;
|
|
markActive.itemID = possessorComponent->GetMountItemID();
|
|
markActive.Send(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void GameMessages::HandleAcknowledgePossession(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
Game::entityManager->SerializeEntity(entity);
|
|
bool hasObjectId{};
|
|
inStream.Read(hasObjectId);
|
|
if (hasObjectId) {
|
|
LWOOBJID objectId{};
|
|
inStream.Read(objectId);
|
|
auto* mount = Game::entityManager->GetEntity(objectId);
|
|
if (mount) Game::entityManager->SerializeEntity(mount);
|
|
}
|
|
}
|
|
|
|
//Racing
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
// NT
|
|
|
|
//-----------------------------------------------------------------------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------- 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::HandleParseChatMessage(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
|
std::u16string wsString;
|
|
int iClientState;
|
|
inStream.Read(iClientState);
|
|
|
|
uint32_t wsStringLength;
|
|
inStream.Read(wsStringLength);
|
|
|
|
if (wsStringLength > MAX_MESSAGE_LENGTH) {
|
|
LOG("Max message length reached, capping message.");
|
|
wsStringLength = MAX_MESSAGE_LENGTH;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < wsStringLength; ++i) {
|
|
uint16_t character;
|
|
inStream.Read(character);
|
|
wsString.push_back(character);
|
|
}
|
|
|
|
if (!wsString.empty() && 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);
|
|
if (argsLength > MAX_MESSAGE_LENGTH) return;
|
|
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) {
|
|
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
|
if (characterComponent) {
|
|
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));
|
|
}
|
|
|
|
character->SetZoneID(zoneID);
|
|
character->SetZoneInstance(zoneInstance);
|
|
character->SetZoneClone(zoneClone);
|
|
}
|
|
|
|
ClientPackets::TransferToWorld transfer;
|
|
transfer.serverIP = LUString(serverIP);
|
|
transfer.serverPort = serverPort;
|
|
transfer.mythranShift = mythranShift;
|
|
transfer.Send(sysAddr);
|
|
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::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::PlayFXEffect(entity->GetObjectID(), 7074, u"create", "7074").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
//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::BroadcastTextToChatbox chatboxText;
|
|
chatboxText.target = entity->GetObjectID();
|
|
chatboxText.attrs = attrs;
|
|
chatboxText.wsText = wsText;
|
|
chatboxText.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
|
|
void GameMessages::HandlePickupCurrency(RakNet::BitStream& inStream, Entity* entity) {
|
|
unsigned int currency;
|
|
inStream.Read(currency);
|
|
|
|
if (currency == 0) return;
|
|
|
|
auto* ch = entity->GetCharacter();
|
|
if (ch && entity->PickupCoins(currency)) {
|
|
ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP);
|
|
}
|
|
}
|
|
|
|
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::HandleMatchRequest(RakNet::BitStream& inStream, Entity* entity) {
|
|
LWOOBJID activator;
|
|
uint32_t playerChoicesLen;
|
|
std::string playerChoices;
|
|
int type;
|
|
int value;
|
|
|
|
inStream.Read(activator);
|
|
inStream.Read(playerChoicesLen);
|
|
if (playerChoicesLen > MAX_MESSAGE_LENGTH) return;
|
|
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::HandleReportBug(RakNet::BitStream& inStream, Entity* entity) {
|
|
//Definitely not stolen from autogenerated code, no sir:
|
|
IBugReports::Info reportInfo;
|
|
|
|
//Reading:
|
|
uint32_t messageLength;
|
|
inStream.Read(messageLength);
|
|
|
|
if (messageLength > MAX_MESSAGE_LENGTH) return;
|
|
|
|
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);
|
|
if (clientVersionLength > MAX_MESSAGE_LENGTH) return;
|
|
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);
|
|
if (nOtherPlayerIDLength > MAX_MESSAGE_LENGTH) return;
|
|
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);
|
|
if (selectionLength > MAX_MESSAGE_LENGTH) return;
|
|
for (unsigned int k = 0; k < selectionLength; k++) {
|
|
unsigned char character;
|
|
inStream.Read(character);
|
|
reportInfo.selection.push_back(character);
|
|
}
|
|
|
|
// Report Abuse about another player sends their ID here (and "0" otherwise); that's a player report, not a bug
|
|
if (const auto reportedId = GeneralUtils::TryParse<LWOOBJID>(reportInfo.otherPlayer).value_or(LWOOBJID_EMPTY); reportedId != LWOOBJID_EMPTY) {
|
|
PlayerReports::ReportPlayer(entity, reportedId, reportInfo.body);
|
|
return;
|
|
}
|
|
|
|
Database::Get()->InsertNewBugReport(reportInfo);
|
|
DashboardNotify::Changed("bug_reports");
|
|
}
|
|
|
|
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);
|
|
if (pathNameLength > MAX_MESSAGE_LENGTH) return;
|
|
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))), true);
|
|
}
|
|
}
|
|
|
|
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::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;
|
|
}
|
|
|
|
|
|
namespace GameMessages {
|
|
bool GameMsg::Send() {
|
|
return Game::entityManager->SendMessage(*this);
|
|
}
|
|
|
|
bool GameMsg::Send(const LWOOBJID _target) {
|
|
target = _target;
|
|
return Send();
|
|
}
|
|
|
|
void NetGameMsg::WritePacket(RakNet::BitStream& bitStream) const {
|
|
CMSGHEADER;
|
|
|
|
bitStream.Write(target); // Who this message will be sent to on the (a) client
|
|
bitStream.Write(msgId); // the ID of this message
|
|
|
|
Serialize(bitStream); // write the message data
|
|
}
|
|
|
|
void NetGameMsg::SendToClient(const SystemAddress& sysAddr) const {
|
|
CBITSTREAM;
|
|
WritePacket(bitStream);
|
|
SEND_PACKET;
|
|
}
|
|
|
|
void NetGameMsg::BroadcastExcept(const SystemAddress& excluded) const {
|
|
CBITSTREAM;
|
|
WritePacket(bitStream);
|
|
Game::server->Send(bitStream, excluded, true);
|
|
}
|
|
|
|
void NetGameMsg::Send(const SystemAddress& sysAddr) const {
|
|
CBITSTREAM;
|
|
WritePacket(bitStream);
|
|
|
|
// Send to everyone if someone sent unassigned system address, or to one specific client.
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
|
SEND_PACKET_BROADCAST;
|
|
} else {
|
|
SEND_PACKET;
|
|
}
|
|
}
|
|
|
|
void DisplayTooltip::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(doOrDie);
|
|
bitStream.Write(noRepeat);
|
|
bitStream.Write(noRevive);
|
|
bitStream.Write(isPropertyTooltip);
|
|
bitStream.Write(show);
|
|
bitStream.Write(translate);
|
|
bitStream.Write(time);
|
|
bitStream.Write<int32_t>(id.size());
|
|
bitStream.Write(id);
|
|
|
|
std::string toWrite;
|
|
for (const auto& item : localizeParams | std::views::values) {
|
|
toWrite += item->GetString() + "\n";
|
|
}
|
|
if (!toWrite.empty()) toWrite.pop_back();
|
|
bitStream.Write<int32_t>(toWrite.size());
|
|
bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite));
|
|
if (!toWrite.empty()) bitStream.Write<uint16_t>(0x00); // Null Terminator
|
|
|
|
bitStream.Write<int32_t>(imageName.size());
|
|
bitStream.Write(imageName);
|
|
bitStream.Write<int32_t>(text.size());
|
|
bitStream.Write(text);
|
|
}
|
|
|
|
void UseItemOnClient::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(itemLOT);
|
|
bitStream.Write(itemToUse);
|
|
bitStream.Write(itemType);
|
|
bitStream.Write(playerId);
|
|
bitStream.Write(targetPosition.x);
|
|
bitStream.Write(targetPosition.y);
|
|
bitStream.Write(targetPosition.z);
|
|
}
|
|
|
|
void SetModelToBuild::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(modelLot != -1);
|
|
if (modelLot != -1) bitStream.Write(modelLot);
|
|
}
|
|
|
|
void SpawnModelBricks::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(amount != 0.0f);
|
|
if (amount != 0.0f) bitStream.Write(amount);
|
|
bitStream.Write(position != NiPoint3Constant::ZERO);
|
|
if (position != NiPoint3Constant::ZERO) {
|
|
bitStream.Write(position.x);
|
|
bitStream.Write(position.y);
|
|
bitStream.Write(position.z);
|
|
}
|
|
}
|
|
|
|
bool ShootingGalleryFire::Deserialize(RakNet::BitStream& bitStream) {
|
|
if (!bitStream.Read(target.x)) return false;
|
|
if (!bitStream.Read(target.y)) return false;
|
|
if (!bitStream.Read(target.z)) return false;
|
|
if (!bitStream.Read(rotation.w)) return false;
|
|
if (!bitStream.Read(rotation.x)) return false;
|
|
if (!bitStream.Read(rotation.y)) return false;
|
|
if (!bitStream.Read(rotation.z)) return false;
|
|
return true;
|
|
}
|
|
|
|
void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
entity.OnShootingGalleryFire(*this);
|
|
}
|
|
|
|
bool RequestServerObjectInfo::Deserialize(RakNet::BitStream& bitStream) {
|
|
if (!bitStream.Read(bVerbose)) return false;
|
|
if (!bitStream.Read(clientId)) return false;
|
|
if (!bitStream.Read(targetForReport)) return false;
|
|
return true;
|
|
}
|
|
|
|
void RequestServerObjectInfo::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
auto* handlingEntity = Game::entityManager->GetEntity(targetForReport);
|
|
if (handlingEntity) {
|
|
RequestServerObjectInfoEvent event(*this);
|
|
handlingEntity->HandleMsg(event);
|
|
} else LOG("Failed to find target %llu", targetForReport);
|
|
}
|
|
|
|
bool RequestUse::Deserialize(RakNet::BitStream& stream) {
|
|
if (!stream.Read(bIsMultiInteractUse)) return false;
|
|
if (!stream.Read(multiInteractID)) return false;
|
|
if (!stream.Read(multiInteractType)) return false;
|
|
if (!stream.Read(object)) return false;
|
|
if (!stream.Read(secondary)) return false;
|
|
return true;
|
|
}
|
|
|
|
void RequestUse::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
Entity* interactedObject = Game::entityManager->GetEntity(object);
|
|
|
|
if (interactedObject == nullptr) {
|
|
LOG("Object %llu tried to interact, but doesn't exist!", object);
|
|
|
|
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);
|
|
}
|
|
|
|
RequestUseEvent event(*this);
|
|
interactedObject->HandleMsg(event);
|
|
|
|
//Perform use task if possible:
|
|
auto missionComponent = entity.GetComponent<MissionComponent>();
|
|
|
|
if (!missionComponent) return;
|
|
|
|
missionComponent->Progress(eMissionTaskType::TALK_TO_NPC, interactedObject->GetLOT(), interactedObject->GetObjectID());
|
|
missionComponent->Progress(eMissionTaskType::INTERACT, interactedObject->GetLOT(), interactedObject->GetObjectID());
|
|
}
|
|
|
|
void PlayBehaviorSound::Serialize(RakNet::BitStream& stream) const {
|
|
stream.Write(soundID != -1);
|
|
if (soundID != -1) stream.Write(soundID);
|
|
}
|
|
|
|
void EmotePlayed::Serialize(RakNet::BitStream& stream) const {
|
|
stream.Write(emoteID);
|
|
stream.Write(targetID);
|
|
}
|
|
|
|
void DropClientLoot::Serialize(RakNet::BitStream& stream) const {
|
|
stream.Write(bUsePosition);
|
|
|
|
stream.Write(finalPosition != NiPoint3Constant::ZERO);
|
|
if (finalPosition != NiPoint3Constant::ZERO) stream.Write(finalPosition);
|
|
|
|
stream.Write(currency);
|
|
stream.Write(item);
|
|
stream.Write(lootID);
|
|
stream.Write(ownerID);
|
|
stream.Write(sourceID);
|
|
|
|
stream.Write(spawnPos != NiPoint3Constant::ZERO);
|
|
if (spawnPos != NiPoint3Constant::ZERO) stream.Write(spawnPos);
|
|
}
|
|
|
|
bool PickupItem::Deserialize(RakNet::BitStream& stream) {
|
|
if (!stream.Read(lootID)) return false;
|
|
if (!stream.Read(lootOwnerID)) return false;
|
|
return true;
|
|
}
|
|
|
|
void PickupItem::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
auto* team = TeamManager::Instance()->GetTeam(entity.GetObjectID());
|
|
LOG("Has team %i picking up %llu:%llu", team != nullptr, lootID, lootOwnerID);
|
|
if (team) {
|
|
for (const auto memberId : team->members) {
|
|
PickupItemEvent event(*this);
|
|
event.Send(memberId);
|
|
TeamPickupItem teamPickupMsg{};
|
|
teamPickupMsg.target = lootID;
|
|
teamPickupMsg.lootID = lootID;
|
|
teamPickupMsg.lootOwnerID = lootOwnerID;
|
|
const auto* const memberEntity = Game::entityManager->GetEntity(memberId);
|
|
if (memberEntity) teamPickupMsg.Send(memberEntity->GetSystemAddress());
|
|
}
|
|
} else {
|
|
entity.PickupItem(lootID);
|
|
}
|
|
}
|
|
|
|
void TeamPickupItem::Serialize(RakNet::BitStream& stream) const {
|
|
stream.Write(lootID);
|
|
stream.Write(lootOwnerID);
|
|
}
|
|
|
|
void ToggleGMInvis::Serialize(RakNet::BitStream& stream) const {
|
|
stream.Write(bStateOut);
|
|
}
|
|
}
|