diff --git a/dCommon/PositionUpdate.h b/dCommon/PositionUpdate.h new file mode 100644 index 00000000..1b84e371 --- /dev/null +++ b/dCommon/PositionUpdate.h @@ -0,0 +1,50 @@ +#ifndef __POSITIONUPDATE__H__ +#define __POSITIONUPDATE__H__ + +#include "NiPoint3.h" +#include "NiQuaternion.h" + + +struct RemoteInputInfo { + RemoteInputInfo() { + m_RemoteInputX = 0; + m_RemoteInputY = 0; + m_IsPowersliding = false; + m_IsModified = false; + } + + void operator=(const RemoteInputInfo& other) { + m_RemoteInputX = other.m_RemoteInputX; + m_RemoteInputY = other.m_RemoteInputY; + m_IsPowersliding = other.m_IsPowersliding; + m_IsModified = other.m_IsModified; + } + + bool operator==(const RemoteInputInfo& other) { + return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; + } + + float m_RemoteInputX; + float m_RemoteInputY; + bool m_IsPowersliding; + bool m_IsModified; +}; + +struct LocalSpaceInfo { + LWOOBJID objectId = LWOOBJID_EMPTY; + NiPoint3 position = NiPoint3::ZERO; + NiPoint3 linearVelocity = NiPoint3::ZERO; +}; + +struct PositionUpdate { + NiPoint3 position = NiPoint3::ZERO; + NiQuaternion rotation = NiQuaternion::IDENTITY; + bool onGround = false; + bool onRail = false; + NiPoint3 velocity = NiPoint3::ZERO; + NiPoint3 angularVelocity = NiPoint3::ZERO; + LocalSpaceInfo localSpaceInfo; + RemoteInputInfo remoteInputInfo; +}; + +#endif //!__POSITIONUPDATE__H__ diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 347b4caa..8300de41 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -25,6 +25,7 @@ #include "eMissionTaskType.h" #include "eTriggerEventType.h" #include "eObjectBits.h" +#include "PositionUpdate.h" //Component includes: #include "Component.h" @@ -2056,3 +2057,75 @@ uint8_t Entity::GetCollectibleID() const { auto* collectible = GetComponent(); return collectible ? collectible->GetCollectibleId() : 0; } + +void Entity::ProcessPositionUpdate(PositionUpdate& update) { + if (!IsPlayer()) return; + auto* controllablePhysicsComponent = GetComponent(); + if (!controllablePhysicsComponent) return; + + auto* possessorComponent = GetComponent(); + bool updateChar = true; + + if (possessorComponent) { + auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + + if (possassableEntity) { + auto* possessableComponent = possassableEntity->GetComponent(); + + // While possessing something, only update char if we are attached to the thing we are possessing + updateChar = possessableComponent && possessableComponent->GetPossessionType() == ePossessionType::ATTACHED_VISIBLE; + + auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent(); + if (havokVehiclePhysicsComponent) { + havokVehiclePhysicsComponent->SetPosition(update.position); + havokVehiclePhysicsComponent->SetRotation(update.rotation); + havokVehiclePhysicsComponent->SetIsOnGround(update.onGround); + havokVehiclePhysicsComponent->SetIsOnRail(update.onRail); + havokVehiclePhysicsComponent->SetVelocity(update.velocity); + havokVehiclePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3::ZERO); + havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity); + havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + havokVehiclePhysicsComponent->SetRemoteInputInfo(update.remoteInputInfo); + } else { + // Need to get the mount's controllable physics + auto* possessedControllablePhysicsComponent = possassableEntity->GetComponent(); + if (!possessedControllablePhysicsComponent) return; + possessedControllablePhysicsComponent->SetPosition(update.position); + possessedControllablePhysicsComponent->SetRotation(update.rotation); + possessedControllablePhysicsComponent->SetIsOnGround(update.onGround); + possessedControllablePhysicsComponent->SetIsOnRail(update.onRail); + possessedControllablePhysicsComponent->SetVelocity(update.velocity); + possessedControllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3::ZERO); + possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + } + Game::entityManager->SerializeEntity(possassableEntity); + } + } + + if (!updateChar) { + update.velocity = NiPoint3::ZERO; + update.angularVelocity = NiPoint3::ZERO; + } + + // Handle statistics + auto* characterComponent = GetComponent(); + if (characterComponent) { + characterComponent->TrackPositionUpdate(update.position); + } + + controllablePhysicsComponent->SetPosition(update.position); + controllablePhysicsComponent->SetRotation(update.rotation); + controllablePhysicsComponent->SetIsOnGround(update.onGround); + controllablePhysicsComponent->SetIsOnRail(update.onRail); + controllablePhysicsComponent->SetVelocity(update.velocity); + controllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3::ZERO); + controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + + auto* player = static_cast(this); + player->SetGhostReferencePoint(update.position); + Game::entityManager->QueueGhostUpdate(player->GetObjectID()); + + if (updateChar) Game::entityManager->SerializeEntity(this); +} diff --git a/dGame/Entity.h b/dGame/Entity.h index 83e8dc3f..02fc14d3 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -31,6 +31,7 @@ class Component; class Item; class Character; class EntityCallbackTimer; +class PositionUpdate; enum class eTriggerEventType; enum class eGameMasterLevel : uint8_t; enum class eReplicaComponentType : uint32_t; @@ -296,6 +297,8 @@ public: Entity* GetScheduledKiller() { return m_ScheduleKiller; } + void ProcessPositionUpdate(PositionUpdate& update); + protected: LWOOBJID m_ObjectID; diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 736339a4..2060de89 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -216,7 +216,52 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { chars.push_back(character); } - WorldPackets::SendCharacterList(sysAddr, u); + RakNet::BitStream bitStream; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE); + + std::vector characters = u->GetCharacters(); + bitStream.Write(characters.size()); + bitStream.Write(0); //TODO: Pick the most recent played index. character index in front, just picking 0 + + for (uint32_t i = 0; i < characters.size(); ++i) { + bitStream.Write(characters[i]->GetObjectID()); + bitStream.Write(0); + + bitStream.Write(LUWString(characters[i]->GetName())); + bitStream.Write(LUWString(characters[i]->GetUnapprovedName())); + + bitStream.Write(characters[i]->GetNameRejected()); + bitStream.Write(false); + + bitStream.Write(LUString("", 10)); + + bitStream.Write(characters[i]->GetShirtColor()); + bitStream.Write(characters[i]->GetShirtStyle()); + bitStream.Write(characters[i]->GetPantsColor()); + bitStream.Write(characters[i]->GetHairStyle()); + bitStream.Write(characters[i]->GetHairColor()); + bitStream.Write(characters[i]->GetLeftHand()); + bitStream.Write(characters[i]->GetRightHand()); + bitStream.Write(characters[i]->GetEyebrows()); + bitStream.Write(characters[i]->GetEyes()); + bitStream.Write(characters[i]->GetMouth()); + bitStream.Write(0); + + bitStream.Write(characters[i]->GetZoneID()); + bitStream.Write(characters[i]->GetZoneInstance()); + bitStream.Write(characters[i]->GetZoneClone()); + + bitStream.Write(characters[i]->GetLastLogin()); + + const auto& equippedItems = characters[i]->GetEquippedItems(); + bitStream.Write(equippedItems.size()); + + for (uint32_t j = 0; j < equippedItems.size(); ++j) { + bitStream.Write(equippedItems[j]); + } + } + + SEND_PACKET; } void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) { @@ -322,7 +367,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); - }); + }); } void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) { diff --git a/dGame/dComponents/HavokVehiclePhysicsComponent.h b/dGame/dComponents/HavokVehiclePhysicsComponent.h index 2d04e0ac..ba46f093 100644 --- a/dGame/dComponents/HavokVehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -4,31 +4,7 @@ #include "Entity.h" #include "PhysicsComponent.h" #include "eReplicaComponentType.h" - -struct RemoteInputInfo { - RemoteInputInfo() { - m_RemoteInputX = 0; - m_RemoteInputY = 0; - m_IsPowersliding = false; - m_IsModified = false; - } - - void operator=(const RemoteInputInfo& other) { - m_RemoteInputX = other.m_RemoteInputX; - m_RemoteInputY = other.m_RemoteInputY; - m_IsPowersliding = other.m_IsPowersliding; - m_IsModified = other.m_IsModified; - } - - bool operator==(const RemoteInputInfo& other) { - return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; - } - - float m_RemoteInputX; - float m_RemoteInputY; - bool m_IsPowersliding; - bool m_IsModified; -}; +#include "PositionUpdate.h" /** * Physics component for vehicles. diff --git a/dNet/CMakeLists.txt b/dNet/CMakeLists.txt index b4b77c82..68de8eb1 100644 --- a/dNet/CMakeLists.txt +++ b/dNet/CMakeLists.txt @@ -8,10 +8,5 @@ set(DNET_SOURCES "AuthPackets.cpp" "ZoneInstanceManager.cpp") add_library(dNet STATIC ${DNET_SOURCES}) -target_include_directories(dNet PRIVATE - ${PROJECT_SOURCE_DIR}/dGame/dComponents - ${PROJECT_SOURCE_DIR}/dScripts # transitive through components -) -target_link_libraries(dNet - PUBLIC dCommon dDatabase - INTERFACE dZoneManager) + +target_link_libraries(dNet PUBLIC dCommon) diff --git a/dNet/ClientPackets.cpp b/dNet/ClientPackets.cpp index 47513a66..a6b9f8c6 100644 --- a/dNet/ClientPackets.cpp +++ b/dNet/ClientPackets.cpp @@ -4,422 +4,120 @@ */ #include "ClientPackets.h" -#include "UserManager.h" -#include "User.h" -#include "Character.h" -#include "EntityManager.h" -#include "Entity.h" -#include "ControllablePhysicsComponent.h" -#include "Game.h" -#include "Logger.h" -#include "WorldPackets.h" -#include "NiPoint3.h" -#include "NiQuaternion.h" #include "dCommonVars.h" -#include "BitStream.h" -#include "dChatFilter.h" -#include "WorldPackets.h" -#include "ChatPackets.h" -#include "dServer.h" -#include "GameMessages.h" -#include "dZoneManager.h" -#include "Player.h" -#include "Zone.h" -#include "PossessorComponent.h" -#include "PossessableComponent.h" -#include "HavokVehiclePhysicsComponent.h" -#include "dConfig.h" -#include "CharacterComponent.h" -#include "Database.h" -#include "eGameMasterLevel.h" -#include "eReplicaComponentType.h" -#include "CheatDetection.h" -#include "Amf3.h" - -void ClientPackets::HandleChatMessage(const SystemAddress& sysAddr, Packet* packet) { - User* user = UserManager::Instance()->GetUser(sysAddr); - if (!user) { - LOG("Unable to get user to parse chat message"); - return; - } - - if (user->GetIsMuted()) { - user->GetLastUsedChar()->SendMuteNotice(); - return; - } +#include "PositionUpdate.h" +ChatMessage ClientPackets::HandleChatMessage(Packet* packet) { CINSTREAM_SKIP_HEADER; - char chatChannel; - uint16_t unknown; + ChatMessage message; uint32_t messageLength; - std::u16string message; - inStream.Read(chatChannel); - inStream.Read(unknown); + inStream.Read(message.chatChannel); + inStream.Read(message.unknown); inStream.Read(messageLength); for (uint32_t i = 0; i < (messageLength - 1); ++i) { uint16_t character; inStream.Read(character); - message.push_back(character); + message.message.push_back(character); } - std::string playerName = user->GetLastUsedChar()->GetName(); - bool isMythran = user->GetLastUsedChar()->GetGMLevel() > eGameMasterLevel::CIVILIAN; - bool isOk = Game::chatFilter->IsSentenceOkay(GeneralUtils::UTF16ToWTF8(message), user->GetLastUsedChar()->GetGMLevel()).empty(); - LOG_DEBUG("Msg: %s was approved previously? %i", GeneralUtils::UTF16ToWTF8(message).c_str(), user->GetLastChatMessageApproved()); - if (!isOk) { - // Add a limit to the string converted by general utils because it is a user received string and may be a bad actor. - CheatDetection::ReportCheat( - user, - sysAddr, - "Player %s attempted to bypass chat filter with message: %s", - playerName.c_str(), - GeneralUtils::UTF16ToWTF8(message, 512).c_str()); - } - if (!isOk && !isMythran) return; - - std::string sMessage = GeneralUtils::UTF16ToWTF8(message); - LOG("%s: %s", playerName.c_str(), sMessage.c_str()); - ChatPackets::SendChatMessage(sysAddr, chatChannel, playerName, user->GetLoggedInChar(), isMythran, message); + return message; } -void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet) { - User* user = UserManager::Instance()->GetUser(sysAddr); - if (!user) { - LOG("Unable to get user to parse position update"); - return; - } - +PositionUpdate ClientPackets::HandleClientPositionUpdate(Packet* packet) { + PositionUpdate update; CINSTREAM_SKIP_HEADER; - Entity* entity = Game::entityManager->GetEntity(user->GetLastUsedChar()->GetObjectID()); - if (!entity) return; + inStream.Read(update.position.x); + inStream.Read(update.position.y); + inStream.Read(update.position.z); - ControllablePhysicsComponent* comp = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (!comp) return; + inStream.Read(update.rotation.x); + inStream.Read(update.rotation.y); + inStream.Read(update.rotation.z); + inStream.Read(update.rotation.w); - /* - //If we didn't move, this will match and stop our velocity - if (packet->length == 37) { - NiPoint3 zeroVel(0.0f, 0.0f, 0.0f); - comp->SetVelocity(zeroVel); - comp->SetAngularVelocity(zeroVel); - comp->SetIsOnGround(true); //probably8 - Game::entityManager->SerializeEntity(entity); - return; - } - */ - - auto* possessorComponent = entity->GetComponent(); - - NiPoint3 position; - inStream.Read(position.x); - inStream.Read(position.y); - inStream.Read(position.z); - - NiQuaternion rotation; - inStream.Read(rotation.x); - inStream.Read(rotation.y); - inStream.Read(rotation.z); - inStream.Read(rotation.w); - - bool onGround = false; - bool onRail = false; - inStream.Read(onGround); - inStream.Read(onRail); + inStream.Read(update.onGround); + inStream.Read(update.onRail); bool velocityFlag = false; inStream.Read(velocityFlag); - NiPoint3 velocity{}; if (velocityFlag) { - inStream.Read(velocity.x); - inStream.Read(velocity.y); - inStream.Read(velocity.z); + inStream.Read(update.velocity.x); + inStream.Read(update.velocity.y); + inStream.Read(update.velocity.z); } bool angVelocityFlag = false; inStream.Read(angVelocityFlag); - NiPoint3 angVelocity{}; if (angVelocityFlag) { - inStream.Read(angVelocity.x); - inStream.Read(angVelocity.y); - inStream.Read(angVelocity.z); + inStream.Read(update.angularVelocity.x); + inStream.Read(update.angularVelocity.y); + inStream.Read(update.angularVelocity.z); } // TODO figure out how to use these. Ignoring for now, but reading in if they exist. bool hasLocalSpaceInfo{}; - LWOOBJID objectId{}; - NiPoint3 localSpacePosition{}; - bool hasLinearVelocity{}; - NiPoint3 linearVelocity{}; if (inStream.Read(hasLocalSpaceInfo) && hasLocalSpaceInfo) { - inStream.Read(objectId); - inStream.Read(localSpacePosition.x); - inStream.Read(localSpacePosition.y); - inStream.Read(localSpacePosition.z); + inStream.Read(update.localSpaceInfo.objectId); + inStream.Read(update.localSpaceInfo.position.x); + inStream.Read(update.localSpaceInfo.position.y); + inStream.Read(update.localSpaceInfo.position.z); + bool hasLinearVelocity = false; if (inStream.Read(hasLinearVelocity) && hasLinearVelocity) { - inStream.Read(linearVelocity.x); - inStream.Read(linearVelocity.y); - inStream.Read(linearVelocity.z); + inStream.Read(update.localSpaceInfo.linearVelocity.x); + inStream.Read(update.localSpaceInfo.linearVelocity.y); + inStream.Read(update.localSpaceInfo.linearVelocity.z); } } + bool hasRemoteInputInfo{}; - RemoteInputInfo remoteInput{}; - if (inStream.Read(hasRemoteInputInfo) && hasRemoteInputInfo) { - inStream.Read(remoteInput.m_RemoteInputX); - inStream.Read(remoteInput.m_RemoteInputY); - inStream.Read(remoteInput.m_IsPowersliding); - inStream.Read(remoteInput.m_IsModified); + inStream.Read(update.remoteInputInfo.m_RemoteInputX); + inStream.Read(update.remoteInputInfo.m_RemoteInputY); + inStream.Read(update.remoteInputInfo.m_IsPowersliding); + inStream.Read(update.remoteInputInfo.m_IsModified); } - bool updateChar = true; - - if (possessorComponent != nullptr) { - auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); - - if (possassableEntity != nullptr) { - auto* possessableComponent = possassableEntity->GetComponent(); - if (possessableComponent) { - // While possessing something, only update char if we are attached to the thing we are possessing - if (possessableComponent->GetPossessionType() != ePossessionType::ATTACHED_VISIBLE) updateChar = false; - } - - auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent(); - if (havokVehiclePhysicsComponent != nullptr) { - havokVehiclePhysicsComponent->SetPosition(position); - havokVehiclePhysicsComponent->SetRotation(rotation); - havokVehiclePhysicsComponent->SetIsOnGround(onGround); - havokVehiclePhysicsComponent->SetIsOnRail(onRail); - havokVehiclePhysicsComponent->SetVelocity(velocity); - havokVehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); - havokVehiclePhysicsComponent->SetAngularVelocity(angVelocity); - havokVehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); - havokVehiclePhysicsComponent->SetRemoteInputInfo(remoteInput); - } else { - // Need to get the mount's controllable physics - auto* controllablePhysicsComponent = possassableEntity->GetComponent(); - if (!controllablePhysicsComponent) return; - controllablePhysicsComponent->SetPosition(position); - controllablePhysicsComponent->SetRotation(rotation); - controllablePhysicsComponent->SetIsOnGround(onGround); - controllablePhysicsComponent->SetIsOnRail(onRail); - controllablePhysicsComponent->SetVelocity(velocity); - controllablePhysicsComponent->SetDirtyVelocity(velocityFlag); - controllablePhysicsComponent->SetAngularVelocity(angVelocity); - controllablePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); - } - Game::entityManager->SerializeEntity(possassableEntity); - } - } - - if (!updateChar) { - velocity = NiPoint3::ZERO; - angVelocity = NiPoint3::ZERO; - } - - - - // Handle statistics - auto* characterComponent = entity->GetComponent(); - if (characterComponent != nullptr) { - characterComponent->TrackPositionUpdate(position); - } - - comp->SetPosition(position); - comp->SetRotation(rotation); - comp->SetIsOnGround(onGround); - comp->SetIsOnRail(onRail); - comp->SetVelocity(velocity); - comp->SetDirtyVelocity(velocityFlag); - comp->SetAngularVelocity(angVelocity); - comp->SetDirtyAngularVelocity(angVelocityFlag); - - auto* player = static_cast(entity); - player->SetGhostReferencePoint(position); - Game::entityManager->QueueGhostUpdate(player->GetObjectID()); - - if (updateChar) Game::entityManager->SerializeEntity(entity); - - //TODO: add moving platform stuffs - /*bool movingPlatformFlag; - inStream.Read(movingPlatformFlag); - if (movingPlatformFlag) { - LWOOBJID objectID; - NiPoint3 niData2; - - inStream.Read(objectID); - inStream.Read(niData2.x); - inStream.Read(niData2.y); - inStream.Read(niData2.z); - - - - bool niData3Flag; - inStream.Read(niData3Flag); - if (niData3Flag) { - NiPoint3 niData3; - inStream.Read(niData3.x); - inStream.Read(niData3.y); - inStream.Read(niData3.z); - - controllablePhysics->GetLocationData()->GetMovingPlatformData()->SetData3(niData3); - } - }*/ - - /* - for (int i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) - { - const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i); - - if (entity->GetSystemAddress() == player) - { - continue; - } - - Game::entityManager->SerializeEntity(entity, player); - } - */ + return update; } -void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet) { - User* user = UserManager::Instance()->GetUser(sysAddr); - if (!user) { - LOG("Unable to get user to parse chat moderation request"); - return; - } +ChatModerationRequest ClientPackets::HandleChatModerationRequest(Packet* packet) { + CINSTREAM_SKIP_HEADER; + + ChatModerationRequest request; - auto* entity = Player::GetPlayer(sysAddr); - - if (entity == nullptr) { - LOG("Unable to get player to parse chat moderation request"); - return; - } - - // Check if the player has restricted chat access - auto* character = entity->GetCharacter(); - - if (character->HasPermission(ePermissionMap::RestrictedChatAccess)) { - // Send a message to the player - ChatPackets::SendSystemMessage( - sysAddr, - u"This character has restricted chat access." - ); - - return; - } - - RakNet::BitStream stream(packet->data, packet->length, false); - - uint64_t header; - stream.Read(header); - - // Data - uint8_t chatLevel; - uint8_t requestID; - uint16_t messageLength; - - std::string receiver = ""; - std::string message = ""; - - stream.Read(chatLevel); - stream.Read(requestID); + inStream.Read(request.chatLevel); + inStream.Read(request.requestID); for (uint32_t i = 0; i < 42; ++i) { uint16_t character; - stream.Read(character); - receiver.push_back(static_cast(character)); + inStream.Read(character); + request.receiver.push_back(static_cast(character)); } - if (!receiver.empty()) { - if (std::string(receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are - receiver = std::string(receiver.c_str() + 4, receiver.size() - 4); + if (!request.receiver.empty()) { + if (std::string(request.receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are + request.receiver = std::string(request.receiver.c_str() + 4, request.receiver.size() - 4); } } - stream.Read(messageLength); + uint16_t messageLength; + inStream.Read(messageLength); for (uint32_t i = 0; i < messageLength; ++i) { uint16_t character; - stream.Read(character); - message.push_back(static_cast(character)); + inStream.Read(character); + request.message.push_back(static_cast(character)); } - bool isBestFriend = false; - - if (chatLevel == 1) { - // Private chat - LWOOBJID idOfReceiver = LWOOBJID_EMPTY; - - { - auto characterIdFetch = Database::Get()->GetCharacterInfo(receiver); - - if (characterIdFetch) { - idOfReceiver = characterIdFetch->id; - } - } - const auto& bffMap = user->GetIsBestFriendMap(); - if (bffMap.find(receiver) == bffMap.end() && idOfReceiver != LWOOBJID_EMPTY) { - auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); - - if (bffInfo) { - isBestFriend = bffInfo->bestFriendStatus == 3; - } - - if (isBestFriend) { - user->UpdateBestFriendValue(receiver, true); - } - } else if (bffMap.find(receiver) != bffMap.end()) { - isBestFriend = true; - } - } - - std::vector> segments = Game::chatFilter->IsSentenceOkay(message, entity->GetGMLevel(), !(isBestFriend && chatLevel == 1)); - - bool bAllClean = segments.empty(); - - if (user->GetIsMuted()) { - bAllClean = false; - } - - user->SetLastChatMessageApproved(bAllClean); - WorldPackets::SendChatModerationResponse(sysAddr, bAllClean, requestID, receiver, segments); + return request; } -void ClientPackets::SendTop5HelpIssues(Packet* packet) { - auto* user = UserManager::Instance()->GetUser(packet->systemAddress); - if (!user) return; - auto* character = user->GetLastUsedChar(); - if (!character) return; - auto * entity = character->GetEntity(); - if (!entity) return; - +int32_t ClientPackets::SendTop5HelpIssues(Packet* packet) { CINSTREAM_SKIP_HEADER; int32_t language = 0; inStream.Read(language); - - // TODO: Handle different languages in a nice way - // 0: en_US - // 1: pl_US - // 2: de_DE - // 3: en_GB - - AMFArrayValue data; - // Summaries - data.Insert("Summary0", Game::config->GetValue("help_0_summary")); - data.Insert("Summary1", Game::config->GetValue("help_1_summary")); - data.Insert("Summary2", Game::config->GetValue("help_2_summary")); - data.Insert("Summary3", Game::config->GetValue("help_3_summary")); - data.Insert("Summary4", Game::config->GetValue("help_4_summary")); - - // Descriptions - data.Insert("Description0", Game::config->GetValue("help_0_description")); - data.Insert("Description1", Game::config->GetValue("help_1_description")); - data.Insert("Description2", Game::config->GetValue("help_2_description")); - data.Insert("Description3", Game::config->GetValue("help_3_description")); - data.Insert("Description4", Game::config->GetValue("help_4_description")); - - GameMessages::SendUIMessageServerToSingleClient(entity, packet->systemAddress, "UIHelpTop5", data); - + return language; } diff --git a/dNet/ClientPackets.h b/dNet/ClientPackets.h index 5b9fd76d..a7d2941b 100644 --- a/dNet/ClientPackets.h +++ b/dNet/ClientPackets.h @@ -6,13 +6,31 @@ #ifndef CLIENTPACKETS_H #define CLIENTPACKETS_H -#include "RakNetTypes.h" +#include +#include + +class PositionUpdate; + +struct Packet; + +struct ChatMessage { + uint8_t chatChannel = 0; + uint16_t unknown = 0; + std::u16string message; +}; + +struct ChatModerationRequest { + uint8_t chatLevel = 0; + uint8_t requestID = 0; + std::string receiver; + std::string message; +}; namespace ClientPackets { - void HandleChatMessage(const SystemAddress& sysAddr, Packet* packet); - void HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet); - void HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet); - void SendTop5HelpIssues(Packet* packet); + ChatMessage HandleChatMessage(Packet* packet); + PositionUpdate HandleClientPositionUpdate(Packet* packet); + ChatModerationRequest HandleChatModerationRequest(Packet* packet); + int32_t SendTop5HelpIssues(Packet* packet); }; #endif // CLIENTPACKETS_H diff --git a/dNet/WorldPackets.cpp b/dNet/WorldPackets.cpp index 1ee98c83..6ea0b350 100644 --- a/dNet/WorldPackets.cpp +++ b/dNet/WorldPackets.cpp @@ -1,26 +1,22 @@ -#include "dCommonVars.h" #include "WorldPackets.h" +#include "dCommonVars.h" #include "BitStream.h" #include "PacketUtils.h" #include "GeneralUtils.h" -#include "User.h" -#include "Character.h" #include "Logger.h" -#include #include "Game.h" #include "LDFFormat.h" #include "dServer.h" -#include "dZoneManager.h" -#include "CharacterComponent.h" #include "ZCompression.h" #include "eConnectionType.h" #include "BitStreamUtils.h" -void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum) { +#include + +void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::LOAD_STATIC_ZONE); - auto zone = Game::zoneManager->GetZone()->GetZoneID(); bitStream.Write(zone.GetMapID()); bitStream.Write(zone.GetInstanceID()); //bitStream.Write(zone.GetCloneID()); @@ -38,57 +34,6 @@ void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, flo SEND_PACKET; } -void WorldPackets::SendCharacterList(const SystemAddress& sysAddr, User* user) { - if (!user) return; - - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE); - - std::vector characters = user->GetCharacters(); - bitStream.Write(characters.size()); - bitStream.Write(0); //character index in front, just picking 0 - - for (uint32_t i = 0; i < characters.size(); ++i) { - bitStream.Write(characters[i]->GetObjectID()); - bitStream.Write(0); - - bitStream.Write(LUWString(characters[i]->GetName())); - bitStream.Write(LUWString(characters[i]->GetUnapprovedName())); - - bitStream.Write(characters[i]->GetNameRejected()); - bitStream.Write(false); - - bitStream.Write(LUString("", 10)); - - bitStream.Write(characters[i]->GetShirtColor()); - bitStream.Write(characters[i]->GetShirtStyle()); - bitStream.Write(characters[i]->GetPantsColor()); - bitStream.Write(characters[i]->GetHairStyle()); - bitStream.Write(characters[i]->GetHairColor()); - bitStream.Write(characters[i]->GetLeftHand()); - bitStream.Write(characters[i]->GetRightHand()); - bitStream.Write(characters[i]->GetEyebrows()); - bitStream.Write(characters[i]->GetEyes()); - bitStream.Write(characters[i]->GetMouth()); - bitStream.Write(0); - - bitStream.Write(characters[i]->GetZoneID()); - bitStream.Write(characters[i]->GetZoneInstance()); - bitStream.Write(characters[i]->GetZoneClone()); - - bitStream.Write(characters[i]->GetLastLogin()); - - const auto& equippedItems = characters[i]->GetEquippedItems(); - bitStream.Write(equippedItems.size()); - - for (uint32_t j = 0; j < equippedItems.size(); ++j) { - bitStream.Write(equippedItems[j]); - } - } - - SEND_PACKET; -} - void WorldPackets::SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_CREATE_RESPONSE); @@ -128,26 +73,20 @@ void WorldPackets::SendServerState(const SystemAddress& sysAddr) { SEND_PACKET; } -void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) { +void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CREATE_CHARACTER); RakNet::BitStream data; data.Write(7); //LDF key count - auto character = entity->GetComponent(); - if (!character) { - LOG("Entity is not a character?? what??"); - return; - } - - LDFData* objid = new LDFData(u"objid", entity->GetObjectID()); - LDFData* lot = new LDFData(u"template", 1); - LDFData* xmlConfigData = new LDFData(u"xmlData", xmlData); - LDFData* name = new LDFData(u"name", username); - LDFData* gmlevel = new LDFData(u"gmlevel", static_cast(gm)); - LDFData* chatmode = new LDFData(u"chatmode", static_cast(gm)); - LDFData* reputation = new LDFData(u"reputation", character->GetReputation()); + std::unique_ptr> objid(new LDFData(u"objid", player)); + std::unique_ptr> lot(new LDFData(u"template", 1)); + std::unique_ptr> xmlConfigData(new LDFData(u"xmlData", xmlData)); + std::unique_ptr> name(new LDFData(u"name", username)); + std::unique_ptr> gmlevel(new LDFData(u"gmlevel", static_cast(gm))); + std::unique_ptr> chatmode(new LDFData(u"chatmode", static_cast(gm))); + std::unique_ptr> reputationLdf(new LDFData(u"reputation", reputation)); objid->WriteToPacket(&data); lot->WriteToPacket(&data); @@ -155,15 +94,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* ent gmlevel->WriteToPacket(&data); chatmode->WriteToPacket(&data); xmlConfigData->WriteToPacket(&data); - reputation->WriteToPacket(&data); - - delete objid; - delete lot; - delete xmlConfigData; - delete gmlevel; - delete chatmode; - delete name; - delete reputation; + reputationLdf->WriteToPacket(&data); //Compress the data before sending: const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed()); @@ -187,14 +118,12 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* ent * an assertion is done to prevent bad data from being saved or sent. */ #pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'. - for (size_t i = 0; i < size; i++) - bitStream.Write(compressedData[i]); + bitStream.WriteAlignedBytes(compressedData, size); #pragma warning(default:6385) - // PacketUtils::SavePacket("chardata.bin", (const char*)bitStream.GetData(), static_cast(bitStream.GetNumberOfBytesUsed())); SEND_PACKET; delete[] compressedData; - LOG("Sent CreateCharacter for ID: %llu", entity->GetObjectID()); + LOG("Sent CreateCharacter for ID: %llu", player); } void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems) { diff --git a/dNet/WorldPackets.h b/dNet/WorldPackets.h index ea8186c7..0d5de079 100644 --- a/dNet/WorldPackets.h +++ b/dNet/WorldPackets.h @@ -2,9 +2,8 @@ #define WORLDPACKETS_H #include "dCommonVars.h" +#include #include -#include -#include "Entity.h" class User; struct SystemAddress; @@ -13,14 +12,13 @@ enum class eCharacterCreationResponse : uint8_t; enum class eRenameResponse : uint8_t; namespace WorldPackets { - void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum); - void SendCharacterList(const SystemAddress& sysAddr, User* user); + void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone); void SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response); void SendCharacterRenameResponse(const SystemAddress& sysAddr, eRenameResponse response); void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response); void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); void SendServerState(const SystemAddress& sysAddr); - void SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); + void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); } diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index d65596b6..e6a0973f 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -78,6 +78,7 @@ #include "eGameMasterLevel.h" #include "StringifiedEnum.h" #include "Server.h" +#include "PositionUpdate.h" namespace Game { Logger* logger = nullptr; @@ -713,7 +714,7 @@ void HandleMasterPacket(Packet* packet) { z = pos.z; } - WorldPackets::SendLoadStaticZone(it->second.sysAddr, x, y, z, zone->GetChecksum()); + WorldPackets::SendLoadStaticZone(it->second.sysAddr, x, y, z, zone->GetChecksum(), Game::zoneManager->GetZoneID()); } if (Game::server->GetZoneID() == 0) { @@ -1005,7 +1006,10 @@ void HandlePacket(Packet* packet) { info.lot = 1; Entity* player = Game::entityManager->CreateEntity(info, UserManager::Instance()->GetUser(packet->systemAddress)); - WorldPackets::SendCreateCharacter(packet->systemAddress, player, c->GetXMLData(), username, c->GetGMLevel()); + auto* characterComponent = player->GetComponent(); + if (!characterComponent) return; + + WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel()); WorldPackets::SendServerState(packet->systemAddress); const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID()); @@ -1018,8 +1022,6 @@ void HandlePacket(Packet* packet) { Game::entityManager->ConstructAllEntities(packet->systemAddress); - auto* characterComponent = player->GetComponent(); - if (!characterComponent) return; characterComponent->RocketUnEquip(player); // Do charxml fixes here @@ -1142,7 +1144,16 @@ void HandlePacket(Packet* packet) { } case eWorldMessageType::POSITION_UPDATE: { - ClientPackets::HandleClientPositionUpdate(packet->systemAddress, packet); + auto positionUpdate = ClientPackets::HandleClientPositionUpdate(packet); + + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + LOG("Unable to get user to parse position update"); + return; + } + + Entity* entity = Game::entityManager->GetEntity(user->GetLastUsedChar()->GetObjectID()); + if (entity) entity->ProcessPositionUpdate(positionUpdate); break; } @@ -1190,7 +1201,74 @@ void HandlePacket(Packet* packet) { } case eWorldMessageType::STRING_CHECK: { - ClientPackets::HandleChatModerationRequest(packet->systemAddress, packet); + auto request = ClientPackets::HandleChatModerationRequest(packet); + + // TODO: Find a good home for the logic in this case. + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + LOG("Unable to get user to parse chat moderation request"); + return; + } + + auto* entity = Player::GetPlayer(packet->systemAddress); + + if (entity == nullptr) { + LOG("Unable to get player to parse chat moderation request"); + return; + } + + // Check if the player has restricted chat access + auto* character = entity->GetCharacter(); + + if (character->HasPermission(ePermissionMap::RestrictedChatAccess)) { + // Send a message to the player + ChatPackets::SendSystemMessage( + packet->systemAddress, + u"This character has restricted chat access." + ); + + return; + } + + bool isBestFriend = false; + + if (request.chatLevel == 1) { + // Private chat + LWOOBJID idOfReceiver = LWOOBJID_EMPTY; + + { + auto characterIdFetch = Database::Get()->GetCharacterInfo(request.receiver); + + if (characterIdFetch) { + idOfReceiver = characterIdFetch->id; + } + } + const auto& bffMap = user->GetIsBestFriendMap(); + if (bffMap.find(request.receiver) == bffMap.end() && idOfReceiver != LWOOBJID_EMPTY) { + auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); + + if (bffInfo) { + isBestFriend = bffInfo->bestFriendStatus == 3; + } + + if (isBestFriend) { + user->UpdateBestFriendValue(request.receiver, true); + } + } else if (bffMap.find(request.receiver) != bffMap.end()) { + isBestFriend = true; + } + } + + std::vector> segments = Game::chatFilter->IsSentenceOkay(request.message, entity->GetGMLevel(), !(isBestFriend && request.chatLevel == 1)); + + bool bAllClean = segments.empty(); + + if (user->GetIsMuted()) { + bAllClean = false; + } + + user->SetLastChatMessageApproved(bAllClean); + WorldPackets::SendChatModerationResponse(packet->systemAddress, bAllClean, request.requestID, request.receiver, segments); break; } @@ -1198,7 +1276,29 @@ void HandlePacket(Packet* packet) { if (chatDisabled) { ChatPackets::SendMessageFail(packet->systemAddress); } else { - ClientPackets::HandleChatMessage(packet->systemAddress, packet); + auto chatMessage = ClientPackets::HandleChatMessage(packet); + + // TODO: Find a good home for the logic in this case. + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + LOG("Unable to get user to parse chat message"); + return; + } + + if (user->GetIsMuted()) { + user->GetLastUsedChar()->SendMuteNotice(); + return; + } + std::string playerName = user->GetLastUsedChar()->GetName(); + bool isMythran = user->GetLastUsedChar()->GetGMLevel() > eGameMasterLevel::CIVILIAN; + bool isOk = Game::chatFilter->IsSentenceOkay(GeneralUtils::UTF16ToWTF8(chatMessage.message), user->GetLastUsedChar()->GetGMLevel()).empty(); + LOG_DEBUG("Msg: %s was approved previously? %i", GeneralUtils::UTF16ToWTF8(chatMessage.message).c_str(), user->GetLastChatMessageApproved()); + if (!isOk) return; + if (!isOk && !isMythran) return; + + std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message); + LOG("%s: %s", playerName.c_str(), sMessage.c_str()); + ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message); } break; @@ -1224,7 +1324,37 @@ void HandlePacket(Packet* packet) { case eWorldMessageType::UI_HELP_TOP_5: { - ClientPackets::SendTop5HelpIssues(packet); + auto language = ClientPackets::SendTop5HelpIssues(packet); + // TODO: Handle different languages in a nice way + // 0: en_US + // 1: pl_US + // 2: de_DE + // 3: en_GB + + // TODO: Find a good home for the logic in this case. + auto* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) return; + auto* character = user->GetLastUsedChar(); + if (!character) return; + auto* entity = character->GetEntity(); + if (!entity) return; + + AMFArrayValue data; + // Summaries + data.Insert("Summary0", Game::config->GetValue("help_0_summary")); + data.Insert("Summary1", Game::config->GetValue("help_1_summary")); + data.Insert("Summary2", Game::config->GetValue("help_2_summary")); + data.Insert("Summary3", Game::config->GetValue("help_3_summary")); + data.Insert("Summary4", Game::config->GetValue("help_4_summary")); + + // Descriptions + data.Insert("Description0", Game::config->GetValue("help_0_description")); + data.Insert("Description1", Game::config->GetValue("help_1_description")); + data.Insert("Description2", Game::config->GetValue("help_2_description")); + data.Insert("Description3", Game::config->GetValue("help_3_description")); + data.Insert("Description4", Game::config->GetValue("help_4_description")); + + GameMessages::SendUIMessageServerToSingleClient(entity, packet->systemAddress, "UIHelpTop5", data); break; }