diff --git a/source/WorldServer/Commands/Commands.cpp b/source/WorldServer/Commands/Commands.cpp index c59e196..10e3ab6 100644 --- a/source/WorldServer/Commands/Commands.cpp +++ b/source/WorldServer/Commands/Commands.cpp @@ -12081,9 +12081,12 @@ void Commands::Command_MoveCharacter(Client* client, Seperator* sep) { { char* name = sep->arg[0]; char* zoneName = sep->arg[1]; + int32 zone_duplicating_id = 0; + if(sep->arg[2][0]) + zone_duplicating_id = atoul(sep->arg[2]); char query[256]; - snprintf(query, 256, "UPDATE characters c, zones z set c.x = z.safe_x, c.y = z.safe_y, c.z = z.safe_z, c.heading = z.safe_heading, c.current_zone_id = z.id where c.name = '%s' and z.name='%s'", name, zoneName); + snprintf(query, 256, "UPDATE characters c, zones z set c.x = z.safe_x, c.y = z.safe_y, c.z = z.safe_z, c.heading = z.safe_heading, c.current_zone_id = z.id, c.zone_duplicating_id = %u where c.name = '%s' and z.name='%s'", zone_duplicating_id, name, zoneName); if (database.RunQuery(query, strnlen(query, 256))) { client->Message(CHANNEL_COLOR_YELLOW, "Ran query:%s", query); diff --git a/source/WorldServer/Web/PeerManager.cpp b/source/WorldServer/Web/PeerManager.cpp index 8be9d95..c0fbbdf 100644 --- a/source/WorldServer/Web/PeerManager.cpp +++ b/source/WorldServer/Web/PeerManager.cpp @@ -170,7 +170,7 @@ void PeerManager::setZonePeerDataSelf(ZoneChangeDetails* opt_details, std::strin } } -std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details, bool only_always_loaded) { +std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details, bool only_always_loaded, int32 matchDuplicatedId) { bool matchFullZone = false; std::string fullZoneId = ""; for (auto& [peerId, peer] : peers) { @@ -201,12 +201,16 @@ std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 i int32 default_reenter_time = zone.second.get("default_reenter_time"); int8 instance_type = zone.second.get("instance_type"); bool always_loaded = zone.second.get("always_loaded"); + int32 duplicate_id = zone.second.get("duplicated_id"); if (only_always_loaded && !always_loaded) continue; if (!shutting_down) { bool match = false; + if(matchDuplicatedId > 0 && duplicate_id != matchDuplicatedId) + continue; + if (instance_zone && inc_instance_id > 0 && instance_id == inc_instance_id) { match = true; } @@ -244,7 +248,7 @@ std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 i return fullZoneId; } -int32 PeerManager::getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id) { +int32 PeerManager::getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value) { int32 highestID = 0; bool matched_zone = false; for (auto& [peerId, peer] : peers) { @@ -281,7 +285,7 @@ int32 PeerManager::getZoneHighestDuplicateId(const std::string& inc_zone_name, i } } - if(matched_zone) { + if(matched_zone && increment_new_value) { highestID++; } diff --git a/source/WorldServer/Web/PeerManager.h b/source/WorldServer/Web/PeerManager.h index 1a0f80b..fa92bb5 100644 --- a/source/WorldServer/Web/PeerManager.h +++ b/source/WorldServer/Web/PeerManager.h @@ -181,8 +181,8 @@ public: void SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0); void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0); void sendZonePeerList(Client* client); - std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false); - int32 getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id); + std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false, int32 matchDuplicatedId = 0); + int32 getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value = true); void setZonePeerData(ZoneChangeDetails* opt_details); void setPrimary(const std::string& id); bool hasPrimary(); diff --git a/source/WorldServer/World.cpp b/source/WorldServer/World.cpp index f252a4b..59f7e3c 100644 --- a/source/WorldServer/World.cpp +++ b/source/WorldServer/World.cpp @@ -1608,11 +1608,11 @@ void ZoneList::ReloadSpawns() { } -int32 ZoneList::GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id) +int32 ZoneList::GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value) { list::iterator zone_iter; ZoneServer* tmp = 0; - int32 highest_id = peer_manager.getZoneHighestDuplicateId(inc_zone_name, inc_zone_id); + int32 highest_id = peer_manager.getZoneHighestDuplicateId(inc_zone_name, inc_zone_id, increment_new_value); MZoneList.readlock(__FUNCTION__, __LINE__); bool match = false; bool matched_peer = (highest_id>0); @@ -1635,12 +1635,47 @@ int32 ZoneList::GetHighestDuplicateID(const std::string& inc_zone_name, int32 in MZoneList.releasereadlock(__FUNCTION__, __LINE__); - if(match && !matched_peer) + if(match && !matched_peer && increment_new_value) highest_id++; return highest_id; } +bool ZoneList::GetDuplicateZoneDetails(ZoneChangeDetails* zone_details, const std::string& inc_zone_name, int32 inc_zone_id, int32 matchDuplicateId) +{ + list::iterator zone_iter; + ZoneServer* tmp = 0; + std::string peerId = peer_manager.getZonePeerId(inc_zone_name, inc_zone_id, 0, zone_details, false, matchDuplicateId); + if(peerId.size() > 0) + return true; + + MZoneList.readlock(__FUNCTION__, __LINE__); + bool match = false; + for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++) + { + tmp = *zone_iter; + + if(tmp->IsInstanceZone()) + continue; + + if(((inc_zone_id > 0 && tmp->GetZoneID() == inc_zone_id) || (inc_zone_name.length() > 0 && strncasecmp(tmp->GetZoneName(), inc_zone_name.c_str(), inc_zone_name.length())==0))){ + if(tmp->DuplicatedID() == matchDuplicateId) { + peer_manager.setZonePeerDataSelf(zone_details, std::string(tmp->GetZoneFile()), std::string(tmp->GetZoneName()), tmp->GetZoneID(), + tmp->GetInstanceID(), tmp->GetSafeX(), tmp->GetSafeY(), tmp->GetSafeZ(), tmp->GetSafeHeading(), + tmp->GetZoneLockState(), tmp->GetMinimumStatus(), tmp->GetMinimumLevel(), tmp->GetMaximumLevel(), + tmp->GetMinimumVersion(), tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime(), + tmp->GetInstanceType(), tmp->NumPlayers(), tmp->IsCityZone(), tmp); + match = true; + break; + } + } + } + + MZoneList.releasereadlock(__FUNCTION__, __LINE__); + + return match; +} + bool World::ReportBug(string data, char* player_name, int32 account_id, const char* spawn_name, int32 spawn_id, int32 zone_id){ //loginserver vector list; diff --git a/source/WorldServer/World.h b/source/WorldServer/World.h index 5230078..e31d6a9 100644 --- a/source/WorldServer/World.h +++ b/source/WorldServer/World.h @@ -512,7 +512,8 @@ class ZoneList { void ReloadMail(); void ReloadSpawns(); - int32 GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id); + int32 GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value = true); + bool GetDuplicateZoneDetails(ZoneChangeDetails* zone_details, const std::string& inc_zone_name, int32 inc_zone_id, int32 matchDuplicateId); void WatchdogHeartbeat(); diff --git a/source/WorldServer/WorldDatabase.cpp b/source/WorldServer/WorldDatabase.cpp index c325f71..8c45b93 100644 --- a/source/WorldServer/WorldDatabase.cpp +++ b/source/WorldServer/WorldDatabase.cpp @@ -1780,7 +1780,7 @@ bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client* MYSQL_ROW row, row4; int32 id = 0; query.escaped_name = getEscapeString(ch_name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage, alignment, first_world_login FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage, alignment, first_world_login, zone_duplicating_id FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id); // no character found if ( result == NULL ) { LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name); @@ -1840,12 +1840,13 @@ SOGA chars looked ok in LoginServer screen tho... odd. if ( LoadCharacterInstances(client) ) client->UpdateCharacterInstances(); + int32 zone_duplicate_id = atoul(row[32]); InstanceData* data = client->GetPlayer()->GetCharacterInstances()->FindInstanceByZoneID(zoneid); // housing doesn't have a data pointer here is why the data check was removed.. hmm if (instanceid > 0) client->SetCurrentZoneByInstanceID(instanceid, zoneid); else - client->SetCurrentZone(zoneid); + client->SetCurrentZone(zoneid, zone_duplicate_id); int32 lastsavedtime = atoi(row[28]); client->SetLastSavedTimeStamp(lastsavedtime); @@ -4378,7 +4379,7 @@ void WorldDatabase::Save(Client* client){ else if(client->GetCurrentZone()) zone_id = client->GetCurrentZone()->GetZoneID(); - query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetCharacterID()); + query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u, zone_duplicating_id = %u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetDuplicatingZoneID(), client->GetCharacterID()); query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i, pet_name = '%s' where char_id = %u", player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(), player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(), diff --git a/source/WorldServer/client.cpp b/source/WorldServer/client.cpp index d69fc2b..7d5431e 100644 --- a/source/WorldServer/client.cpp +++ b/source/WorldServer/client.cpp @@ -166,6 +166,7 @@ Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125 zoning_x = 0; zoning_y = 0; zoning_z = 0; + duplicate_zoning_id = 0; zoning_instance_id = 0; player_pos_changed = false; player_pos_timer = Timer::GetCurrentTime2() + 1000; @@ -958,9 +959,8 @@ void Client::SendZoneInfo() { QueuePacket(fog_packet->serialize()); safe_delete(fog_packet); } - - zone->SendFlightPathsPackets(this); } + zone->SendFlightPathsPackets(this); } /* uchar blah[] ={0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x00,0x00,0x00,0x00 @@ -2753,33 +2753,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { case OP_ReadyForTakeOffMsg: { LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ReadyForTakeOffMsg", opcode, opcode); - - int32 index = GetCurrentZone()->GetFlightPathIndex(GetPendingFlightPath()); - if (GetPendingFlightPath() > 0) { - if (index != -1) { - PacketStruct* packet = configReader.getStruct("WS_ClearForTakeOff", GetVersion()); - if (packet) { - packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(GetPlayer())); - packet->setDataByName("path_id", index); - packet->setDataByName("speed", GetCurrentZone()->GetFlightPathSpeed(GetPendingFlightPath())); - QueuePacket(packet->serialize()); - safe_delete(packet); - - on_auto_mount = true; - } - } - else { - LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but unable to get an index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID()); - Message(CHANNEL_ERROR, "Unable to get index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID()); - EndAutoMount(); - } - - SetPendingFlightPath(0); - - } - else - LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but there is no pending flight path..."); - + AttemptStartAutoMount(); break; } @@ -4133,15 +4107,25 @@ void Client::SetCurrentZone(ZoneServer* zone) { if (player) { player->SetZone(zone, GetVersion()); } + if(zone) + duplicate_zoning_id = zone->DuplicatedID(); } -void Client::SetCurrentZone(int32 id) { +void Client::SetCurrentZone(int32 id, int32 duplicate_zone_id) { if (current_zone) { //current_zone->GetCombat()->RemoveHate(player); current_zone->RemoveSpawn(player, false, true, true, true, true); } + + duplicate_zoning_id = 0; + bool foundDupeZone = false; ZoneChangeDetails zone_details; - if (zone_list.GetZone(&zone_details, id, "", true, false, true, false)) { + if(duplicate_zone_id) { + if(foundDupeZone = zone_list.GetDuplicateZoneDetails(&zone_details, "", id, duplicate_zone_id)) + duplicate_zoning_id = duplicate_zone_id; + } + + if (foundDupeZone || zone_list.GetZone(&zone_details, id, "", true, false, true, false)) { SetCurrentZone((ZoneServer*)zone_details.zonePtr); } } @@ -4151,6 +4135,7 @@ void Client::SetCurrentZoneByInstanceID(int32 id, int32 zoneid) { //current_zone->GetCombat()->RemoveHate(player); current_zone->RemoveSpawn(player, false, true, true, true, true); } + duplicate_zoning_id = 0; ZoneChangeDetails zone_details; int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0; world.GetGroupManager()->EstablishRaidLevelRange(this, &minLevel, &maxLevel, &avgLevel, &firstLevel); @@ -4766,7 +4751,7 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) { instance_zone->GetSafeZ(), instance_zone->GetSafeHeading(), instance_zone->GetZoneLockState(), instance_zone->GetMinimumStatus(), instance_zone->GetMinimumLevel(), instance_zone->GetMaximumLevel(), instance_zone->GetMinimumVersion(), instance_zone->GetDefaultLockoutTime(), instance_zone->GetDefaultReenterTime(), - instance_zone->GetInstanceType(), instance_zone->NumPlayers(), instance_zone); + instance_zone->GetInstanceType(), instance_zone->NumPlayers(), instance_zone->IsCityZone(), instance_zone); } } else { @@ -10009,6 +9994,7 @@ void Client::ProcessTeleport(Spawn* spawn, vector* destin if (packet) { packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn)); + int32 additional_locations = 0; // Put all the destinations the player can go in a new vector vector destinations; for (int32 i = 0; i < transport_list.size(); i++) { @@ -10033,26 +10019,47 @@ void Client::ProcessTeleport(Spawn* spawn, vector* destin packet->setDataByName("current_map_y", destination->map_y); } else { + int32 additional_zones = zone_list.GetHighestDuplicateID("", destination->destination_zone_id, false); + additional_locations += additional_zones; destinations.push_back(destination); } } // Use the new vector to create the packet destination = 0; - packet->setArrayLengthByName("num_destinations", destinations.size()); + packet->setArrayLengthByName("num_destinations", additional_locations+destinations.size()); + int32 offset = 0; + int32 unique_ids = 3000000; // db has hard cap of 2 million for (int32 i = 0; i < destinations.size(); i++) { destination = destinations.at(i); - packet->setArrayDataByName("unique_id", destination->unique_id, i); - packet->setArrayDataByName("display_name", destination->display_name.c_str(), i); - packet->setArrayDataByName("zone_name", destination->display_name.c_str(), i); - packet->setArrayDataByName("zone_file_name", destination->display_name.c_str(), i); - packet->setArrayDataByName("cost", destination->cost, i); + packet->setArrayDataByName("unique_id", destination->unique_id, i+offset); + packet->setArrayDataByName("display_name", destination->display_name.c_str(), i+offset); + packet->setArrayDataByName("zone_name", destination->display_name.c_str(), i+offset); + packet->setArrayDataByName("zone_file_name", destination->display_name.c_str(), i+offset); + packet->setArrayDataByName("cost", destination->cost, i+offset); if (has_map) { - packet->setArrayDataByName("map_x", destination->map_x, i); - packet->setArrayDataByName("map_y", destination->map_y, i); + packet->setArrayDataByName("map_x", destination->map_x, i+offset); + packet->setArrayDataByName("map_y", destination->map_y, i+offset); } + + int32 additional_zones = zone_list.GetHighestDuplicateID("", destination->destination_zone_id, false); + for (int32 a = 0; a < additional_zones; a++) { + int32 field_pos = a+1; + packet->setArrayDataByName("unique_id", unique_ids++, i+offset+field_pos); + std::string name = destination->display_name + " " + std::to_string(field_pos); + packet->setArrayDataByName("display_name", name.c_str(), i+offset+field_pos); + packet->setArrayDataByName("zone_name", name.c_str(), i+offset+field_pos); + packet->setArrayDataByName("zone_file_name", destination->display_name.c_str(), i+offset+field_pos); + packet->setArrayDataByName("cost", destination->cost, i+offset+field_pos); + + if (has_map) { + packet->setArrayDataByName("map_x", destination->map_x, i+offset+field_pos); + packet->setArrayDataByName("map_y", destination->map_y, i+offset+field_pos); + } + } + offset += additional_zones; } @@ -10069,6 +10076,7 @@ void Client::ProcessTeleport(Spawn* spawn, vector* destin } void Client::ProcessTeleportLocation(EQApplicationPacket* app) { + int32 duplicateId = 0; PacketStruct* packet = configReader.getStruct("WS_TeleportDestination", GetVersion()); if (packet) { if (packet->LoadPacketData(app->pBuffer, app->size)) { @@ -10078,11 +10086,18 @@ void Client::ProcessTeleportLocation(EQApplicationPacket* app) { int32 cost = packet->getType_int32_ByName("cost"); vector destinations; TransportDestination* destination = 0; + duplicateId = extractZoneNumericalSuffix(zone_name); + if(duplicateId > 0) { + size_t lastSpacePos = zone_name.find_last_of(' '); + if (lastSpacePos != std::string::npos) { + zone_name = zone_name.substr(0, lastSpacePos); + } + } if (this->GetTemporaryTransportID() || (spawn && spawn == transport_spawn && spawn->GetTransporterID())) GetCurrentZone()->GetTransporters(&destinations, this, this->GetTemporaryTransportID() ? this->GetTemporaryTransportID() : spawn->GetTransporterID()); vector::iterator itr; for (itr = destinations.begin(); itr != destinations.end(); itr++) { - if ((*itr)->unique_id == unique_id && (*itr)->display_name == zone_name && (*itr)->cost == cost) { + if (((*itr)->unique_id == unique_id || unique_id >= 3000000) && (*itr)->display_name == zone_name && (*itr)->cost == cost) { destination = *itr; break; } @@ -10115,7 +10130,14 @@ void Client::ProcessTeleportLocation(EQApplicationPacket* app) { if (!TryZoneInstance(destination->destination_zone_id, false)) { LogWrite(INSTANCE__DEBUG, 0, "Instance", "Attempting to zone normally"); ZoneChangeDetails zone_details; - if (zone_list.GetZone(&zone_details, destination->destination_zone_id)) { + bool foundDupeZone = false; + duplicate_zoning_id = 0; + if(duplicateId > 0) { + if(foundDupeZone = zone_list.GetDuplicateZoneDetails(&zone_details, "", destination->destination_zone_id, duplicateId)) + duplicate_zoning_id = duplicateId; + } + + if (foundDupeZone || zone_list.GetZone(&zone_details, destination->destination_zone_id)) { Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false); } } @@ -11587,6 +11609,34 @@ void Client::SavePlayerImages() { incoming_paperdoll.current_size_bytes = 0; } +void Client::AttemptStartAutoMount() { + int32 index = GetCurrentZone()->GetFlightPathIndex(GetPendingFlightPath()); + if (GetPendingFlightPath() > 0) { + if (index != -1) { + PacketStruct* packet = configReader.getStruct("WS_ClearForTakeOff", GetVersion()); + if (packet) { + packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(GetPlayer())); + packet->setDataByName("path_id", index); + packet->setDataByName("speed", GetCurrentZone()->GetFlightPathSpeed(GetPendingFlightPath())); + QueuePacket(packet->serialize()); + safe_delete(packet); + + on_auto_mount = true; + } + } + else { + LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but unable to get an index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID()); + Message(CHANNEL_ERROR, "Unable to get index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID()); + EndAutoMount(); + } + + SetPendingFlightPath(0); + + } + else + LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but there is no pending flight path..."); +} + void Client::EndAutoMount() { PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", GetVersion()); if (packet) { @@ -12208,6 +12258,20 @@ void Client::SendFlightAutoMount(int32 path_id, int16 mount_id, int8 mount_red_c if (mount_id) ((Entity*)GetPlayer())->SetMount(mount_id, mount_red_color, mount_green_color, mount_blue_color); + + if(GetVersion() <= 561) { + PacketStruct* packet = configReader.getStruct("WS_CreateBoatTransportMsg", GetVersion()); + if (!packet) { + LogWrite(CCLIENT__ERROR, 0, "Client", "WS_CreateBoatTransportMsg missing for version %u", GetVersion()); + return; + } + int8 index = (int8)GetCurrentZone()->GetFlightPathIndex(GetPendingFlightPath()); + packet->setDataByName("path_id", index); + // packet->PrintPacket(); + QueuePacket(packet->serialize()); + safe_delete(packet); + AttemptStartAutoMount(); + } } void Client::SendShowBook(Spawn* sender, string title, int8 language, int8 num_pages, ...) diff --git a/source/WorldServer/client.h b/source/WorldServer/client.h index 6e69eea..24e79c2 100644 --- a/source/WorldServer/client.h +++ b/source/WorldServer/client.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "../common/EQStream.h" #include "../common/timer.h" @@ -314,12 +316,14 @@ public: int32 GetCurrentZoneID(); void SetCurrentZoneByInstanceID(int32 id, int32 zoneid); //void SetCurrentZoneByInstanceID(instanceid, zoneid); - void SetCurrentZone(int32 id); + void SetCurrentZone(int32 id, int32 zone_duplicate_id = 0); void SetCurrentZone(ZoneServer* zone); void SetZoningDestination(ZoneServer* zone) { zoning_destination = zone; } ZoneServer* GetZoningDestination() { return zoning_destination; } + int32 GetDuplicatingZoneID() { return duplicate_zoning_id; } + Player* GetPlayer() { return player; } EQStream* getConnection() { return eqs; } void setConnection(EQStream* ieqs) { eqs = ieqs; } @@ -508,7 +512,25 @@ public: void SetPendingFlightPath(int32 val) { pending_flight_path = val; } int32 GetPendingFlightPath() { return pending_flight_path; } + void AttemptStartAutoMount(); + + int32 extractZoneNumericalSuffix(const std::string& input) { + try { + std::regex pattern(R"(.*\s(\d+)$)"); // Matches a space followed by digits at the end + std::smatch match; + if (std::regex_match(input, match, pattern)) { + return std::stoul(match[1].str()); // Extract and convert the numerical part + } + } catch (const std::regex_error& e) { + std::cerr << "Regex error: " << e.what() << "\n"; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << "\n"; + } + + return 0; // Return nullopt if no match is found or an exception occurs + } + void EndAutoMount(); bool GetOnAutoMount() { return on_auto_mount; } @@ -764,6 +786,7 @@ private: int32 zoning_id; int32 zoning_instance_id; ZoneServer* zoning_destination; + int32 duplicate_zoning_id; // when a public zone is instanced for too many players this its number float zoning_x; float zoning_y; float zoning_z;