/* * Darkflame Universe * Copyright 2018 */ #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 "dLogger.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 "VehiclePhysicsComponent.h" #include "dConfig.h" #include "CharacterComponent.h" #include "Database.h" #include "PacketUtils.h" #include "eGuildRank.h" void ClientPackets::HandleChatMessage(const SystemAddress& sysAddr, Packet* packet) { User* user = UserManager::Instance()->GetUser(sysAddr); if (!user) { Game::logger->Log("ClientPackets", "Unable to get user to parse chat message"); return; } if (user->GetIsMuted()) { user->GetLastUsedChar()->SendMuteNotice(); return; } CINSTREAM; uint64_t header; inStream.Read(header); char chatChannel; uint16_t unknown; uint32_t messageLength; std::u16string message; inStream.Read(chatChannel); inStream.Read(unknown); inStream.Read(messageLength); for (uint32_t i = 0; i < (messageLength - 1); ++i) { uint16_t character; inStream.Read(character); message.push_back(character); } std::string playerName = user->GetLastUsedChar()->GetName(); bool isMythran = user->GetLastUsedChar()->GetGMLevel() > 0; if (!user->GetLastChatMessageApproved() && !isMythran) return; std::string sMessage = GeneralUtils::UTF16ToWTF8(message); Game::logger->Log("Chat", "%s: %s", playerName.c_str(), sMessage.c_str()); ChatPackets::SendChatMessage(sysAddr, chatChannel, playerName, user->GetLoggedInChar(), isMythran, message); } void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet) { User* user = UserManager::Instance()->GetUser(sysAddr); if (!user) { Game::logger->Log("ClientPackets", "Unable to get user to parse position update"); return; } CINSTREAM; uint64_t header; inStream.Read(header); Entity* entity = EntityManager::Instance()->GetEntity(user->GetLastUsedChar()->GetObjectID()); if (!entity) return; ControllablePhysicsComponent* comp = static_cast(entity->GetComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS)); if (!comp) return; /* //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 EntityManager::Instance()->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); bool velocityFlag = false; inStream.Read(velocityFlag); NiPoint3 velocity{}; if (velocityFlag) { inStream.Read(velocity.x); inStream.Read(velocity.y); inStream.Read(velocity.z); } bool angVelocityFlag = false; inStream.Read(angVelocityFlag); NiPoint3 angVelocity{}; if (angVelocityFlag) { inStream.Read(angVelocity.x); inStream.Read(angVelocity.y); inStream.Read(angVelocity.z); } bool updateChar = true; if (possessorComponent != nullptr) { auto* possassableEntity = EntityManager::Instance()->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* vehiclePhysicsComponent = possassableEntity->GetComponent(); if (vehiclePhysicsComponent != nullptr) { // This is flipped for whatever reason rotation = NiQuaternion(rotation.z, rotation.y, rotation.x, rotation.w); vehiclePhysicsComponent->SetPosition(position); vehiclePhysicsComponent->SetRotation(rotation); vehiclePhysicsComponent->SetIsOnGround(onGround); vehiclePhysicsComponent->SetIsOnRail(onRail); vehiclePhysicsComponent->SetVelocity(velocity); vehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); vehiclePhysicsComponent->SetAngularVelocity(angVelocity); vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); } 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); } EntityManager::Instance()->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); EntityManager::Instance()->QueueGhostUpdate(player->GetObjectID()); if (updateChar) EntityManager::Instance()->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; } EntityManager::Instance()->SerializeEntity(entity, player); } */ } void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet) { User* user = UserManager::Instance()->GetUser(sysAddr); if (!user) { Game::logger->Log("ClientPackets", "Unable to get user to parse chat moderation request"); return; } auto* entity = Player::GetPlayer(sysAddr); if (entity == nullptr) { Game::logger->Log("ClientPackets", "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(PermissionMap::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); for (uint32_t i = 0; i < 42; ++i) { uint16_t character; stream.Read(character); 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); } } stream.Read(messageLength); for (uint32_t i = 0; i < messageLength; ++i) { uint16_t character; stream.Read(character); message.push_back(static_cast(character)); } bool isBestFriend = false; if (chatLevel == 1) { // Private chat LWOOBJID idOfReceiver = LWOOBJID_EMPTY; { sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE name = ?"); stmt->setString(1, receiver); sql::ResultSet* res = stmt->executeQuery(); if (res->next()) { idOfReceiver = res->getInt("id"); } delete stmt; delete res; } if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) { sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"); stmt->setInt(1, entity->GetObjectID()); stmt->setInt(2, idOfReceiver); stmt->setInt(3, idOfReceiver); stmt->setInt(4, entity->GetObjectID()); sql::ResultSet* res = stmt->executeQuery(); if (res->next()) { isBestFriend = res->getInt("best_friend") == 3; } if (isBestFriend) { auto tmpBestFriendMap = user->GetIsBestFriendMap(); tmpBestFriendMap[receiver] = true; user->SetIsBestFriendMap(tmpBestFriendMap); } delete res; delete stmt; } else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().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); } void ClientPackets::HandleGuildCreation(const SystemAddress& sysAddr, Packet* packet) { std::string guildName = PacketUtils::ReadString(8, packet, true); auto user = UserManager::Instance()->GetUser(sysAddr); if (!user) return; auto character = user->GetLastUsedChar(); if (!character) return; Game::logger->Log("ClientPackets", "User %s wants to create a guild with name: %s", character->GetName().c_str(), guildName.c_str()); // First, check to see if there is a guild with that name or not: auto stmt = Database::CreatePreppedStmt("SELECT * FROM guilds WHERE name=?"); stmt->setString(1, guildName.c_str()); auto res = stmt->executeQuery(); if (res->rowsCount() > 0) { Game::logger->Log("ClientPackets", "But a guild already exists with that name!"); auto usedName = GeneralUtils::UTF8ToUTF16(guildName); SendGuildCreateResponse(sysAddr, eGuildCreationResponse::REJECTED_EXISTS, LWOOBJID_EMPTY, usedName); return; } delete res; delete stmt; // if (!Game::chatFilter->IsSentenceOkay(guildName, 1).empty()) { // Game::logger->Log("ClientPackets", "But they used bad words!"); // auto usedName = GeneralUtils::UTF8ToUTF16(guildName); // SendGuildCreateResponse(sysAddr, eGuildCreationResponse::REJECTED_BAD_NAME, LWOOBJID_EMPTY, usedName); // return; // } auto entity = character->GetEntity(); if (!entity) return; // Check to see if the character is already in a guild or not: auto* characterComp = entity->GetComponent(); if (!characterComp) return; if (characterComp->GetGuildID() != 0) { ChatPackets::SendSystemMessage(sysAddr, u"You are already in a guild! Leave your current guild first."); return; } auto creation = (uint32_t)time(nullptr); // If not, insert our newly created guild: auto insertGuild = Database::CreatePreppedStmt("INSERT INTO `guilds`(`name`, `owner_id`, `uscore`, `created`) VALUES (?,?,?,?)"); insertGuild->setString(1, guildName.c_str()); insertGuild->setUInt(2, character->GetID()); insertGuild->setUInt(3, characterComp->GetUScore()); insertGuild->setUInt(4, creation); insertGuild->execute(); delete insertGuild; // Enable the guild on their character component: auto get = Database::CreatePreppedStmt("SELECT id, name FROM guilds WHERE owner_id=?"); get->setInt(1, character->GetID()); auto* results = get->executeQuery(); LWOOBJID guildId = LWOOBJID_EMPTY; std::u16string name; while (results->next()) { guildId = results->getInt(1); name = GeneralUtils::UTF8ToUTF16(results->getString(2).c_str()); characterComp->SetGuild(guildId, name); } if (guildId == LWOOBJID_EMPTY){ Game::logger->Log("ClientPackets", "Unknown error ocurred while creating a guild!"); auto usedName = GeneralUtils::UTF8ToUTF16(guildName); SendGuildCreateResponse(sysAddr, eGuildCreationResponse::UNKNOWN_ERROR, LWOOBJID_EMPTY, usedName); return; } auto insertOwner = Database::CreatePreppedStmt("INSERT INTO `guild_members`(`guild_id`, `character_id`, `rank`, `joined`) VALUES (?,?,?,?)"); insertOwner->setUInt(1, guildId); insertOwner->setUInt(2, character->GetID()); insertOwner->setUInt(3, eGuildRank::FOUNDER); insertOwner->setUInt(4, creation); insertOwner->execute(); delete insertOwner; //Send the guild create response: SendGuildCreateResponse(sysAddr, eGuildCreationResponse::CREATED, guildId, name); // GameMessages::SendDisplayGuildCreateBox(, true, sysAddr) } void ClientPackets::SendGuildCreateResponse(const SystemAddress& sysAddr, eGuildCreationResponse guildResponse, LWOOBJID guildID, std::u16string& guildName) { CBITSTREAM; CMSGHEADER; bitStream.Write(MSG_CLIENT_GUILD_CREATE_RESPONSE); bitStream.Write(guildResponse); bitStream.Write(guildID); PacketUtils::WriteWString(bitStream, guildName, 33); SEND_PACKET; }