/** * EQEmulator: Everquest Server Emulator * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY except by those people which sell it, which * are required to give you total support for your newly bought product; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "dynamic_zone.h" #include "client.h" #include "expedition.h" #include "worldserver.h" #include "../common/eqemu_logsys.h" extern WorldServer worldserver; // message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) const char* const CREATE_NOT_ALL_ADDED = "Not all players in your {} were added to the {}. The {} can take a maximum of {} players, and your {} has {}."; DynamicZone::DynamicZone( uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type) { m_zone_id = zone_id; m_zone_version = version; m_duration = std::chrono::seconds(duration); m_type = type; } Database& DynamicZone::GetDatabase() { return database; } uint16_t DynamicZone::GetCurrentInstanceID() { return zone ? static_cast(zone->GetInstanceID()) : 0; } uint16_t DynamicZone::GetCurrentZoneID() { return zone ? static_cast(zone->GetZoneID()) : 0; } DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) { auto expedition = Expedition::FindCachedExpeditionByDynamicZoneID(dz_id); if (expedition) { return &expedition->GetDynamicZone(); } // todo: other system caches return nullptr; } std::unordered_map DynamicZone::LoadMultipleDzFromDatabase( const std::vector& dynamic_zone_ids) { LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size()); std::unordered_map dynamic_zones; auto entries = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); for (auto& entry : entries) { dynamic_zones.emplace(entry.id, std::move(entry)); } return dynamic_zones; } void DynamicZone::StartAllClientRemovalTimers() { for (const auto& client_iter : entity_list.GetClientList()) { if (client_iter.second) { client_iter.second->SetDzRemovalTimer(true); } } } void DynamicZone::SendInstanceRemoveAllCharacters() { // just remove all clients in bulk instead of only characters assigned to the instance if (IsCurrentZoneDzInstance()) { DynamicZone::StartAllClientRemovalTimers(); } else if (GetInstanceID() != 0) { auto pack = CreateServerRemoveAllCharactersPacket(); worldserver.SendPacket(pack.get()); } } void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool removed) { // if removing, sets removal timer on client inside the instance if (IsCurrentZoneDzInstance()) { Client* client = entity_list.GetClientByCharID(character_id); if (client) { client->SetDzRemovalTimer(removed); } } else if (GetInstanceID() != 0) { auto pack = CreateServerAddRemoveCharacterPacket(character_id, removed); worldserver.SendPacket(pack.get()); } } bool DynamicZone::IsCurrentZoneDzInstance() const { return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID()); } void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) { // async constexpr uint32_t pack_size = sizeof(ServerDzSetDuration_Struct); auto pack = std::make_unique(ServerOP_DzSetSecondsRemaining, pack_size); auto buf = reinterpret_cast(pack->pBuffer); buf->dz_id = GetID(); buf->seconds = seconds_remaining; worldserver.SendPacket(pack.get()); } void DynamicZone::SetUpdatedDuration(uint32_t new_duration) { // preserves original start time, just modifies duration and expire time m_duration = std::chrono::seconds(new_duration); m_expire_time = m_start_time + m_duration; LogDynamicZones("Updated zone [{}]:[{}] seconds remaining: [{}]", m_zone_id, m_instance_id, GetSecondsRemaining()); if (zone && IsCurrentZoneDzInstance()) { zone->SetInstanceTimer(GetSecondsRemaining()); } } void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) { auto pack = CreateServerDzLocationPacket(server_opcode, location); worldserver.SendPacket(pack.get()); } void DynamicZone::HandleWorldMessage(ServerPacket* pack) { switch (pack->opcode) { case ServerOP_DzAddRemoveCharacter: { auto buf = reinterpret_cast(pack->pBuffer); Client* client = entity_list.GetClientByCharID(buf->character_id); if (client) { client->SetDzRemovalTimer(buf->remove); // instance kick timer } break; } case ServerOP_DzRemoveAllCharacters: { auto buf = reinterpret_cast(pack->pBuffer); if (buf->remove) { DynamicZone::StartAllClientRemovalTimers(); } break; } case ServerOP_DzDurationUpdate: { auto buf = reinterpret_cast(pack->pBuffer); auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); if (dz) { dz->SetUpdatedDuration(buf->seconds); } break; } case ServerOP_DzSetCompass: case ServerOP_DzSetSafeReturn: case ServerOP_DzSetZoneIn: { auto buf = reinterpret_cast(pack->pBuffer); if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) { auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); if (dz) { if (pack->opcode == ServerOP_DzSetCompass) { dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false); } else if (pack->opcode == ServerOP_DzSetSafeReturn) { dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false); } else if (pack->opcode == ServerOP_DzSetZoneIn) { dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false); } } } break; } } } void DynamicZone::ProcessCompassChange(const DynamicZoneLocation& location) { DynamicZoneBase::ProcessCompassChange(location); if (m_on_compass_change) { m_on_compass_change(); } }