diff --git a/common/emu_constants.h b/common/emu_constants.h index c6491b3b3..945f513b5 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -317,6 +317,15 @@ namespace EQEmu QuestControlGrid = -1 }; + namespace consent { + enum eConsentType : uint8 { + Normal = 0, + Group, + Raid, + Guild + }; + }; // namespace consent + } /*EQEmu*/ #endif /*COMMON_EMU_CONSTANTS_H*/ diff --git a/common/servertalk.h b/common/servertalk.h index b9cb6202e..2557941e7 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -866,10 +866,13 @@ struct SpawnPlayerCorpse_Struct { struct ServerOP_Consent_Struct { char grantname[64]; char ownername[64]; + char zonename[32]; uint8 permission; uint32 zone_id; uint16 instance_id; uint32 message_string_id; + uint8 consent_type; // 0 = normal, 1 = group, 2 = raid, 3 = guild + uint32 consent_id; }; struct ReloadTasks_Struct { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index dd55fac39..1bbc7f1fc 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1057,110 +1057,37 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } case ServerOP_Consent: { - // Message string id's likely to be used here are: - // CONSENT_YOURSELF = 399 - // CONSENT_INVALID_NAME = 397 - // TARGET_NOT_FOUND = 101 - ZoneServer* zs; - ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - ClientListEntry* cle = client_list.FindCharacter(s->grantname); - if (cle) { - if (cle->instance() != 0) - { - zs = zoneserver_list.FindByInstanceID(cle->instance()); - if (zs) { - zs->SendPacket(pack); - } - else - { - auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->instance_id = s->instance_id; - scs->message_string_id = 101; - zs = zoneserver_list.FindByInstanceID(s->instance_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id); - } - safe_delete(pack); - } - } - else - { - zs = zoneserver_list.FindByZoneID(cle->zone()); - if (zs) { - zs->SendPacket(pack); - } - else { - // send target not found back to requester - auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->message_string_id = 101; - zs = zoneserver_list.FindByZoneID(s->zone_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); - } - safe_delete(pack); - } - } - } - else { - // send target not found back to requester - auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->message_string_id = 397; - zs = zoneserver_list.FindByZoneID(s->zone_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); - } - safe_delete(pack); - } + zoneserver_list.SendPacket(pack); // update corpses in all zones break; } case ServerOP_Consent_Response: { - // Message string id's likely to be used here are: - // CONSENT_YOURSELF = 399 - // CONSENT_INVALID_NAME = 397 - // TARGET_NOT_FOUND = 101 ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - if (s->instance_id != 0) + + ZoneServer* owner_zs = nullptr; + if ((s->instance_id != 0 && (owner_zs = zoneserver_list.FindByInstanceID(s->instance_id))) || + (s->instance_id == 0 && (owner_zs = zoneserver_list.FindByZoneID(s->zone_id)))) { - ZoneServer* zs = zoneserver_list.FindByInstanceID(s->instance_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->instance_id); - } + owner_zs->SendPacket(pack); } else { - ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id); - if (zs) { - zs->SendPacket(pack); - } - else { - LogInfo("Unable to locate zone record for zone id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id); + LogInfo("Unable to locate zone record for zone id [{}] or instance id [{}] in zoneserver list for ServerOP_Consent_Response operation", s->zone_id, s->instance_id); + } + + if (s->consent_type == EQEmu::consent::Normal) + { + // send the message to the client being granted or denied permission + if (ClientListEntry* cle = client_list.FindCharacter(s->grantname)) + { + ZoneServer* granted_zs = nullptr; + if ((cle->instance() != 0 && (granted_zs = zoneserver_list.FindByInstanceID(cle->instance()))) || + (cle->instance() == 0 && (granted_zs = zoneserver_list.FindByZoneID(cle->zone())))) + { + // avoid sending twice if owner and granted are in same zone + if (granted_zs != owner_zs) { + granted_zs->SendPacket(pack); + } + } } } break; diff --git a/zone/client.cpp b/zone/client.cpp index 9ea27869a..f43b4a48e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -165,6 +165,7 @@ Client::Client(EQStreamInterface* ieqs) hp_self_update_throttle_timer(300), hp_other_update_throttle_timer(500), position_update_timer(10000), + consent_throttle_timer(2000), tmSitting(0) { @@ -6254,6 +6255,51 @@ void Client::DragCorpses() } } +void Client::ConsentCorpses(const char* consent_name, bool deny) +{ + if (strcasecmp(consent_name, GetName()) == 0) { + MessageString(Chat::Red, CONSENT_YOURSELF); + } + else if (!consent_throttle_timer.Check()) { + MessageString(Chat::Red, CONSENT_WAIT); + } + else { + auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, consent_name); + strcpy(scs->ownername, GetName()); + strcpy(scs->zonename, "Unknown"); + scs->message_string_id = 0; + scs->permission = deny ? 0 : 1; + scs->zone_id = zone->GetZoneID(); + scs->instance_id = zone->GetInstanceID(); + scs->consent_type = EQEmu::consent::Normal; + scs->consent_id = 0; + if (strcasecmp(scs->grantname, "group") == 0) { + if (!deny) { + Group* grp = GetGroup(); + scs->consent_id = grp ? grp->GetID() : 0; + } + scs->consent_type = EQEmu::consent::Group; + } + else if (strcasecmp(scs->grantname, "raid") == 0) { + if (!deny) { + Raid* raid = GetRaid(); + scs->consent_id = raid ? raid->GetID() : 0; + } + scs->consent_type = EQEmu::consent::Raid; + } + else if (strcasecmp(scs->grantname, "guild") == 0) { + if (!deny) { + scs->consent_id = GuildID(); + } + scs->consent_type = EQEmu::consent::Guild; + } + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration) { if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID()) diff --git a/zone/client.h b/zone/client.h index 4ff431520..7f6a9bd9e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -793,6 +793,9 @@ public: virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); } inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } + inline bool AutoConsentGroupEnabled() const { return m_pp.groupAutoconsent != 0; } + inline bool AutoConsentRaidEnabled() const { return m_pp.raidAutoconsent != 0; } + inline bool AutoConsentGuildEnabled() const { return m_pp.guildAutoconsent != 0; } void SummonHorse(uint16 spell_id); void SetHorseId(uint16 horseid_in); @@ -957,7 +960,6 @@ public: void EnteringMessages(Client* client); void SendRules(Client* client); - std::list consent_list; const bool GetGMSpeed() const { return (gmspeed > 0); } bool CanUseReport; @@ -1137,6 +1139,7 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } + void ConsentCorpses(const char* consent_name, bool deny = false); void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); @@ -1527,6 +1530,7 @@ private: Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */ Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */ Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ + Timer consent_throttle_timer; glm::vec3 m_Proximity; glm::vec4 last_position_before_bulk_update; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ebc6bd38a..09dae201a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4643,43 +4643,16 @@ void Client::Handle_OP_Consent(const EQApplicationPacket *app) { if (app->size<64) { Consent_Struct* c = (Consent_Struct*)app->pBuffer; - if (strcmp(c->name, GetName()) != 0) { - auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, c->name); - strcpy(scs->ownername, GetName()); - scs->message_string_id = 0; - scs->permission = 1; - scs->zone_id = zone->GetZoneID(); - scs->instance_id = zone->GetInstanceID(); - //consent_list.push_back(scs->grantname); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - MessageString(Chat::White, CONSENT_YOURSELF); - } + ConsentCorpses(c->name, false); } - return; } void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) { if (app->size<64) { Consent_Struct* c = (Consent_Struct*)app->pBuffer; - auto pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, c->name); - strcpy(scs->ownername, GetName()); - scs->message_string_id = 0; - scs->permission = 0; - scs->zone_id = zone->GetZoneID(); - scs->instance_id = zone->GetInstanceID(); - //consent_list.remove(scs->grantname); - worldserver.SendPacket(pack); - safe_delete(pack); + ConsentCorpses(c->name, true); } - return; } void Client::Handle_OP_Consider(const EQApplicationPacket *app) @@ -13315,6 +13288,21 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) entity_list.QueueClients(this, app, true); } } + else if (sa->type == AT_GroupConsent) + { + m_pp.groupAutoconsent = (sa->parameter == 1); + ConsentCorpses("Group", (sa->parameter != 1)); + } + else if (sa->type == AT_RaidConsent) + { + m_pp.raidAutoconsent = (sa->parameter == 1); + ConsentCorpses("Raid", (sa->parameter != 1)); + } + else if (sa->type == AT_GuildConsent) + { + m_pp.guildAutoconsent = (sa->parameter == 1); + ConsentCorpses("Guild", (sa->parameter != 1)); + } else { std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 09661826d..6b52a0c63 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -282,6 +282,12 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( allowed_looters[i] = 0; } + Group* grp = nullptr; + Raid* raid = nullptr; + consent_group_id = (client->AutoConsentGroupEnabled() && (grp = client->GetGroup())) ? grp->GetID() : 0; + consent_raid_id = (client->AutoConsentRaidEnabled() && (raid = client->GetRaid())) ? raid->GetID() : 0; + consent_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0; + is_corpse_changed = true; rez_experience = in_rezexp; can_corpse_be_rezzed = true; @@ -647,6 +653,25 @@ void Corpse::DepopPlayerCorpse() { player_corpse_depop = true; } +void Corpse::AddConsentName(const char* add_name) +{ + for (const auto& n : consent_names) { + if (strcasecmp(n.c_str(), add_name) == 0) { + return; + } + } + consent_names.emplace_back(add_name); +} + +void Corpse::RemoveConsentName(const char* rem_name) +{ + consent_names.erase(std::remove_if(consent_names.begin(), consent_names.end(), + [rem_name](const std::string& n) { + return strcasecmp(n.c_str(), rem_name) == 0; + } + ), consent_names.end()); +} + uint32 Corpse::CountItems() { return itemlist.size(); } @@ -1434,29 +1459,43 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { is_corpse_changed = true; } else { - client->Message(Chat::White, "Corpse is too far away."); + client->MessageString(Chat::Red, CORPSE_TOO_FAR); return false; } } else { bool consented = false; - std::list::iterator itr; - for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) { - if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) { - if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; - } - else { - client->Message(Chat::White, "Corpse is too far away."); - return false; - } + for (const auto& n : consent_names) { + if (strcasecmp(client->GetName(), n.c_str()) == 0) { + consented = true; + break; + } + } + + if (!consented) { + Group* grp = nullptr; + Raid* raid = nullptr; + if ((consent_guild_id && consent_guild_id != GUILD_NONE && client->GuildID() == consent_guild_id) || + (consent_group_id && (grp = client->GetGroup()) && grp->GetID() == consent_group_id) || + (consent_raid_id && (raid = client->GetRaid()) && raid->GetID() == consent_raid_id)) + { consented = true; } } - if(!consented) { - client->Message(Chat::White, "You do not have permission to move this corpse."); + + if (consented) { + if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { + GMMove(client->GetX(), client->GetY(), client->GetZ()); + is_corpse_changed = true; + } + else { + client->MessageString(Chat::Red, CORPSE_TOO_FAR); + return false; + } + } + else { + client->MessageString(Chat::Red, CONSENT_DENIED); return false; } } diff --git a/zone/corpse.h b/zone/corpse.h index e7a7c5805..ae365abef 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -74,6 +74,11 @@ class Corpse : public Mob { uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } void SetDecayTimer(uint32 decay_time); + void SetConsentGroupID(uint32 id) { if (IsPlayerCorpse()) { consent_group_id = id; } } + void SetConsentRaidID(uint32 id) { if (IsPlayerCorpse()) { consent_raid_id = id; } } + void SetConsentGuildID(uint32 id) { if (IsPlayerCorpse()) { consent_guild_id = id; } } + void AddConsentName(const char* name); + void RemoveConsentName(const char* name); void Delete(); void Bury(); @@ -142,6 +147,9 @@ private: int32 player_kill_item; /* Determines if Player Kill Item */ uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ uint32 char_id; /* Character ID */ + uint32 consent_group_id = 0; /* consented group id */ + uint32 consent_raid_id = 0; /* consented raid id */ + uint32 consent_guild_id = 0; /* consented guild id */ ItemList itemlist; /* Internal Item list used for corpses */ uint32 copper; uint32 silver; @@ -160,6 +168,7 @@ private: Timer corpse_graveyard_timer; Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ EQEmu::TintProfile item_tint; + std::vector consent_names; LootRequestType loot_request_type; }; diff --git a/zone/string_ids.h b/zone/string_ids.h index 0948d13a8..7c4066b55 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -122,11 +122,13 @@ #define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. #define PICK_LORE 379 //You cannot pick up a lore item you already possess. #define POISON_TOO_HIGH 382 // This poison is too high level for you to apply. +#define CORPSE_TOO_FAR 389 //The corpse is too far away to summon. #define CONSENT_DENIED 390 //You do not have consent to summon that corpse. #define DISCIPLINE_RDY 393 //You are ready to use a new discipline now. #define CONSENT_INVALID_NAME 397 //Not a valid consent name. #define CONSENT_NPC 398 //You cannot consent NPC\'s. #define CONSENT_YOURSELF 399 //You cannot consent yourself. +#define CONSENT_WAIT 400 //You must wait 2 seconds between consents. #define SONG_NEEDS_DRUM 405 //You need to play a percussion instrument for this song #define SONG_NEEDS_WIND 406 //You need to play a wind instrument for this song #define SONG_NEEDS_STRINGS 407 //You need to play a stringed instrument for this song @@ -266,6 +268,8 @@ #define REZZ_ALREADY_PENDING 1379 //You were unable to restore the corpse to life, but you may have success with a later attempt. #define IN_USE 1406 //Someone else is using that. Try again later. #define DUEL_FLED 1408 //%1 has defeated %2 in a duel to the death! %3 has fled like a cowardly dog! +#define GIVE_CONSENT 1427 //You have given %1 permission to drag your corpse in %2. +#define DENY_CONSENT 1428 //You have denied %1 permission to drag your corpse in %2. #define MEMBER_OF_YOUR_GUILD 1429 #define OFFICER_OF_YOUR_GUILD 1430 #define LEADER_OF_YOUR_GUILD 1431 diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a5180963d..4d01f792a 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1442,50 +1442,67 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_Consent: { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - Client* client = entity_list.GetClientByName(s->grantname); - if (client) { - if (s->permission == 1) - client->consent_list.push_back(s->ownername); - else - client->consent_list.remove(s->ownername); - auto outapp = - new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); - ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; - strcpy(crs->grantname, s->grantname); - strcpy(crs->ownername, s->ownername); - crs->permission = s->permission; - strcpy(crs->zonename, "all zones"); - client->QueuePacket(outapp); - safe_delete(outapp); + bool found_corpse = false; + for (auto const& it : entity_list.GetCorpseList()) { + if (it.second->IsPlayerCorpse() && strcmp(it.second->GetOwnerName(), s->ownername) == 0) { + if (s->consent_type == EQEmu::consent::Normal) { + if (s->permission == 1) { + it.second->AddConsentName(s->grantname); + } + else { + it.second->RemoveConsentName(s->grantname); + } + } + else if (s->consent_type == EQEmu::consent::Group) { + it.second->SetConsentGroupID(s->consent_id); + } + else if (s->consent_type == EQEmu::consent::Raid) { + it.second->SetConsentRaidID(s->consent_id); + } + else if (s->consent_type == EQEmu::consent::Guild) { + it.second->SetConsentGuildID(s->consent_id); + } + found_corpse = true; + } } - else { - // target not found - // Message string id's likely to be used here are: - // CONSENT_YOURSELF = 399 - // CONSENT_INVALID_NAME = 397 - // TARGET_NOT_FOUND = 101 - - auto scs_pack = - new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)scs_pack->pBuffer; - strcpy(scs->grantname, s->grantname); - strcpy(scs->ownername, s->ownername); - scs->permission = s->permission; - scs->zone_id = s->zone_id; - scs->instance_id = s->instance_id; - scs->message_string_id = TARGET_NOT_FOUND; - worldserver.SendPacket(scs_pack); - safe_delete(scs_pack); + if (found_corpse) + { + // forward the grant/deny message for this zone to both owner and granted + auto outapp = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)outapp->pBuffer; + memcpy(outapp->pBuffer, s, sizeof(ServerOP_Consent_Struct)); + if (zone) { + strn0cpy(scs->zonename, zone->GetLongName(), sizeof(scs->zonename)); + } + scs->message_string_id = s->permission ? GIVE_CONSENT : DENY_CONSENT; + worldserver.SendPacket(outapp); + safe_delete(outapp); } break; } case ServerOP_Consent_Response: { ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; - Client* client = entity_list.GetClientByName(s->ownername); - if (client) { - client->MessageString(Chat::White, s->message_string_id); + if (s->consent_type == EQEmu::consent::Normal) + { + if (Client* client = entity_list.GetClientByName(s->grantname)) + { + // send the message to the client being granted or denied permission + auto outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); + ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; + strcpy(crs->grantname, s->grantname); + strcpy(crs->ownername, s->ownername); + crs->permission = s->permission; + strn0cpy(crs->zonename, s->zonename, sizeof(crs->zonename)); + client->QueuePacket(outapp); + safe_delete(outapp); + } + } + if (Client* client = entity_list.GetClientByName(s->ownername)) + { + // send owner consent/deny confirmation message + client->MessageString(Chat::White, s->message_string_id, s->grantname, s->zonename); } break; }