Project Nebulark Part 1, So much to list, this is a rough summary
- Raid support and cross peer support for Isle of Refuge, DoF, KoS and AoM clients. - Zone Persistence added to non-instanced zones. - Commands: /whogroup, /whoraid, /raidinvite, /raid_looter, /kickfromgroup, /kickfromraid, /leaveraid, /split, /raidsay (rsay) added. - Cross peer zone and instance support - Cross tell support (along with ignore) - Cross ooc support - Cross group support (can chat, leave group, disband cross peers, update group options) - Cross who all support - houses/instances fixed no more cross objects/spawns/etc from other houses - houses now display characters name with the house zone description - 1000's of house items now properly work with wall/ceiling - debug messages removed from housing placement - Encounters locked to raid instead of group - group options restricted to raid leader - reload rules for following are peer wide: COMMAND_RELOADSTRUCTS COMMAND_RELOAD_QUESTS COMMAND_RELOAD_SPELLS COMMAND_RELOAD_ZONESCRIPTS COMMAND_RELOAD_FACTIONS COMMAND_RELOAD_MAIL COMMAND_RELOAD_GUILDS COMMAND_RELOAD_RULES COMMAND_RELOAD_STARTABILITIES COMMAND_RELOAD_VOICEOVERS COMMAND_RELOADSPAWNSCRIPTS COMMAND_RELOADREGIONSCRIPTS COMMAND_RELOADLUASYSTEM - special/static zones (always_loaded) are now defined by a peer_priority unsigned short (smallint(5)) in zones table. peer_priority = server_config world.peerpriority will spawn on that exe instance, if it is not available it is distributed to all peers. Using the value of 0 (assuming no peer has priority of 0) or 65535 will result in peer distribution of zones. server_config.json "WorldServer" block updated with the following (web peer port information), priority must be unique for EACH peer: "peeraddress": "10.1.1.2", "peerport": "9102", "peerpriority": "1", New Command Line Run Arguments for World Exe to override server_config.json values Allowed options: --worldaddress arg World address --internalworldaddress arg Internal world address --worldport arg (=0) Web world port --webworldaddress arg Web world address --webworldport arg (=0) Web world port --peerpriority arg (=0) Peer priority - fixed Isle of Refuge client group struct (raids added also) - new log category Peering - new LUA Functions AddRespawn(Zone, LocationID, RespawnTime) and CreatePersistedRespawn(LocationID, SpawnType, RespawnTime, ZoneID)
This commit is contained in:
parent
7aa35166ca
commit
315ab1d0e7
@ -1613,11 +1613,14 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="level_max" Type="int16" Size="1" />
|
||||
<Data ElementName="race_id" Type="int8" Size="1" />
|
||||
<Data ElementName="class_id" Type="int8" Size="1" />
|
||||
<Data ElementName="hp_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="hp_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="hp_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="unknown1" Type="int8" Size="1" />
|
||||
<Data ElementName="power_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="trauma_count" Type="int8" Size="1" />
|
||||
<Data ElementName="arcane_count" Type="int8" Size="1" />
|
||||
<Data ElementName="noxious_count" Type="int8" Size="1" />
|
||||
<Data ElementName="elemental_count" Type="int8" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="Substruct_GroupMember" ClientVersion="546" >
|
||||
<Data ElementName="zone_status" Type="int8" Size="1" /> <!-- 0=not filled, 1=in_zone, 2=not in zone, 3=unavailable (probably zoning)-->
|
||||
@ -1784,6 +1787,190 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="unknown5" Type="int8" Size="2" />
|
||||
<Data ElementName="CoEunknown" Type="int8" Size="9" />
|
||||
</Struct>
|
||||
<Struct Name="Substruct_RaidMember" ClientVersion="1" >
|
||||
<Data ElementName="zone_status" Type="int8" Size="1" /> <!-- 0=not filled, 1=in_zone, 2=not in zone-->
|
||||
<Data ElementName="name" Type="char" Size="16" />
|
||||
<Data ElementName="spawn_id" Type="int32" Size="1" />
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" />
|
||||
<Data ElementName="level_current" Type="int16" Size="1" />
|
||||
<Data ElementName="race_id" Type="int8" Size="1" />
|
||||
<Data ElementName="class_id" Type="int8" Size="1" />
|
||||
<Data ElementName="power_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="hp_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="hp_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="trauma_count" Type="int8" Size="1" />
|
||||
<Data ElementName="arcane_count" Type="int8" Size="1" />
|
||||
<Data ElementName="noxious_count" Type="int8" Size="1" />
|
||||
<Data ElementName="elemental_count" Type="int8" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="Substruct_RaidMember" ClientVersion="373" >
|
||||
<Data ElementName="zone_status" Type="int8" Size="1" /> <!-- 0=not filled, 1=in_zone, 2=not in zone-->
|
||||
<Data ElementName="name" Type="char" Size="16" />
|
||||
<Data ElementName="spawn_id" Type="int32" Size="1" />
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" />
|
||||
<Data ElementName="level_current" Type="int16" Size="1" />
|
||||
<Data ElementName="level_max" Type="int16" Size="1" />
|
||||
<Data ElementName="race_id" Type="int8" Size="1" />
|
||||
<Data ElementName="class_id" Type="int8" Size="1" />
|
||||
<Data ElementName="hp_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="hp_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="trauma_count" Type="int8" Size="1" /> <!-- 73 -->
|
||||
<Data ElementName="arcane_count" Type="int8" Size="1" /> <!-- 74 -->
|
||||
<Data ElementName="noxious_count" Type="int8" Size="1" /> <!-- 75 -->
|
||||
<Data ElementName="elemental_count" Type="int8" Size="1" /> <!-- 76 -->
|
||||
</Struct>
|
||||
<Struct Name="Substruct_RaidMember" ClientVersion="546" >
|
||||
<Data ElementName="zone_status" Type="int8" Size="1" /> <!-- 0=not filled, 1=in_zone, 2=not in zone--> <!-- 1 -->
|
||||
<Data ElementName="name" Type="char" Size="41" /> <!-- 42 -->
|
||||
<Data ElementName="spawn_id" Type="int32" Size="1" /> <!-- 46 -->
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" /> <!-- 50 -->
|
||||
<Data ElementName="level_current" Type="int16" Size="1" /> <!-- 52 -->
|
||||
<Data ElementName="level_max" Type="int16" Size="1" /> <!-- 54 -->
|
||||
<Data ElementName="race_id" Type="int8" Size="1" /> <!-- 55 -->
|
||||
<Data ElementName="class_id" Type="int8" Size="1" /> <!-- 56 -->
|
||||
<Data ElementName="hp_current" Type="sint32" Size="1" /> <!-- 60 -->
|
||||
<Data ElementName="hp_max" Type="sint32" Size="1" /> <!-- 64 -->
|
||||
<Data ElementName="power_current" Type="sint32" Size="1" /> <!-- 68 -->
|
||||
<Data ElementName="power_max" Type="sint32" Size="1" /> <!-- 72 -->
|
||||
<Data ElementName="trauma_count" Type="int8" Size="1" /> <!-- 73 -->
|
||||
<Data ElementName="arcane_count" Type="int8" Size="1" /> <!-- 74 -->
|
||||
<Data ElementName="noxious_count" Type="int8" Size="1" /> <!-- 75 -->
|
||||
<Data ElementName="elemental_count" Type="int8" Size="1" /> <!-- 76 -->
|
||||
<Data ElementName="zone" Type="char" Size="60" /> <!-- 136 -->
|
||||
<Data ElementName="instance" Type="int8" Size="1" /> <!-- 137 -->
|
||||
</Struct>
|
||||
<Struct Name="Substruct_RaidMember" ClientVersion="57048" >
|
||||
<Data ElementName="spawn_id" Type="int32" Size="1" />
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" />
|
||||
<Data ElementName="hp_current" Type="sint64" Size="1" />
|
||||
<Data ElementName="hp_current2" Type="sint64" Size="1" />
|
||||
<Data ElementName="hp_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="hp_max2" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_current" Type="sint32" Size="1" />
|
||||
<Data ElementName="power_max" Type="sint32" Size="1" />
|
||||
<Data ElementName="level_current" Type="int16" Size="1" />
|
||||
<Data ElementName="level_max" Type="int16" Size="1" />
|
||||
<Data ElementName="name" Type="char" Size="41" />
|
||||
<Data ElementName="zone" Type="char" Size="60" />
|
||||
<Data ElementName="instance" Type="int8" Size="1" />
|
||||
<Data ElementName="zone_status" Type="int8" Size="1" />
|
||||
<Data ElementName="unknown4" Type="int16" Size="1" />
|
||||
<Data ElementName="race_id" Type="int8" Size="1" />
|
||||
<Data ElementName="class_id" Type="int8" Size="1" />
|
||||
<Data ElementName="trauma_count" Type="int8" Size="1" />
|
||||
<Data ElementName="arcane_count" Type="int8" Size="1" />
|
||||
<Data ElementName="noxious_count" Type="int8" Size="1" />
|
||||
<Data ElementName="elemental_count" Type="int8" Size="1" />
|
||||
<Data ElementName="curse_count" Type="int8" Size="1" />
|
||||
<Data ElementName="unknown5" Type="int8" Size="3" />
|
||||
</Struct>
|
||||
<Struct Name="WS_RaidUpdate" ClientVersion="1" OpcodeName="OP_UpdateRaidMsg" >
|
||||
<Data ElementName="group_member0_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_RaidUpdate" ClientVersion="373" OpcodeName="OP_UpdateRaidMsg" >
|
||||
<Data ElementName="group_member0_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_RaidUpdate" ClientVersion="546" OpcodeName="OP_UpdateRaidMsg" >
|
||||
<Data ElementName="group_member0_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_RaidUpdate" ClientVersion="57048" OpcodeName="OP_UpdateRaidMsg" >
|
||||
<Data ElementName="group_member0_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_0" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_1" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_2" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member0_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member1_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member2_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member3_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member4_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
<Data ElementName="group_member5_3" Substruct="Substruct_RaidMember" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_CharacterSheet" ClientVersion="1" OpcodeName="OP_UpdateCharacterSheetMsg" Comment="1560 bytes">
|
||||
<Data ElementName="character_name" Type="char" Size="16" />
|
||||
<Data ElementName="race" Type="int8" Size="1" /> <!-- 0x10 -->
|
||||
@ -1917,7 +2104,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="spell_state_noaoe" Type="int8" Size="1" /> <!-- 0x4f6 -->
|
||||
<Data ElementName="current_PCA" Type="int32" Size="1" /> <!-- 0x4f7 -->
|
||||
<Data ElementName="group_members" Substruct="Substruct_GroupMember" Size="5" /> <!-- 0x4fb -->
|
||||
<Data ElementName="leader_index" Type="int32" Size="1" /> <!-- 0x5f0 -->
|
||||
<Data ElementName="group_leader_id" Type="int32" Size="1" /> <!-- 0x5f0 -->
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" /> <!-- 0x5f4 -->
|
||||
<Data ElementName="pet_name" Type="char" Size="16" /> <!-- 0x5f8 -->
|
||||
<Data ElementName="pet_hp" Type="float" Size="1" /> <!-- 0x608 -->
|
||||
@ -2071,8 +2258,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="spell_state_noaoe" Type="int8" Size="1" /> <!-- 0x4f6 -->
|
||||
<Data ElementName="unknownstuff" Type="int8" Size="25" /> <!-- 3425 -->
|
||||
<Data ElementName="group_members" Substruct="Substruct_GroupMember" Size="5" /> <!-- 3665 -->
|
||||
<Data ElementName="unknownafter" Type="int8" Size="15" /> <!-- 3425 -->
|
||||
<Data ElementName="leader_index" Type="int32" Size="1" /> <!-- 3669 -->
|
||||
<Data ElementName="group_leader_id" Type="int32" Size="1" /> <!-- 3669 -->
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" /> <!-- 3673 -->
|
||||
<Data ElementName="pet_name" Type="char" Size="16" /> <!-- 3689 -->
|
||||
<Data ElementName="pet_health_pct" Type="float" Size="1" /> <!-- 3693 -->
|
||||
@ -2298,7 +2484,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="spell_state_always_crit_heal_spell" Type="int8" Size="1" /> <!-- 4007 -->
|
||||
<Data ElementName="current_PCA" Type="int32" Size="1" /> <!-- 4008, parental control alarm -->
|
||||
<Data ElementName="group_members" Substruct="Substruct_GroupMember" Size="5" /> <!-- 4012 -->
|
||||
<Data ElementName="leader_index" Type="int32" Size="1" /> <!-- 4697 -->
|
||||
<Data ElementName="group_leader_id" Type="int32" Size="1" /> <!-- 4697 -->
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" /> <!-- 4701 -->
|
||||
<Data ElementName="pet_name" Type="char" Size="32" /> <!-- 4705 -->
|
||||
<Data ElementName="unknown6" Type="int8" Size="9" /><!-- 4745 -->
|
||||
@ -2531,7 +2717,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="rain2" Type="float" Size="1" /><!-- 4761 -->
|
||||
<Data ElementName="unknown6" Type="int8" Size="1" /> <!-- 4773 -->
|
||||
<Data ElementName="group_members" Substruct="Substruct_GroupMember" Size="5" /> <!-- 4012 -->
|
||||
<Data ElementName="leader_index" Type="int32" Size="1" /> <!-- 4697 -->
|
||||
<Data ElementName="group_leader_id" Type="int32" Size="1" /> <!-- 4697 -->
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" /> <!-- 4701 -->
|
||||
<Data ElementName="pet_name" Type="char" Size="32" /> <!-- 4705 -->
|
||||
<Data ElementName="unknown6" Type="int8" Size="9" /><!-- 4745 -->
|
||||
@ -3243,7 +3429,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="vision" Type="int32" Size="1" />
|
||||
<Data ElementName="unknown525b" Type="int8" Size="986" />
|
||||
<Data ElementName="group_members" Substruct="Substruct_GroupMember" Size="5" />
|
||||
<Data ElementName="group_leader_index" Type="int32" Size="1" />
|
||||
<Data ElementName="group_leader_id" Type="int32" Size="1" />
|
||||
<Data ElementName="unknown183" Type="int8" Size="460" />
|
||||
<Data ElementName="pet_id" Type="int32" Size="1" />
|
||||
<Data ElementName="pet_name" Type="char" Size="32" />
|
||||
@ -7830,7 +8016,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="char_account_id" Type="int32" />
|
||||
<Data ElementName="zone" Type="char" Size="80" />
|
||||
</Data>
|
||||
<Data ElementName="unknown10" Type="int8" />
|
||||
<Data ElementName="display_zone" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_WhoQueryReply" ClientVersion="373" OpcodeName="OP_WhoQueryReplyMsg" >
|
||||
<Data ElementName="account_id" Type="int32" />
|
||||
@ -7851,7 +8037,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="zone" Type="char" Size="80" />
|
||||
<Data ElementName="unknown3" Type="int8" Size="28" />
|
||||
</Data>
|
||||
<Data ElementName="unknown10" Type="int8" />
|
||||
<Data ElementName="display_zone" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_WhoQueryReply" ClientVersion="546" OpcodeName="OP_WhoQueryReplyMsg" >
|
||||
<Data ElementName="account_id" Type="int32" />
|
||||
@ -7871,7 +8057,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="zone" Type="char" Size="80" />
|
||||
<Data ElementName="unknown6" Type="int8" Size="28" />
|
||||
</Data>
|
||||
<Data ElementName="unknown10" Type="int8" />
|
||||
<Data ElementName="display_zone" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_WhoQueryReply" ClientVersion="1188" OpcodeName="OP_WhoQueryReplyMsg" >
|
||||
<Data ElementName="account_id" Type="int32" />
|
||||
@ -7892,7 +8078,7 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="guild" Type="char" Size="40" />
|
||||
<Data ElementName="unknown7" Type="int8" />
|
||||
</Data>
|
||||
<Data ElementName="unknown10" Type="int8" />
|
||||
<Data ElementName="display_zone" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_HearChat" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearChatCmd" >
|
||||
<Data ElementName="understood" Type="int8" />
|
||||
@ -11132,9 +11318,36 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="type" Type="int32" Size="1" />
|
||||
<Data ElementName="description" Type="EQ2_16Bit_String" Size="1" />
|
||||
</Struct>
|
||||
<!-- KoS, DoF and earlier clients need to be structured properly here -->
|
||||
<Struct Name="WS_GuildMembershipResponse" ClientVersion="1" OpcodeName="OP_GuildMembershipResponseMsg">
|
||||
<Data ElementName="guild_id" Type="int32" Size="1" />
|
||||
<Data ElementName="character_id_to" Type="int32" Size="1" />
|
||||
<Data ElementName="num_membersx" Type="int32" Size="1" />
|
||||
<Data ElementName="member_array" Type="Array" ArraySizeVariable="num_members" >
|
||||
<Data ElementName="character_id" Type="int32" Size="1" />
|
||||
<Data ElementName="account_id" Type="int32" Size="1" />
|
||||
<Data ElementName="name" Type="EQ2_16Bit_String" Size="1" />
|
||||
<Data ElementName="adventure_class" Type="int32" Size="1" />
|
||||
<Data ElementName="adventure_level" Type="int32" Size="1" />
|
||||
<Data ElementName="tradeskill_class" Type="int32" Size="1" />
|
||||
<Data ElementName="tradeskill_level" Type="int32" Size="1" />
|
||||
<Data ElementName="rank" Type="int32" Size="1" />
|
||||
<Data ElementName="member_flags" Type="int32" Size="1" />
|
||||
<Data ElementName="join_date" Type="int32" Size="1" />
|
||||
<Data ElementName="guild_status" Type="int32" Size="1" />
|
||||
<Data ElementName="last_login" Type="int32" Size="1" />
|
||||
<Data ElementName="recruiter_id" Type="int32" Size="1" />
|
||||
<Data ElementName="points" Type="float" Size="1" />
|
||||
<Data ElementName="zone" Type="EQ2_16Bit_String" Size="1" />
|
||||
<Data ElementName="note" Type="EQ2_16Bit_String" Size="1" />
|
||||
<Data ElementName="officer_note" Type="EQ2_16Bit_String" Size="1" />
|
||||
<Data ElementName="unique_id" Type="int32" Size="1" />
|
||||
</Data>
|
||||
<Data ElementName="unknown7" Type="int16" Size="1" />
|
||||
</Struct>
|
||||
<Struct Name="WS_GuildMembershipResponse" ClientVersion="562" OpcodeName="OP_GuildMembershipResponseMsg">
|
||||
<Data ElementName="guild_id" Type="int32" Size="1" />
|
||||
<Data ElementName="character_id_to" Type="int32" Size="1" />
|
||||
<Data ElementName="num_members" Type="int32" Size="1" />
|
||||
<Data ElementName="member_array" Type="Array" ArraySizeVariable="num_members" >
|
||||
<Data ElementName="character_id" Type="int32" Size="1" />
|
||||
|
@ -360,4 +360,12 @@
|
||||
<ConfigType Type="PACKET" Level="9" Color="Yellow" Enabled="False" Logs="3" />
|
||||
<ConfigType Type="TRACE" Level="9" Color="Yellow" Enabled="False" Logs="1" />
|
||||
</LogConfig>
|
||||
<LogConfig Category="PEERING">
|
||||
<ConfigType Type="INFO" Level="9" Color="WhiteBold" Enabled="True" Logs="3" />
|
||||
<ConfigType Type="WARNING" Level="9" Color="YellowBold" Enabled="True" Logs="3" />
|
||||
<ConfigType Type="ERROR" Level="9" Color="RedBold" Enabled="True" Logs="3" />
|
||||
<ConfigType Type="DEBUG" Level="9" Color="GreenBold" Enabled="False" Logs="3" />
|
||||
<ConfigType Type="PACKET" Level="9" Color="Yellow" Enabled="False" Logs="3" />
|
||||
<ConfigType Type="TRACE" Level="9" Color="Yellow" Enabled="False" Logs="1" />
|
||||
</LogConfig>
|
||||
</EQ2EmuLogConfigs>
|
||||
|
@ -583,6 +583,10 @@ void Commands::Command_Bot_Spawn(Client* client, Seperator* sep) {
|
||||
}
|
||||
|
||||
client->GetPlayer()->SpawnedBots[bot_id] = bot->GetID();
|
||||
|
||||
if(bot->IsNPC()) {
|
||||
((NPC*)bot)->HaltMovement();
|
||||
}
|
||||
}
|
||||
else {
|
||||
client->Message(CHANNEL_ERROR, "Error spawning bot (%u)", bot_id);
|
||||
|
@ -49,6 +49,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "../classes.h"
|
||||
#include "../Transmute.h"
|
||||
#include "../Bots/Bot.h"
|
||||
#include "../Web/PeerManager.h"
|
||||
|
||||
extern WorldDatabase database;
|
||||
extern MasterSpellList master_spell_list;
|
||||
@ -72,6 +73,7 @@ extern RuleManager rule_manager;
|
||||
extern MasterAAList master_aa_list;
|
||||
extern MasterRaceTypeList race_types_list;
|
||||
extern Classes classes;
|
||||
extern PeerManager peer_manager;
|
||||
|
||||
//devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
|
||||
#if defined(__GNUC__)
|
||||
@ -1921,6 +1923,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
world.SetReloadingSubsystem("Structs");
|
||||
configReader.ReloadStructs();
|
||||
world.RemoveReloadingSubSystem("Structs");
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -1931,6 +1934,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
client_list.ReloadQuests();
|
||||
zone_list.ReloadClientQuests();
|
||||
world.RemoveReloadingSubSystem("Quests");
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -1943,6 +1947,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading NPC Spells Lists (Note: Must Reload Spawns/Repop to reset npc spells)...");
|
||||
world.PurgeNPCSpells();
|
||||
database.LoadNPCSpells();
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler, 1);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
}
|
||||
else {
|
||||
@ -1957,6 +1962,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
world.RemoveReloadingSubSystem("Spells");
|
||||
world.PurgeNPCSpells();
|
||||
database.LoadNPCSpells();
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
}
|
||||
break;
|
||||
@ -1979,6 +1985,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
if (lua_interface)
|
||||
lua_interface->DestroyZoneScripts();
|
||||
world.RemoveReloadingSubSystem("ZoneScripts");
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -1997,18 +2004,21 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
master_faction_list.Clear();
|
||||
database.LoadFactionList();
|
||||
world.RemoveReloadingSubSystem("Factions");
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
case COMMAND_RELOAD_MAIL: {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Mail...");
|
||||
zone_list.ReloadMail();
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
case COMMAND_RELOAD_GUILDS: {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Guilds...");
|
||||
world.ReloadGuilds();
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -2022,6 +2032,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
case COMMAND_RELOAD_RULES: {
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Rules...");
|
||||
database.LoadRuleSets(true);
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -2035,6 +2046,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Starting Skills/Spells...");
|
||||
world.PurgeStartingLists();
|
||||
world.LoadStartingLists();
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -2042,6 +2054,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Voiceovers...");
|
||||
world.PurgeVoiceOvers();
|
||||
world.LoadVoiceOvers();
|
||||
peer_manager.sendPeersMessage("/reloadcommand", command->handler);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
|
||||
break;
|
||||
}
|
||||
@ -3178,8 +3191,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
case COMMAND_GROUPSAY:{
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
if(sep && sep->arg[0] && gmi)
|
||||
if(sep && sep->arg[0] && gmi) {
|
||||
world.GetGroupManager()->GroupChatMessage(gmi->group_id, client->GetPlayer(), client->GetPlayer()->GetCurrentLanguage(), sep->argplus[0]);
|
||||
peer_manager.SendPeersChannelMessage(gmi->group_id, std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_GROUP_SAY, client->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMAND_GROUPINVITE: {
|
||||
@ -3211,18 +3226,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
|
||||
int8 result = world.GetGroupManager()->Invite(client->GetPlayer(), target);
|
||||
|
||||
if (result == 0) {
|
||||
|
||||
if (target && result == 0) {
|
||||
client->Message(CHANNEL_COMMANDS, "You invite %s to group with you.", target->GetName());
|
||||
if (target_client) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_ReceiveOffer", target_client->GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("type", 1);
|
||||
packet->setDataByName("name", client->GetPlayer()->GetName());
|
||||
packet->setDataByName("unknown2", 1);
|
||||
target_client->QueuePacket(packet->serialize());
|
||||
safe_delete(packet);
|
||||
}
|
||||
client->SendReceiveOffer(target_client, 1, std::string(client->GetPlayer()->GetName()), 1);
|
||||
}
|
||||
}
|
||||
else if (result == 1)
|
||||
@ -3245,9 +3253,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
case COMMAND_GROUPDISBAND: {
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
|
||||
if (gmi) { // TODO: Leader check
|
||||
if (gmi && gmi->leader) { // TODO: Leader check..DONE! :X
|
||||
// world.GetGroupManager()->SimpleGroupMessage(gmi->group_id, "Your group has been disbanded.");
|
||||
world.GetGroupManager()->RemoveGroup(gmi->group_id);
|
||||
peer_manager.sendPeersDisbandGroup(gmi->group_id);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -3419,24 +3428,68 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
break;
|
||||
}
|
||||
case COMMAND_GROUP_ACCEPT_INVITE: {
|
||||
if((sep && sep->arg[0] && strcmp(sep->arg[0], "group") == 0) || (!sep && client->GetVersion() <= 561)) {
|
||||
int8 result = world.GetGroupManager()->AcceptInvite(client->GetPlayer());
|
||||
|
||||
if (result == 0)
|
||||
client->SimpleMessage(CHANNEL_GROUP_CHAT, "You have joined the group.");
|
||||
else if (result == 1)
|
||||
client->SimpleMessage(CHANNEL_GROUP_CHAT, "You do not have a pending invite.");
|
||||
else if (result == 2)
|
||||
client->SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - could not find leader.");
|
||||
else
|
||||
client->SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - unknown error.");
|
||||
}
|
||||
int8 result = 3;
|
||||
std::string leader = world.GetGroupManager()->HasPendingInvite(client->GetPlayer());
|
||||
std::string playerName(client->GetPlayer()->GetName());
|
||||
Client* leader_client = client->GetCurrentZone()->GetClientByName((char*)leader.c_str());
|
||||
bool group_existed = false;
|
||||
if(leader_client && leader_client->GetPlayer()->GetGroupMemberInfo()) {
|
||||
group_existed = true;
|
||||
}
|
||||
if(client->GetPlayer()->GetGroupMemberInfo() && client->GetPlayer()->GetGroupMemberInfo()->leader) {
|
||||
int8 raid_result = world.GetGroupManager()->AcceptRaidInvite(std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetGroupMemberInfo()->group_id);
|
||||
if(raid_result == 1) {
|
||||
GroupOptions options;
|
||||
if(world.GetGroupManager()->GetDefaultGroupOptions(client->GetPlayer()->GetGroupMemberInfo()->group_id, &options)) {
|
||||
std::vector<int32> raidGroups;
|
||||
world.GetGroupManager()->GetRaidGroups(client->GetPlayer()->GetGroupMemberInfo()->group_id, &raidGroups);
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, client->GetPlayer()->GetGroupMemberInfo()->group_id, "", "", &options, "", &raidGroups, true);
|
||||
}
|
||||
world.GetGroupManager()->ClearGroupRaidLooterFlag(client->GetPlayer()->GetGroupMemberInfo()->group_id);
|
||||
world.GetGroupManager()->SendGroupUpdate(client->GetPlayer()->GetGroupMemberInfo()->group_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(net.is_primary) {
|
||||
int32 group_id = 0;
|
||||
result = world.GetGroupManager()->AcceptInvite(client->GetPlayer(), &group_id, false);
|
||||
client->HandleGroupAcceptResponse(result);
|
||||
if(result == 0) {
|
||||
GroupOptions options;
|
||||
if(leader_client) {
|
||||
if(!group_existed) {
|
||||
leader_client->SetGroupOptionsReference(&options);
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, group_id, leader, playerName, &options);
|
||||
}
|
||||
|
||||
world.GetGroupManager()->AddGroupMember(group_id, leader_client->GetPlayer(), true);
|
||||
world.GetGroupManager()->GroupMessage(leader_client->GetPlayer()->GetGroupMemberInfo()->group_id, "%s has joined the group.", playerName.c_str());
|
||||
world.GetGroupManager()->AddGroupMember(leader_client->GetPlayer()->GetGroupMemberInfo()->group_id, client->GetPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(leader.size() < 1) {
|
||||
client->HandleGroupAcceptResponse(1);
|
||||
}
|
||||
else {
|
||||
Client* leader_client = client->GetCurrentZone()->GetClientByName((char*)leader.c_str());
|
||||
GroupOptions options;
|
||||
if(leader_client) {
|
||||
leader_client->SetGroupOptionsReference(&options);
|
||||
world.GetGroupManager()->AddInvite(leader_client->GetPlayer(), client->GetPlayer());
|
||||
peer_manager.sendPrimaryNewGroupRequest(leader, playerName, client->GetPlayer()->GetID(), &options);
|
||||
|
||||
}
|
||||
else {
|
||||
client->HandleGroupAcceptResponse(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMAND_GROUP_DECLINE_INVITE: {
|
||||
if(sep && sep->arg[0] && strcmp(sep->arg[0], "group") == 0) {
|
||||
world.GetGroupManager()->DeclineInvite(client->GetPlayer()); // TODO: Add message to leader
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMAND_SUMMON:{
|
||||
@ -3519,8 +3572,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
int32 zone_id = 0;
|
||||
bool listSearch = false;
|
||||
bool isInstance = false;
|
||||
bool notZoningCommand = false;
|
||||
ZoneServer* zsZone = 0;
|
||||
|
||||
ZoneChangeDetails zone_details;
|
||||
if(sep && sep->arg[0][0])
|
||||
{
|
||||
if(strncasecmp(sep->arg[0], "list", 4) == 0)
|
||||
@ -3529,6 +3583,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
else if(strncasecmp(sep->arg[0], "active", 6) == 0)
|
||||
{
|
||||
zone_list.SendZoneList(client);
|
||||
notZoningCommand = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3542,10 +3597,16 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
{
|
||||
PrintSep(sep, "ZONE LOCK");
|
||||
|
||||
if(sep->IsNumber(1))
|
||||
zsZone = zone_list.Get(atoul(sep->arg[1]), false, false, false);
|
||||
else
|
||||
zsZone = zone_list.Get(sep->arg[1], false, false, false);
|
||||
if(sep->IsNumber(1)) {
|
||||
if(zone_list.GetZone(&zone_details, atoul(sep->arg[1]), "", false, false, false, false)) {
|
||||
zsZone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(zone_list.GetZone(&zone_details, 0, std::string(sep->arg[1]), false, false, false, false)) {
|
||||
zsZone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
}
|
||||
|
||||
if( zsZone )
|
||||
{
|
||||
@ -3554,17 +3615,23 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_RED, "Zone %s is not running and cannot be locked.", sep->arg[1]);
|
||||
|
||||
notZoningCommand = true;
|
||||
break;
|
||||
}
|
||||
else if(strncasecmp(sep->arg[0], "unlock", 6) == 0)
|
||||
{
|
||||
PrintSep(sep, "ZONE UNLOCK");
|
||||
|
||||
if(sep->IsNumber(1))
|
||||
zsZone = zone_list.Get(atoul(sep->arg[1]), false, false, false);
|
||||
else
|
||||
zsZone = zone_list.Get(sep->arg[1], false, false, false);
|
||||
if(sep->IsNumber(1)) {
|
||||
if(zone_list.GetZone(&zone_details, atoul(sep->arg[1]), "", false, false, false, false)) {
|
||||
zsZone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(zone_list.GetZone(&zone_details, 0, std::string(sep->arg[1]), false, false, false, false)) {
|
||||
zsZone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
}
|
||||
|
||||
if( zsZone )
|
||||
{
|
||||
@ -3573,6 +3640,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_RED, "Zone %s is not running and cannot be unlocked.", sep->arg[1]);
|
||||
notZoningCommand = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@ -3590,15 +3658,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
if(instanceID > 0)
|
||||
{
|
||||
ZoneServer* zsInstance = zone_list.GetByInstanceID(instanceID);
|
||||
|
||||
if(zsInstance != NULL)
|
||||
{
|
||||
instanceID = zsInstance->GetInstanceID();
|
||||
zone = zsInstance->GetZoneName();
|
||||
zone_id = zsInstance->GetZoneID();
|
||||
isInstance = true;
|
||||
if(zone_list.GetZoneByInstance(&zone_details, instanceID, 0, true)) {
|
||||
instanceID = zone_details.instanceId;
|
||||
zone = zone_details.zoneName;
|
||||
zone_id = zone_details.zoneId;
|
||||
}
|
||||
isInstance = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3616,7 +3681,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
{
|
||||
client->Message(CHANNEL_COLOR_YELLOW,"Zoning to %s...", zonestr);
|
||||
if(isInstance)
|
||||
client->Zone(instanceID,true,true,false);
|
||||
client->Zone(&zone_details,(ZoneServer*)zone_details.zonePtr,true,false);
|
||||
else
|
||||
client->Zone(zonestr);
|
||||
}
|
||||
@ -3765,7 +3830,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID())
|
||||
break;
|
||||
|
||||
client->SendMoveObjectMode(spawn, 0);
|
||||
Item* item = master_item_list.GetItem(spawn->GetPickupItemID());
|
||||
|
||||
client->SendMoveObjectMode(spawn, (item && item->houseitem_info) ? item->houseitem_info->house_location : 0);
|
||||
break;
|
||||
}
|
||||
case COMMAND_PICKUP:
|
||||
@ -5710,6 +5777,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
case COMMAND_ASSIST: { Command_Assist(client, sep); break; }
|
||||
case COMMAND_TARGET: { Command_Target(client, sep); break; }
|
||||
case COMMAND_TARGET_PET: { Command_Target_Pet(client, sep); break; }
|
||||
case COMMAND_WHOGROUP: { Command_WhoGroup(client, sep); break; }
|
||||
case COMMAND_WHORAID: { Command_WhoRaid(client, sep); break; }
|
||||
case COMMAND_RAIDINVITE: { Command_RaidInvite(client, sep); break; }
|
||||
case COMMAND_RAID_LOOTER: { Command_Raid_Looter(client, sep); break; }
|
||||
case COMMAND_KICKFROMGROUP: { Command_KickFromGroup(client, sep); break; }
|
||||
case COMMAND_KICKFROMRAID: { Command_KickFromRaid(client, sep); break; }
|
||||
case COMMAND_LEAVERAID: { Command_LeaveRaid(client, sep); break; }
|
||||
case COMMAND_SPLIT: { Command_Split(client, sep); break; }
|
||||
case COMMAND_RAIDSAY: { Command_RaidSay(client, sep); break; }
|
||||
default:
|
||||
{
|
||||
LogWrite(COMMAND__WARNING, 0, "Command", "Unhandled command: %s", command->command.data.c_str());
|
||||
@ -6263,12 +6339,20 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
|
||||
|
||||
if (strncmp(command, "rank_name", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && guild)
|
||||
guild->SetRankName(atoi(sep->arg[1]), sep->argplus[2]);
|
||||
else if (strncmp(command, "rank_permission", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild)
|
||||
else if (strncmp(command, "rank_permission", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild) {
|
||||
guild->SetPermission(atoi(sep->arg[1]), atoi(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
|
||||
else if (strncmp(command, "filter_event", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild)
|
||||
peer_manager.sendPeersGuildPermission(guild->GetID(), atoul(sep->arg[1]), atoul(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
|
||||
}
|
||||
else if (strncmp(command, "filter_event", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild) {
|
||||
guild->SetEventFilter(atoi(sep->arg[1]), atoi(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
|
||||
else if (strncmp(command, "kick", length) == 0 && sep->arg[1] && guild)
|
||||
guild->KickGuildMember(client, sep->arg[1]);
|
||||
peer_manager.sendPeersGuildEventFilter(guild->GetID(), atoul(sep->arg[1]), atoul(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
|
||||
}
|
||||
else if (strncmp(command, "kick", length) == 0 && sep->arg[1] && guild) {
|
||||
int32 character_id = guild->KickGuildMember(client, sep->arg[1]);
|
||||
if(character_id > 0) {
|
||||
peer_manager.sendPeersRemoveGuildMember(character_id, guild->GetID(), std::string(client->GetPlayer()->GetName()));
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "demote", length) == 0 && sep->arg[1] && guild)
|
||||
guild->DemoteGuildMember(client, sep->arg[1]);
|
||||
else if (strncmp(command, "promote", length) == 0 && sep->arg[1] && guild)
|
||||
@ -6381,9 +6465,19 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
|
||||
else if (strncmp(command, "create", length) == 0 && sep->arg[1])
|
||||
{
|
||||
const char* guild_name = sep->argplus[1];
|
||||
|
||||
if (!guild_list.GetGuild(guild_name))
|
||||
world.CreateGuild(guild_name, client, client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
|
||||
if(!guild_name || strlen(guild_name) < 4) {
|
||||
client->SimpleMessage(CHANNEL_NARRATIVE, "Guild name is too short.");
|
||||
}
|
||||
else if (!guild_list.GetGuild(guild_name)) {
|
||||
if(net.is_primary) {
|
||||
int32 guildID = world.CreateGuild(guild_name, client, client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
|
||||
if(guildID > 0)
|
||||
peer_manager.sendPeersCreateGuild(guildID);
|
||||
}
|
||||
else {
|
||||
peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), std::string(client->GetPlayer()->GetName()));
|
||||
}
|
||||
}
|
||||
else
|
||||
client->SimpleMessage(CHANNEL_NARRATIVE, "A guild with that name already exists.");
|
||||
}
|
||||
@ -6479,8 +6573,11 @@ void Commands::Command_GuildSay(Client* client, Seperator* sep)
|
||||
|
||||
if (guild)
|
||||
{
|
||||
if (sep && sep->arg[0])
|
||||
guild->HandleGuildSay(client, sep->argplus[0]);
|
||||
if (sep && sep->arg[0]) {
|
||||
bool success = guild->HandleGuildSay(client, sep->argplus[0]);
|
||||
if(success)
|
||||
peer_manager.SendPeersGuildChannelMessage(guild->GetID(), std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_GUILD_SAY, client->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
}
|
||||
else
|
||||
client->SimpleMessage(CHANNEL_NARRATIVE, "You are not a member of a guild");
|
||||
@ -6497,8 +6594,11 @@ void Commands::Command_OfficerSay(Client* client, Seperator* sep)
|
||||
|
||||
if (guild)
|
||||
{
|
||||
if (sep && sep->arg[0])
|
||||
guild->HandleOfficerSay(client, sep->argplus[0]);
|
||||
if (sep && sep->arg[0]) {
|
||||
bool success = guild->HandleOfficerSay(client, sep->argplus[0]);
|
||||
if(success)
|
||||
peer_manager.SendPeersGuildChannelMessage(guild->GetID(), std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_OFFICER_SAY, client->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
}
|
||||
else
|
||||
client->SimpleMessage(CHANNEL_NARRATIVE, "You are not a member of a guild");
|
||||
@ -6581,8 +6681,10 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
|
||||
if (sep && sep->arg[0])
|
||||
{
|
||||
const char* guild_name = sep->arg[0];
|
||||
|
||||
if (!guild_list.GetGuild(guild_name))
|
||||
if(!guild_name || strlen(guild_name) < 4) {
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Guild name is too short.");
|
||||
}
|
||||
else if (!guild_list.GetGuild(guild_name))
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
@ -6592,7 +6694,14 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
|
||||
|
||||
if (to_client)
|
||||
{
|
||||
world.CreateGuild(guild_name, to_client);
|
||||
if(net.is_primary) {
|
||||
int32 guildID = world.CreateGuild(guild_name, to_client, to_client->GetPlayer()->GetGroupMemberInfo() ? to_client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
|
||||
if(guildID > 0)
|
||||
peer_manager.sendPeersCreateGuild(guildID);
|
||||
}
|
||||
else {
|
||||
peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), std::string(to_client->GetPlayer()->GetName()));
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
@ -6604,13 +6713,27 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
|
||||
|
||||
if (to_client)
|
||||
{
|
||||
world.CreateGuild(guild_name, to_client);
|
||||
if(net.is_primary) {
|
||||
int32 guildID = world.CreateGuild(guild_name, to_client, to_client->GetPlayer()->GetGroupMemberInfo() ? to_client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
|
||||
if(guildID > 0)
|
||||
peer_manager.sendPeersCreateGuild(guildID);
|
||||
}
|
||||
else {
|
||||
peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), std::string(to_client->GetPlayer()->GetName()));
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
world.CreateGuild(guild_name);
|
||||
if(net.is_primary) {
|
||||
int32 guildID = world.CreateGuild(guild_name);
|
||||
if(guildID > 0)
|
||||
peer_manager.sendPeersCreateGuild(guildID);
|
||||
}
|
||||
else {
|
||||
peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), "");
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
|
||||
@ -6734,29 +6857,21 @@ void Commands::Command_GuildsRemove(Client* client, Seperator* sep)
|
||||
if (found)
|
||||
{
|
||||
Client* to_client = 0;
|
||||
|
||||
if (sep->arg[1] && strlen(sep->arg[1]) > 0)
|
||||
to_client = zone_list.GetClientByCharName(string(sep->arg[1]));
|
||||
char* charName = nullptr;
|
||||
if(sep->arg[1][0])
|
||||
charName = sep->arg[1];
|
||||
else if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsPlayer())
|
||||
to_client = ((Player*)client->GetPlayer()->GetTarget())->GetClient();
|
||||
|
||||
if (to_client)
|
||||
{
|
||||
Player* to_player = to_client->GetPlayer();
|
||||
if (to_player->GetGuild())
|
||||
{
|
||||
if (to_player->GetGuild() == guild)
|
||||
{
|
||||
guild->KickGuildMember(client, to_player->GetName());
|
||||
}
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "%s is not in the guild '%s'.", to_player->GetName(), guild->GetName());
|
||||
}
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "%s is not in a guild.", to_player->GetName());
|
||||
}
|
||||
charName = ((Player*)client->GetPlayer()->GetTarget())->GetName();
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Could not find player '%s' to invite to the guild.", sep->arg[1]);
|
||||
{
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Missing player name or not a valid target to remove from the guild.");
|
||||
return;
|
||||
}
|
||||
int32 character_id = guild->KickGuildMember(client, charName);
|
||||
if(character_id > 0)
|
||||
peer_manager.sendPeersRemoveGuildMember(character_id, guild->GetID(), std::string(client->GetPlayer()->GetName()));
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Could not find player '%s' to remove from the guild.", charName);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -10775,6 +10890,31 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
|
||||
client->QueuePacket(packet->serialize());
|
||||
safe_delete(packet);
|
||||
}
|
||||
else if(atoi(sep->arg[0]) == 35) {
|
||||
if(client->GetPlayer()->GetGroupMemberInfo() && client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity()) {
|
||||
Entity* target = (Entity*)client->GetPlayer()->GetTarget();
|
||||
if(target->GetGroupMemberInfo()) {
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(client->GetPlayer()->GetGroupMemberInfo()->group_id);
|
||||
PlayerGroup* group2 = world.GetGroupManager()->GetGroup(target->GetGroupMemberInfo()->group_id);
|
||||
if(group && group2) {
|
||||
group->AddGroupToRaid(group2->GetID());
|
||||
group2->AddGroupToRaid(group->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(atoi(sep->arg[0]) == 36) {
|
||||
EQ2Packet* packet = client->GetPlayer()->GetRaidUpdatePacket(client->GetVersion());
|
||||
if(packet) {
|
||||
client->QueuePacket(packet);
|
||||
}
|
||||
}
|
||||
else if(atoi(sep->arg[0]) == 37) {
|
||||
Guild* guild = client->GetPlayer()->GetGuild();
|
||||
if(guild)
|
||||
guild->SendGuildMemberList();
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_ExaminePartialSpellInfo", client->GetVersion());
|
||||
@ -11093,7 +11233,10 @@ void Commands::Command_ZoneSafeCoords(Client *client, Seperator *sep)
|
||||
|
||||
if (zone_id > 0)
|
||||
{
|
||||
zone = zone_list.Get(zone_id, false, false, false);
|
||||
ZoneChangeDetails zone_details;
|
||||
if(zone_list.GetZone(&zone_details, zone_id, "", false, false, false)) {
|
||||
zone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
if (zone)
|
||||
{
|
||||
zone->SetSafeX(client->GetPlayer()->GetX());
|
||||
@ -11162,18 +11305,21 @@ void Commands::Command_ZoneSet(Client* client, Seperator* sep)
|
||||
{
|
||||
ZoneServer* zone = 0;
|
||||
int32 zone_id = 0;
|
||||
|
||||
ZoneChangeDetails zone_details;
|
||||
if (sep->IsNumber(0) && atoi(sep->arg[0]) > 0)
|
||||
{
|
||||
zone_id = atoi(sep->arg[0]);
|
||||
zone = zone_list.Get(atoi(sep->arg[0]), false, false, false);
|
||||
zone_id = atoul(sep->arg[0]);
|
||||
if(zone_list.GetZone(&zone_details, zone_id, "", false, false, false, false)) {
|
||||
zone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
zone_id = database.GetZoneID(sep->arg[0]);
|
||||
|
||||
if (zone_id > 0)
|
||||
zone = zone_list.Get(sep->arg[0], false, false, false);
|
||||
if(zone_list.GetZone(&zone_details, zone_id, "", false, false, false, false)) {
|
||||
zone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
}
|
||||
|
||||
if (zone_id > 0)
|
||||
@ -12379,3 +12525,245 @@ void Commands::Command_Target_Pet(Client* client, Seperator* sep) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Function: Command_WhoGroup()
|
||||
Purpose : Lists all members of current group
|
||||
Example : /whogroup
|
||||
*/
|
||||
void Commands::Command_WhoGroup(Client* client, Seperator* sep) {
|
||||
Entity* player = (Entity*)client->GetPlayer();
|
||||
if(player->GetGroupMemberInfo()) {
|
||||
world.GetGroupManager()->SendWhoGroupMembers(client, player->GetGroupMemberInfo()->group_id);
|
||||
}
|
||||
else {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You are not currently in a group.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_WhoRaid()
|
||||
Purpose : Lists all members of raid
|
||||
Example : /whoraid
|
||||
*/
|
||||
void Commands::Command_WhoRaid(Client* client, Seperator* sep) {
|
||||
Entity* player = (Entity*)client->GetPlayer();
|
||||
if(player->GetGroupMemberInfo()) {
|
||||
world.GetGroupManager()->SendWhoRaidMembers(client, player->GetGroupMemberInfo()->group_id);
|
||||
}
|
||||
else {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You are not currently in a group or raid.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_RaidInvite()
|
||||
Purpose : Invites a group to the raid
|
||||
Example : /raidinvite
|
||||
*/
|
||||
void Commands::Command_RaidInvite(Client* client, Seperator* sep) {
|
||||
Entity* target = nullptr;
|
||||
if( sep && sep->arg[0] ) {
|
||||
Client* target_client = zone_list.GetClientByCharName(sep->arg[0]);
|
||||
if(target_client)
|
||||
target = (Entity*)target_client->GetPlayer();
|
||||
}
|
||||
if(!target) {
|
||||
if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
|
||||
target = (Entity*)client->GetPlayer()->GetTarget();
|
||||
}
|
||||
world.GetGroupManager()->SendRaidInvite(client, target);
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_Raid_Looter()
|
||||
Purpose : Adds a looter to the raid loot list
|
||||
Example : /raid_looter <name>
|
||||
*/
|
||||
void Commands::Command_Raid_Looter(Client* client, Seperator* sep) {
|
||||
if(!client->GetPlayer()->GetGroupMemberInfo() || client->GetPlayer()->GetGroupMemberInfo()->leader)
|
||||
return;
|
||||
Entity* target = nullptr;
|
||||
if( sep && sep->arg[0] ) {
|
||||
Client* target_client = zone_list.GetClientByCharName(sep->arg[0]);
|
||||
if(target_client)
|
||||
target = (Entity*)target_client->GetPlayer();
|
||||
}
|
||||
if(!target) {
|
||||
if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
|
||||
target = (Entity*)client->GetPlayer()->GetTarget();
|
||||
}
|
||||
|
||||
bool isLeaderRaid = world.GetGroupManager()->IsInRaidGroup(client->GetPlayer()->GetGroupMemberInfo()->group_id, client->GetPlayer()->GetGroupMemberInfo()->group_id, true);
|
||||
if(isLeaderRaid && target && target->IsEntity()) {
|
||||
if(((Entity*)target)->GetGroupMemberInfo() && world.GetGroupManager()->IsInRaidGroup(client->GetPlayer()->GetGroupMemberInfo()->group_id, ((Entity*)target)->GetGroupMemberInfo()->group_id, false)) {
|
||||
if(((Entity*)target)->GetGroupMemberInfo()->is_raid_looter) {
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "%s removed as a raid looter.", target->GetName());
|
||||
((Entity*)target)->GetGroupMemberInfo()->is_raid_looter = false;
|
||||
}
|
||||
else {
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "%s added as a raid looter.", target->GetName());
|
||||
((Entity*)target)->GetGroupMemberInfo()->is_raid_looter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_KickFromGroup()
|
||||
Purpose : Kick a player from a group
|
||||
Example : /kickfromgroup <name>
|
||||
*/
|
||||
void Commands::Command_KickFromGroup(Client* client, Seperator* sep) {
|
||||
Entity* target = nullptr;
|
||||
Client* target_client = nullptr;
|
||||
if( sep && sep->arg[0] ) {
|
||||
target_client = zone_list.GetClientByCharName(sep->arg[0]);
|
||||
if(target_client) {
|
||||
target = target_client->GetPlayer();
|
||||
}
|
||||
}
|
||||
if(!target) {
|
||||
if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
|
||||
target = (Entity*)client->GetPlayer()->GetTarget();
|
||||
|
||||
if(target && target->IsPlayer())
|
||||
target_client = ((Player*)target)->GetClient();
|
||||
}
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
|
||||
if (gmi && gmi->leader && target && target->GetGroupMemberInfo() && gmi->group_id == target->GetGroupMemberInfo()->group_id) {
|
||||
int32 group_id = gmi->group_id;
|
||||
world.GetGroupManager()->RemoveGroupMember(group_id, target);
|
||||
if (!world.GetGroupManager()->IsGroupIDValid(group_id)) {
|
||||
// leader->Message(CHANNEL_COLOR_GROUP, "%s has left the group.", client->GetPlayer()->GetName());
|
||||
}
|
||||
else {
|
||||
world.GetGroupManager()->GroupMessage(group_id, "%s has been removed from the group.", target->GetName());
|
||||
}
|
||||
|
||||
if(target_client)
|
||||
target_client->SimpleMessage(CHANNEL_GROUP_CHAT, "You have been kicked from the group");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_KickFromRaid()
|
||||
Purpose : Kick a group from a raid
|
||||
Example : /kickfromraid <name>
|
||||
*/
|
||||
void Commands::Command_KickFromRaid(Client* client, Seperator* sep) {
|
||||
Entity* target = nullptr;
|
||||
Client* target_client = nullptr;
|
||||
if( sep && sep->arg[0] ) {
|
||||
target_client = zone_list.GetClientByCharName(sep->arg[0]);
|
||||
if(target_client) {
|
||||
target = target_client->GetPlayer();
|
||||
}
|
||||
}
|
||||
if(!target) {
|
||||
if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
|
||||
target = (Entity*)client->GetPlayer()->GetTarget();
|
||||
|
||||
if(target && target->IsPlayer())
|
||||
target_client = ((Player*)target)->GetClient();
|
||||
}
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
if(gmi && gmi->leader && target && target->GetGroupMemberInfo() && world.GetGroupManager()->IsInRaidGroup(gmi->group_id, target->GetGroupMemberInfo()->group_id, false) &&
|
||||
world.GetGroupManager()->IsInRaidGroup(gmi->group_id, gmi->group_id, true)) {
|
||||
GroupOptions goptions;
|
||||
world.GetGroupManager()->GetDefaultGroupOptions(gmi->group_id, &goptions);
|
||||
world.GetGroupManager()->RemoveGroupFromRaid(gmi->group_id, target->GetGroupMemberInfo()->group_id);
|
||||
std::vector<int32> raidGroups;
|
||||
world.GetGroupManager()->GetRaidGroups(gmi->group_id, &raidGroups);
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, gmi->group_id, "", "", &goptions, "", &raidGroups, true);
|
||||
std::vector<int32> emptyRaid;
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, target->GetGroupMemberInfo()->group_id, "", "", &goptions, "", &emptyRaid, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_LeaveRaid()
|
||||
Purpose : Leave a raid
|
||||
Example : /leaveraid
|
||||
*/
|
||||
void Commands::Command_LeaveRaid(Client* client, Seperator* sep) {
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
int32 orig_group_id = 0;
|
||||
if(gmi && gmi->leader && world.GetGroupManager()->IsInRaidGroup(gmi->group_id, gmi->group_id, false)) {
|
||||
orig_group_id = gmi->group_id;
|
||||
GroupOptions goptions;
|
||||
world.GetGroupManager()->GetDefaultGroupOptions(gmi->group_id, &goptions);
|
||||
std::vector<int32> raidGroups;
|
||||
world.GetGroupManager()->GetRaidGroups(gmi->group_id, &raidGroups);
|
||||
std::vector<int32>::iterator cur_group_itr = std::find(raidGroups.begin(), raidGroups.end(), gmi->group_id);
|
||||
if(cur_group_itr != raidGroups.end())
|
||||
raidGroups.erase(cur_group_itr);
|
||||
|
||||
bool sendEmpty = false;
|
||||
std::vector<int32> emptyRaid;
|
||||
if(raidGroups.size() < 2) {
|
||||
sendEmpty = true;
|
||||
}
|
||||
|
||||
for(cur_group_itr = raidGroups.begin(); cur_group_itr != raidGroups.end(); cur_group_itr++) {
|
||||
if(sendEmpty) {
|
||||
world.GetGroupManager()->ClearGroupRaid((*cur_group_itr));
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, (*cur_group_itr), "", "", &goptions, "", &emptyRaid, true);
|
||||
world.GetGroupManager()->SendGroupUpdate((*cur_group_itr), nullptr, true);
|
||||
}
|
||||
else {
|
||||
world.GetGroupManager()->ReplaceRaidGroups((*cur_group_itr), &raidGroups);
|
||||
}
|
||||
}
|
||||
|
||||
if(!sendEmpty) {
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, orig_group_id, "", "", &goptions, "", &raidGroups, true);
|
||||
}
|
||||
|
||||
world.GetGroupManager()->ClearGroupRaid(orig_group_id);
|
||||
world.GetGroupManager()->SendGroupUpdate(orig_group_id, nullptr, true);
|
||||
|
||||
peer_manager.sendPeersNewGroupRequest("", 0, orig_group_id, "", "", &goptions, "", &emptyRaid, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_Split()
|
||||
Purpose : split coin to group
|
||||
Example : /split {plat} {gold} {silver} {copper}
|
||||
*/
|
||||
void Commands::Command_Split(Client* client, Seperator* sep) {
|
||||
// int32 item_id = atoul(sep->arg[0,1,2,3]);
|
||||
int32 plat = 0, gold = 0, silver = 0, copper = 0;
|
||||
if(sep->IsNumber(0))
|
||||
plat = atoul(sep->arg[0]);
|
||||
if(sep->IsNumber(1))
|
||||
gold = atoul(sep->arg[1]);
|
||||
if(sep->IsNumber(2))
|
||||
silver = atoul(sep->arg[2]);
|
||||
if(sep->IsNumber(3))
|
||||
copper = atoul(sep->arg[3]);
|
||||
|
||||
world.GetGroupManager()->SplitWithGroupOrRaid(client, plat, gold, silver, copper);
|
||||
}
|
||||
|
||||
/*
|
||||
Function: Command_RaidSay()
|
||||
Purpose : Speak to raid members
|
||||
Example : /raidsay {message}, /rsay {message}
|
||||
*/
|
||||
void Commands::Command_RaidSay(Client* client, Seperator* sep) {
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
if(sep && sep->arg[0] && gmi) {
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
int32 spawn_group_id = gmi->group_id;
|
||||
PlayerGroup* spawn_group = world.GetGroupManager()->GetGroup(spawn_group_id);
|
||||
bool israidgroup = (spawn_group && spawn_group->IsGroupRaid());
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
if(israidgroup) {
|
||||
world.GetGroupManager()->GroupChatMessage(gmi->group_id, client->GetPlayer(), client->GetPlayer()->GetCurrentLanguage(), sep->argplus[0], CHANNEL_RAID_SAY);
|
||||
peer_manager.SendPeersChannelMessage(gmi->group_id, std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_RAID_SAY, client->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
}
|
||||
}
|
@ -454,6 +454,16 @@ public:
|
||||
void Command_Assist(Client* client, Seperator* sep);
|
||||
void Command_Target(Client* client, Seperator* sep);
|
||||
void Command_Target_Pet(Client* client, Seperator* sep);
|
||||
|
||||
void Command_WhoGroup(Client* client, Seperator* sep);
|
||||
void Command_WhoRaid(Client* client, Seperator* sep);
|
||||
void Command_RaidInvite(Client* client, Seperator* sep);
|
||||
void Command_Raid_Looter(Client* client, Seperator* sep);
|
||||
void Command_KickFromGroup(Client* client, Seperator* sep);
|
||||
void Command_KickFromRaid(Client* client, Seperator* sep);
|
||||
void Command_LeaveRaid(Client* client, Seperator* sep);
|
||||
void Command_Split(Client* client, Seperator* sep);
|
||||
void Command_RaidSay(Client* client, Seperator* sep);
|
||||
|
||||
// AA Commands
|
||||
void Get_AA_Xml(Client* client, Seperator* sep);
|
||||
@ -949,6 +959,16 @@ private:
|
||||
|
||||
#define COMMAND_SET_CONSUME_FOOD 538
|
||||
|
||||
#define COMMAND_WHOGROUP 539 // /whogroup /whog
|
||||
#define COMMAND_WHORAID 540 // /whoraid /whor
|
||||
#define COMMAND_RAIDINVITE 541 // /raidinvite
|
||||
#define COMMAND_RAID_LOOTER 542 // /raid_looter
|
||||
#define COMMAND_KICKFROMGROUP 543 // /kickfromgroup
|
||||
#define COMMAND_KICKFROMRAID 544 // /kickfromraid
|
||||
#define COMMAND_LEAVERAID 545 // /leaveraid
|
||||
#define COMMAND_SPLIT 546 // /split
|
||||
#define COMMAND_RAIDSAY 547 // /raidsay /rsay
|
||||
|
||||
#define GET_AA_XML 750
|
||||
#define ADD_AA 751
|
||||
#define COMMIT_AA_PROFILE 752
|
||||
|
@ -359,14 +359,14 @@ bool ConsoleZoneCommand(Seperator *sep)
|
||||
{
|
||||
if( sep->IsNumber(2) )
|
||||
{
|
||||
zone = zone_list.Get(atoi(sep->arg[2]), false, false, false);
|
||||
if( zone )
|
||||
ZoneChangeDetails zone_details;
|
||||
if( zone_list.GetZone(&zone_details, atoi(sep->arg[2]), "", false, false, false) )
|
||||
{
|
||||
printf("> Zone status for zone ID %i...\n", atoi(sep->arg[2]));
|
||||
printf("============================================================================================\n");
|
||||
printf("| %30s | %10s | %42s |\n", "Zone", "Param", "Value");
|
||||
printf("============================================================================================\n");
|
||||
printf("| %30s | %10s | %42s |\n", zone->GetZoneName(), "locked", zone->GetZoneLockState() ? "true" : "false");
|
||||
printf("| %30s | %10s | %42s |\n", zone_details.zoneName, "locked", zone_details.lockState ? "true" : "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -29,12 +29,14 @@
|
||||
#include "../WorldDatabase.h"
|
||||
#include "../../common/Log.h"
|
||||
#include "../Rules/Rules.h"
|
||||
#include "../Web/PeerManager.h"
|
||||
|
||||
extern ConfigReader configReader;
|
||||
extern ZoneList zone_list;
|
||||
extern WorldDatabase database;
|
||||
extern World world;
|
||||
extern RuleManager rule_manager;
|
||||
extern PeerManager peer_manager;
|
||||
|
||||
/***************************************************************************************************************************************************
|
||||
* GUILD
|
||||
@ -275,7 +277,7 @@ int8 Guild::GetRecruitingDescTag(int8 index) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Guild::SetPermission(int8 rank, int8 permission, int8 value, bool send_packet) {
|
||||
bool Guild::SetPermission(int8 rank, int8 permission, int8 value, bool send_packet, bool save_needed) {
|
||||
|
||||
bool ret = false;
|
||||
if (value == 0 || value == 1) {
|
||||
@ -287,7 +289,8 @@ bool Guild::SetPermission(int8 rank, int8 permission, int8 value, bool send_pack
|
||||
if (ret && send_packet) {
|
||||
LogWrite(GUILD__DEBUG, 1, "Guilds", "Set Guild Permissions - Rank: %i, Permission: %i, Value: %i", rank, permission, value);
|
||||
SendGuildUpdate();
|
||||
ranks_save_needed = true;
|
||||
if(save_needed)
|
||||
ranks_save_needed = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -301,7 +304,7 @@ int8 Guild::GetPermission(int8 rank, int8 permission) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Guild::SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet) {
|
||||
bool Guild::SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet, bool save_needed) {
|
||||
|
||||
bool ret = false;
|
||||
if ((category == GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY || category == GUILD_EVENT_FILTER_CATEGORY_BROADCAST) && (value == 0 || value == 1)) {
|
||||
@ -313,7 +316,8 @@ bool Guild::SetEventFilter(int8 event_id, int8 category, int8 value, bool send_p
|
||||
if (ret && send_packet) {
|
||||
LogWrite(GUILD__DEBUG, 1, "Guilds", "Set Guild Event Filter - EventID: %i, Category: %i, Value: %i", event_id, category, value);
|
||||
SendGuildUpdate();
|
||||
event_filters_save_needed = true;
|
||||
if(save_needed)
|
||||
event_filters_save_needed = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -712,6 +716,36 @@ bool Guild::AddNewGuildMember(Client *client, const char *invited_by, int8 rank)
|
||||
}
|
||||
|
||||
member_save_needed = true;
|
||||
|
||||
peer_manager.sendPeersAddGuildMember(gm->character_id, GetID(), (invited_by != nullptr) ? std::string(invited_by) : "", gm->join_date, rank);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Guild::AddNewGuildMember(int32 characterID, const char *invited_by, int32 join_timestamp, int8 rank) {
|
||||
GuildMember *gm;
|
||||
|
||||
if (members.count(characterID) == 0) {
|
||||
gm = new GuildMember;
|
||||
bool foundMember = peer_manager.GetClientGuildDetails(characterID, gm);
|
||||
if(!foundMember) {
|
||||
LogWrite(GUILD__ERROR, 0, "Guilds", "FAILED TO FIND MEMBER: %s invited %s to join guild: %s", invited_by, gm->name, GetName());
|
||||
safe_delete(gm);
|
||||
return false;
|
||||
}
|
||||
gm->rank = rank;
|
||||
gm->join_date = join_timestamp;
|
||||
gm->last_login_date = gm->join_date;
|
||||
mMembers.writelock(__FUNCTION__, __LINE__);
|
||||
members[characterID] = gm;
|
||||
mMembers.releasewritelock(__FUNCTION__, __LINE__);
|
||||
|
||||
if (invited_by) {
|
||||
AddNewGuildEvent(GUILD_EVENT_MEMBER_JOINS, "%s has accepted %s's invitation to join %s.", Timer::GetUnixTimeStamp(), true, gm->name, invited_by, GetName());
|
||||
SendMessageToGuild(GUILD_EVENT_MEMBER_JOINS, "%s has accepted %s's invitation to join %s.", gm->name, invited_by, GetName());
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "%s invited %s to join guild: %s", invited_by, gm->name, GetName());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -848,20 +882,21 @@ bool Guild::PromoteGuildMember(Client *client, const char *name, bool send_packe
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Guild::KickGuildMember(Client *client, const char *name, bool send_packet) {
|
||||
int32 Guild::KickGuildMember(Client *client, const char *name, bool send_packet) {
|
||||
|
||||
GuildMember *gm;
|
||||
Client *kicked_client;
|
||||
const char *kicker_name;
|
||||
|
||||
int32 character_id = 0;
|
||||
assert(client);
|
||||
assert(name);
|
||||
|
||||
if (!(gm = GetGuildMember(name)))
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
kicker_name = client->GetPlayer()->GetName();
|
||||
|
||||
character_id = gm->character_id;
|
||||
|
||||
if (!strncmp(kicker_name, gm->name, sizeof(gm->name))) {
|
||||
AddNewGuildEvent(GUILD_EVENT_MEMBER_LEAVES, "%s left the guild.", Timer::GetUnixTimeStamp(), true, gm->name);
|
||||
SendMessageToGuild(GUILD_EVENT_MEMBER_LEAVES, "%s left the guild.", gm->name);
|
||||
@ -897,7 +932,7 @@ bool Guild::KickGuildMember(Client *client, const char *name, bool send_packet)
|
||||
safe_delete_array(gm->recruiter_picture_data);
|
||||
safe_delete(gm);
|
||||
|
||||
return true;
|
||||
return character_id;
|
||||
}
|
||||
|
||||
bool Guild::InvitePlayer(Client *client, const char *name, bool send_packet) {
|
||||
@ -1853,7 +1888,10 @@ void Guild::SendGuildMemberList(Client* client) {
|
||||
}
|
||||
mMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
//DumpPacket(packet->serialize());
|
||||
client->QueuePacket(packet->serialize());
|
||||
//packet->PrintPacket();
|
||||
EQ2Packet* pack = packet->serialize();
|
||||
//DumpPacket(pack);
|
||||
client->QueuePacket(pack);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
@ -2179,7 +2217,7 @@ void Guild::SendGuildRecruiterInfo(Client* client, Player* player) {
|
||||
}
|
||||
}
|
||||
|
||||
void Guild::HandleGuildSay(Client* sender, const char* message) {
|
||||
bool Guild::HandleGuildSay(Client* sender, const char* message) {
|
||||
|
||||
map<int32, GuildMember *>::iterator itr;
|
||||
GuildMember *gm;
|
||||
@ -2189,11 +2227,11 @@ void Guild::HandleGuildSay(Client* sender, const char* message) {
|
||||
assert(message);
|
||||
|
||||
if (!(gm = GetGuildMemberOnline(sender)))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_SPEAK_IN_GUILD_CHAT)) {
|
||||
sender->SimpleMessage(CHANNEL_NARRATIVE, "You do not have permission to speak in guild chat.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMembers.readlock(__FUNCTION__, __LINE__);
|
||||
@ -2206,9 +2244,30 @@ void Guild::HandleGuildSay(Client* sender, const char* message) {
|
||||
}
|
||||
mMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild Say");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Guild::HandleOfficerSay(Client* sender, const char* message) {
|
||||
void Guild::HandleGuildSay(std::string senderName, const char* message, int8 language) {
|
||||
|
||||
map<int32, GuildMember *>::iterator itr;
|
||||
GuildMember *gm;
|
||||
|
||||
assert(message);
|
||||
Client* client = 0;
|
||||
mMembers.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = members.begin(); itr != members.end(); itr++) {
|
||||
if (!(client = zone_list.GetClientByCharID(itr->second->character_id)))
|
||||
continue;
|
||||
|
||||
if (permissions.Get(itr->second->rank)->Get(GUILD_PERMISSIONS_SEE_GUILD_CHAT))
|
||||
client->GetCurrentZone()->HandleChatMessage(senderName, client->GetPlayer()->GetName(), CHANNEL_GUILD_SAY, message, 0, 0, language);
|
||||
}
|
||||
mMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild Say");
|
||||
}
|
||||
|
||||
bool Guild::HandleOfficerSay(Client* sender, const char* message) {
|
||||
|
||||
map<int32, GuildMember *>::iterator itr;
|
||||
GuildMember *gm;
|
||||
@ -2218,11 +2277,11 @@ void Guild::HandleOfficerSay(Client* sender, const char* message) {
|
||||
assert(message);
|
||||
|
||||
if (!(gm = GetGuildMemberOnline(sender)))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_SPEAK_IN_OFFICER_CHAT)) {
|
||||
sender->SimpleMessage(CHANNEL_NARRATIVE, "You do not have permission to speak in officer chat.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMembers.readlock(__FUNCTION__, __LINE__);
|
||||
@ -2235,6 +2294,25 @@ void Guild::HandleOfficerSay(Client* sender, const char* message) {
|
||||
}
|
||||
mMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "Officer Say");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Guild::HandleOfficerSay(std::string senderName, const char* message, int8 language) {
|
||||
|
||||
map<int32, GuildMember *>::iterator itr;
|
||||
Client *client;
|
||||
|
||||
mMembers.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = members.begin(); itr != members.end(); itr++) {
|
||||
if (!(client = zone_list.GetClientByCharID(itr->second->character_id)))
|
||||
continue;
|
||||
|
||||
if (permissions.Get(itr->second->rank)->Get(GUILD_PERMISSIONS_SEE_OFFICER_CHAT))
|
||||
client->GetCurrentZone()->HandleChatMessage(senderName, client->GetPlayer()->GetName(), CHANNEL_OFFICER_SAY, message, 0, 0, language);
|
||||
|
||||
}
|
||||
mMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "Officer Say");
|
||||
}
|
||||
|
||||
void Guild::SendMessageToGuild(int8 event_type, const char* message, ...) {
|
||||
@ -2260,6 +2338,29 @@ void Guild::SendMessageToGuild(int8 event_type, const char* message, ...) {
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "Sent message to entire guild.");
|
||||
}
|
||||
|
||||
void Guild::SendGuildChatMessage(const char* message, ...) {
|
||||
|
||||
map<int32, GuildMember *>::iterator itr;
|
||||
Client *client;
|
||||
va_list argptr;
|
||||
char buffer[4096];
|
||||
|
||||
va_start(argptr, message);
|
||||
vsnprintf(buffer, sizeof(buffer), message, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
mMembers.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = members.begin(); itr != members.end(); itr++) {
|
||||
if (!(client = zone_list.GetClientByCharID(itr->second->character_id)))
|
||||
continue;
|
||||
|
||||
if (event_filters.Get(itr->second->rank)->Get(GUILD_EVENT_FILTER_CATEGORY_BROADCAST))
|
||||
client->SimpleMessage(CHANNEL_GUILD_CHAT, buffer);
|
||||
}
|
||||
mMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
LogWrite(GUILD__DEBUG, 0, "Guilds", "Sent message to entire guild.");
|
||||
}
|
||||
|
||||
string Guild::GetEpicMobDeathMessage(const char* player_name, const char* mob_name) {
|
||||
|
||||
char message[256];
|
||||
|
@ -297,9 +297,9 @@ public:
|
||||
int8 GetRecruitingPlayStyle() const {return recruiting_play_style;}
|
||||
bool SetRecruitingDescTag(int8 index, int8 tag, bool send_packet = true);
|
||||
int8 GetRecruitingDescTag(int8 index);
|
||||
bool SetPermission(int8 rank, int8 permission, int8 value, bool send_packet = true);
|
||||
bool SetPermission(int8 rank, int8 permission, int8 value, bool send_packet = true, bool save_needed = true);
|
||||
int8 GetPermission(int8 rank, int8 permission);
|
||||
bool SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet = true);
|
||||
bool SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet = true, bool save_needed = true);
|
||||
int8 GetEventFilter(int8 event_id, int8 category);
|
||||
int32 GetNumUniqueAccounts();
|
||||
int32 GetNumRecruiters();
|
||||
@ -321,12 +321,13 @@ public:
|
||||
bool SetGuildMemberNote(const char* name, const char* note, bool send_packet = true);
|
||||
bool SetGuildOfficerNote(const char* name, const char* note, bool send_packet = true);
|
||||
bool AddNewGuildMember(Client* client, const char* invited_by = 0, int8 rank = GUILD_RANK_RECRUIT);
|
||||
bool AddNewGuildMember(int32 characterID, const char *invited_by, int32 join_timestamp, int8 rank);
|
||||
bool AddGuildMember(GuildMember* guild_member);
|
||||
void RemoveGuildMember(int32 character_id, bool send_packet = true);
|
||||
void RemoveAllGuildMembers();
|
||||
bool DemoteGuildMember(Client* client, const char* name, bool send_packet = true);
|
||||
bool PromoteGuildMember(Client* client, const char* name, bool send_packet = true);
|
||||
bool KickGuildMember(Client* client, const char* name, bool send_packet = true);
|
||||
int32 KickGuildMember(Client* client, const char* name, bool send_packet = true);
|
||||
bool InvitePlayer(Client* client, const char* name, bool send_packet = true);
|
||||
bool AddPointsToAll(Client* client, float points, const char* comment = 0, bool send_packet = true);
|
||||
bool AddPointsToAllOnline(Client* client, float points, const char* comment = 0, bool send_packet = true);
|
||||
@ -372,9 +373,12 @@ public:
|
||||
void SendGuildRecruitingDetails(Client* client);
|
||||
void SendGuildRecruitingImages(Client* client);
|
||||
void SendGuildRecruiterInfo(Client* client, Player* player);
|
||||
void HandleGuildSay(Client* sender, const char* message);
|
||||
void HandleOfficerSay(Client* sender, const char* message);
|
||||
bool HandleGuildSay(Client* sender, const char* message);
|
||||
void HandleGuildSay(std::string senderName, const char* message, int8 language);
|
||||
bool HandleOfficerSay(Client* sender, const char* message);
|
||||
void HandleOfficerSay(std::string senderName, const char* message, int8 language);
|
||||
void SendMessageToGuild(int8 event_type, const char* message, ...);
|
||||
void SendGuildChatMessage(const char* message, ...);
|
||||
void SetSaveNeeded(bool val) {save_needed = val;}
|
||||
bool GetSaveNeeded() {return save_needed;}
|
||||
void SetMemberSaveNeeded(bool val) {member_save_needed = val;}
|
||||
|
@ -67,6 +67,39 @@ void WorldDatabase::LoadGuilds() {
|
||||
LogWrite(GUILD__INFO, 0, "Guilds", "\tLoaded %u Guild(s)", num_guilds);
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadGuild(int32 guild_id) {
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
Guild* tmpGuild = guild_list.GetGuild(guild_id);
|
||||
if(tmpGuild) // already loaded
|
||||
return;
|
||||
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on` FROM `guilds` where id=%u", guild_id);
|
||||
if (result && (row = mysql_fetch_row(result))) {
|
||||
LogWrite(GUILD__DEBUG, 1, "Guilds", "%u. %s", atoul(row[0]), row[1]);
|
||||
Guild* guild = new Guild;
|
||||
guild->SetID(atoul(row[0]));
|
||||
guild->SetName(row[1]);
|
||||
if (row[2])
|
||||
guild->SetMOTD(row[2], false);
|
||||
guild->SetLevel(atoi(row[3]), false);
|
||||
guild->SetEXPCurrent(atoul(row[4]), false);
|
||||
guild->SetEXPToNextLevel(atoul(row[5]), false);
|
||||
guild->SetFormedDate(atoul(row[6]));
|
||||
|
||||
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoaded %i guild members.", LoadGuildMembers(guild));
|
||||
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Ranks...");
|
||||
LoadGuildRanks(guild);
|
||||
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Event Filters...");
|
||||
LoadGuildEventFilters(guild);
|
||||
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Events...");
|
||||
LoadGuildEvents(guild);
|
||||
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Recruiting...");
|
||||
LoadGuildRecruiting(guild);
|
||||
guild_list.AddGuild(guild);
|
||||
}
|
||||
}
|
||||
|
||||
int32 WorldDatabase::LoadGuildMembers(Guild* guild) {
|
||||
int32 num_members = 0;
|
||||
Query query;
|
||||
|
@ -888,6 +888,7 @@ public:
|
||||
int32 status_rent_reduction;
|
||||
float coin_rent_reduction;
|
||||
int8 house_only;
|
||||
int8 house_location; // 0 = floor, 1 = ceiling, 2 = wall
|
||||
};
|
||||
struct HouseContainer_Info{
|
||||
int64 allowed_types;
|
||||
|
@ -458,7 +458,7 @@ int32 WorldDatabase::LoadHouseItem(int32 item_id)
|
||||
MYSQL_ROW row;
|
||||
|
||||
std::string select_query_addition = std::string(" where item_id = ") + std::to_string(item_id);
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT item_id, rent_reduction, status_rent_reduction, coin_rent_reduction, house_only FROM item_details_house%s", (item_id == 0) ? "" : select_query_addition.c_str());
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT item_id, rent_reduction, status_rent_reduction, coin_rent_reduction, house_only, house_location FROM item_details_house%s", (item_id == 0) ? "" : select_query_addition.c_str());
|
||||
int32 total = 0;
|
||||
int32 id = 0;
|
||||
|
||||
@ -477,6 +477,7 @@ int32 WorldDatabase::LoadHouseItem(int32 item_id)
|
||||
item->houseitem_info->status_rent_reduction = atoi(row[2]);
|
||||
item->houseitem_info->coin_rent_reduction = atof(row[3]);
|
||||
item->houseitem_info->house_only = atoi(row[4]);
|
||||
item->houseitem_info->house_location = atoul(row[5]);
|
||||
total++;
|
||||
}
|
||||
else
|
||||
|
@ -152,7 +152,7 @@ void WorldDatabase::LoadGlobalLoot(ZoneServer* zone) {
|
||||
count++;
|
||||
}
|
||||
|
||||
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u Global loot list%s.", count, count == 1 ? "" : "s");
|
||||
LogWrite(LOOT__DEBUG, 4, "Loot", "--Loaded %u Global loot list%s.", count, count == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@ bool WorldDatabase::LoadSpawnLoot(ZoneServer* zone, Spawn* spawn)
|
||||
LogWrite(LOOT__DEBUG, 5, "Loot", "---Adding loot table %u to spawn %u", table_id, spawn_id);
|
||||
count++;
|
||||
}
|
||||
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u spawn loot list%s.", count, count == 1 ? "" : "s");
|
||||
LogWrite(LOOT__DEBUG, 4, "Loot", "--Loaded %u spawn loot list%s.", count, count == 1 ? "" : "s");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -66,6 +66,8 @@ extern int errno;
|
||||
#include "World.h"
|
||||
#include "../common/ConfigReader.h"
|
||||
#include "Rules/Rules.h"
|
||||
#include "Web/PeerManager.h"
|
||||
#include "Web/HTTPSClientPool.h"
|
||||
|
||||
extern sint32 numzones;
|
||||
extern sint32 numclients;
|
||||
@ -80,6 +82,8 @@ extern volatile bool RunLoops;
|
||||
volatile bool LoginLoopRunning = false;
|
||||
extern ConfigReader configReader;
|
||||
extern RuleManager rule_manager;
|
||||
extern PeerManager peer_manager;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
|
||||
bool AttemptingConnect = false;
|
||||
|
||||
@ -176,7 +180,7 @@ bool LoginServer::Process() {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Login Server returned a fatal error: %s\n", pack->pBuffer);
|
||||
tcpc->Disconnect();
|
||||
ret = false;
|
||||
net.ReadLoginINI();
|
||||
//net.ReadLoginINI(); // can't properly support with command line args now
|
||||
break;
|
||||
}
|
||||
case ServerOP_CharTimeStamp:
|
||||
@ -367,10 +371,13 @@ bool LoginServer::Process() {
|
||||
|
||||
int32 access_key = 0;
|
||||
|
||||
// if it is a accepted login, we add the zone auth request
|
||||
access_key = DetermineCharacterLoginRequest ( utwr );
|
||||
|
||||
if ( access_key != 0 )
|
||||
ZoneChangeDetails details;
|
||||
std::string name = database.loadCharacterFromLogin(&details, utwr->char_id, utwr->lsaccountid);
|
||||
// if it is a accepted login, we add the zone auth request
|
||||
access_key = DetermineCharacterLoginRequest ( utwr, &details, name);
|
||||
|
||||
if ( access_key != 0 )
|
||||
{
|
||||
zone_auth.PurgeInactiveAuth();
|
||||
char* characterName = database.GetCharacterName( utwr->char_id );
|
||||
@ -555,22 +562,11 @@ void LoginServer::SendFilterNameResponse ( int8 resp, int32 acct_id , int32 char
|
||||
safe_delete(outpack);
|
||||
}
|
||||
|
||||
int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr ) {
|
||||
int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr, ZoneChangeDetails* details, std::string name) {
|
||||
LogWrite(LOGIN__TRACE, 9, "Login", "Enter: %s", __FUNCTION__);
|
||||
ServerPacket* outpack = new ServerPacket;
|
||||
outpack->opcode = ServerOP_UsertoWorldResp;
|
||||
outpack->size = sizeof(UsertoWorldResponse_Struct);
|
||||
outpack->pBuffer = new uchar[outpack->size];
|
||||
memset(outpack->pBuffer, 0, outpack->size);
|
||||
UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
|
||||
utwrs->lsaccountid = utwr->lsaccountid;
|
||||
utwrs->char_id = utwr->char_id;
|
||||
utwrs->ToID = utwr->FromID;
|
||||
int32 timestamp = Timer::GetUnixTimeStamp();
|
||||
utwrs->access_key = timestamp;
|
||||
|
||||
// set default response to 0
|
||||
utwrs->response = 0;
|
||||
int32 key = static_cast<unsigned int>(MakeRandomFloat(0.01,1.0) * UINT32_MAX);
|
||||
int8 response = 0;
|
||||
|
||||
sint16 lowestStatus = database.GetLowestCharacterAdminStatus( utwr->lsaccountid );
|
||||
|
||||
@ -587,19 +583,19 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Login Rejected based on PLAY_ERROR (UserStatus) (MinStatus: %i), UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id );
|
||||
switch(status){
|
||||
case -10:
|
||||
utwrs->response = PLAY_ERROR_CHAR_NOT_LOADED;
|
||||
response = PLAY_ERROR_CHAR_NOT_LOADED;
|
||||
break;
|
||||
case -9:
|
||||
utwrs->response = 0;//PLAY_ERROR_ACCOUNT_IN_USE;
|
||||
response = 0;//PLAY_ERROR_ACCOUNT_IN_USE;
|
||||
break;
|
||||
case -8:
|
||||
utwrs->response = PLAY_ERROR_LOADING_ERROR;
|
||||
response = PLAY_ERROR_LOADING_ERROR;
|
||||
break;
|
||||
case -1:
|
||||
utwrs->response = PLAY_ERROR_ACCOUNT_BANNED;
|
||||
response = PLAY_ERROR_ACCOUNT_BANNED;
|
||||
break;
|
||||
default:
|
||||
utwrs->response = PLAY_ERROR_PROBLEM;
|
||||
response = PLAY_ERROR_PROBLEM;
|
||||
}
|
||||
}
|
||||
else if(net.world_locked == true){
|
||||
@ -607,7 +603,7 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
|
||||
|
||||
// has high enough status, allow it
|
||||
if(status >= loginserver.minLockedStatus)
|
||||
utwrs->response = 1;
|
||||
response = 1;
|
||||
}
|
||||
else if(loginserver.maxPlayers > -1 && ((sint16)client_list.Count()) >= loginserver.maxPlayers)
|
||||
{
|
||||
@ -616,14 +612,66 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
|
||||
// has high enough status, allow it
|
||||
if(status >= loginserver.minGameFullStatus)
|
||||
{
|
||||
utwrs->response = 1;
|
||||
response = 1;
|
||||
}
|
||||
else
|
||||
utwrs->response = -3; // server full response is -3
|
||||
response = -3; // server full response is -3
|
||||
}
|
||||
else
|
||||
utwrs->response = 1;
|
||||
response = 1;
|
||||
|
||||
bool attemptedPeer = false;
|
||||
if(response == 1 && details->peerId.size() > 0 && details->peerId != "self" && name.size() > 0) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("account_id", utwr->lsaccountid);
|
||||
root.put("character_name", std::string(name));
|
||||
root.put("character_id", std::to_string(utwr->char_id));
|
||||
root.put("zone_id", std::to_string(details->zoneId));
|
||||
root.put("instance_id", std::to_string(details->instanceId));
|
||||
root.put("login_key", std::to_string(key));
|
||||
root.put("client_ip", std::string(utwr->ip_address));
|
||||
root.put("world_id", std::to_string(utwr->worldid));
|
||||
root.put("from_id", std::to_string(utwr->FromID));
|
||||
root.put("first_login", true);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for existing zone %s", __FUNCTION__, name, details->peerWebAddress.c_str(), details->peerWebPort, details->zoneName.c_str());
|
||||
attemptedPeer = true;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(details->peerId, details->peerWebAddress, std::to_string(details->peerWebPort), "/addcharauth", jsonPayload);
|
||||
}
|
||||
else if(response == 1 && details->peerId == "") {
|
||||
std::shared_ptr<Peer> peer = peer_manager.getHealthyPeerWithLeastClients();
|
||||
if(peer != nullptr) {
|
||||
boost::property_tree::ptree root;
|
||||
char* characterName = database.GetCharacterName( utwr->char_id );
|
||||
if(!characterName) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: AddCharAuth failed to identify character name for char id %u to peer %s:%u", __FUNCTION__, utwr->char_id, peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
else {
|
||||
root.put("account_id", utwr->lsaccountid);
|
||||
root.put("character_name", std::string(characterName));
|
||||
root.put("character_id", std::to_string(utwr->char_id));
|
||||
root.put("zone_id", std::to_string(details->zoneId));
|
||||
root.put("instance_id", std::to_string(details->instanceId));
|
||||
root.put("login_key", std::to_string(key));
|
||||
root.put("client_ip", std::string(utwr->ip_address));
|
||||
root.put("world_id", std::to_string(utwr->worldid));
|
||||
root.put("from_id", std::to_string(utwr->FromID));
|
||||
root.put("first_login", true);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for new zone %s", __FUNCTION__, characterName, peer->webAddr.c_str(), peer->webPort, details->zoneName.c_str());
|
||||
attemptedPeer = true;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addcharauth", jsonPayload);
|
||||
}
|
||||
}
|
||||
else if(peer_manager.hasPeers()) {
|
||||
LogWrite(PEERING__WARNING, 0, "Peering", "%s: AddCharAuth failed to find healthy peer for char id %u", __FUNCTION__, utwr->char_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*sint32 x = database.CommandRequirement("$MAXCLIENTS");
|
||||
if( (sint32)numplayers >= x && x != -1 && x != 255 && status < 80)
|
||||
utwrs->response = -3;
|
||||
@ -634,34 +682,61 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
|
||||
utwrs->response = -2;
|
||||
*/
|
||||
//printf("Response is %i for %i\n",utwrs->response,id);struct sockaddr_in sa;
|
||||
|
||||
if(!attemptedPeer) {
|
||||
SendCharApprovedLogin(response, "", "", std::string(utwr->ip_address), 0, utwr->lsaccountid, utwr->char_id, key, utwr->worldid, utwr->FromID);
|
||||
}
|
||||
|
||||
LogWrite(LOGIN__TRACE, 9, "Login", "Exit: %s with timestamp=%u", __FUNCTION__, timestamp);
|
||||
// depending on the response determined above, this could return 0 (for failure)
|
||||
return (attemptedPeer) ? 0 : key;
|
||||
}
|
||||
|
||||
void LoginServer::SendCharApprovedLogin(int8 response, std::string peerAddress, std::string peerInternalAddress, std::string clientIP, int16 peerPort, int32 account_id, int32 char_id, int32 key, int32 world_id, int32 from_id) {
|
||||
ServerPacket* outpack = new ServerPacket;
|
||||
outpack->opcode = ServerOP_UsertoWorldResp;
|
||||
outpack->size = sizeof(UsertoWorldResponse_Struct);
|
||||
outpack->pBuffer = new uchar[outpack->size];
|
||||
memset(outpack->pBuffer, 0, outpack->size);
|
||||
UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
|
||||
utwrs->response = response;
|
||||
utwrs->lsaccountid = account_id;
|
||||
utwrs->char_id = char_id;
|
||||
utwrs->ToID = from_id;
|
||||
utwrs->access_key = key;
|
||||
|
||||
int32 ipv4addr = 0;
|
||||
int result = 0;
|
||||
#ifdef WIN32
|
||||
struct sockaddr_in myaddr;
|
||||
ZeroMemory(&myaddr, sizeof(myaddr));
|
||||
result = InetPton(AF_INET, utwr->ip_address, &(myaddr.sin_addr));
|
||||
result = InetPton(AF_INET, clientIP.c_str(), &(myaddr.sin_addr));
|
||||
if(result)
|
||||
ipv4addr = ntohl(myaddr.sin_addr.s_addr);
|
||||
|
||||
#else
|
||||
result = inet_pton(AF_INET, utwr->ip_address, &ipv4addr);
|
||||
result = inet_pton(AF_INET, clientIP.c_str(), &ipv4addr);
|
||||
if(result)
|
||||
ipv4addr = ntohl(ipv4addr);
|
||||
#endif
|
||||
if (((result > 0 && IsPrivateAddress(ipv4addr)) || (strcmp(net.GetWorldAddress(), utwr->ip_address) == 0)) && (strlen(net.GetInternalWorldAddress()) > 0))
|
||||
strcpy(utwrs->ip_address, net.GetInternalWorldAddress());
|
||||
else
|
||||
strcpy(utwrs->ip_address, net.GetWorldAddress());
|
||||
|
||||
LogWrite(CCLIENT__INFO, 0, "World", "New client login attempt from %s, providing %s as the world server address.",utwr->ip_address, utwrs->ip_address );
|
||||
std::string internalAddress = std::string(net.GetInternalWorldAddress());
|
||||
std::string address = std::string(net.GetWorldAddress());
|
||||
int16 worldport = net.GetWorldPort();
|
||||
if(peerAddress.size() > 0 && peerPort > 0) {
|
||||
internalAddress = peerInternalAddress;
|
||||
address = peerAddress;
|
||||
worldport = peerPort;
|
||||
}
|
||||
if (((result > 0 && IsPrivateAddress(ipv4addr)) || (strcmp(address.c_str(), clientIP.c_str()) == 0)) && (internalAddress.size() > 0))
|
||||
strcpy(utwrs->ip_address, internalAddress.c_str());
|
||||
else
|
||||
strcpy(utwrs->ip_address, address.c_str());
|
||||
|
||||
LogWrite(CCLIENT__INFO, 0, "World", "New client login attempt from %s, providing %s:%u as the world server address.",clientIP.c_str(), utwrs->ip_address, worldport );
|
||||
|
||||
utwrs->port = net.GetWorldPort();
|
||||
utwrs->worldid = utwr->worldid;
|
||||
utwrs->port = worldport;
|
||||
utwrs->worldid = world_id;
|
||||
SendPacket(outpack);
|
||||
delete outpack;
|
||||
|
||||
LogWrite(LOGIN__TRACE, 9, "Login", "Exit: %s with timestamp=%u", __FUNCTION__, timestamp);
|
||||
// depending on the response determined above, this could return 0 (for failure)
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,8 @@ public:
|
||||
|
||||
void SendDeleteCharacter ( CharacterTimeStamp_Struct* cts );
|
||||
|
||||
int32 DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr );
|
||||
|
||||
int32 DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr, ZoneChangeDetails* details, std::string name);
|
||||
void SendCharApprovedLogin(int8 response, std::string peerAddress, std::string peerInternalAddress, std::string clientIP, int16 peerPort, int32 account_id, int32 char_id, int32 key, int32 world_id, int32 from_id);
|
||||
void InitLoginServerVariables();
|
||||
|
||||
sint16 minLockedStatus;
|
||||
|
@ -1700,19 +1700,21 @@ int EQ2Emu_lua_GetZone(lua_State* state) {
|
||||
if (!lua_interface)
|
||||
return 0;
|
||||
int32 zone_id = lua_interface->GetInt32Value(state);
|
||||
ZoneServer* zone = 0;
|
||||
if (zone_id > 0)
|
||||
zone = zone_list.Get(zone_id, true, false, false);
|
||||
ZoneChangeDetails zone_details;
|
||||
std::string zone_name;
|
||||
ZoneServer* zone = nullptr;
|
||||
|
||||
if(zone_id < 1) {
|
||||
zone_name = lua_interface->GetStringValue(state);
|
||||
}
|
||||
bool zone_avail = zone_list.GetZone(&zone_details, zone_id, zone_name, true, false, false, false);
|
||||
if (zone_avail) {
|
||||
zone = (ZoneServer*)zone_details.zonePtr;
|
||||
}
|
||||
else {
|
||||
string zone_name = lua_interface->GetStringValue(state);
|
||||
if (zone_name.length() > 0) {
|
||||
zone = zone_list.Get(zone_name.c_str(), true, false, false);
|
||||
}
|
||||
else {
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
if (spawn)
|
||||
zone = spawn->GetZone();
|
||||
}
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
if (spawn)
|
||||
zone = spawn->GetZone();
|
||||
}
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (zone) {
|
||||
@ -2597,7 +2599,7 @@ int EQ2Emu_lua_RemoveSpellBonus(lua_State* state) {
|
||||
luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
else {
|
||||
LogWrite(LUA__ERROR, 0, "LUA", "Error removing spell bonus buff %s called by %s, zone is not available.", luaspell->spell ? luaspell->spell->GetName() : "NotSet", spawn->GetName());
|
||||
LogWrite(LUA__ERROR, 0, "LUA", "Error removing spell bonus buff %s called by %s, zone is not available.", luaspell->spell ? luaspell->spell->GetName() : "NotSet", spawn ? spawn->GetName() : "N/A");
|
||||
}
|
||||
}
|
||||
else if (spawn && spawn->IsEntity()) {
|
||||
@ -6680,6 +6682,11 @@ int EQ2Emu_lua_RemoveWard(lua_State* state) {
|
||||
}
|
||||
|
||||
ZoneServer* zone = spell->caster->GetZone();
|
||||
if(!zone) {
|
||||
lua_interface->LogError("%s: RemoveWard error: no valid zone for caster", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Spawn* target = 0;
|
||||
spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
|
||||
for (int32 i = 0; i < spell->targets.size(); i++) {
|
||||
@ -14270,3 +14277,34 @@ int EQ2Emu_lua_DespawnByLocationID(lua_State* state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_AddRespawn(lua_State* state) {
|
||||
ZoneServer* zone = lua_interface->GetZone(state);
|
||||
int32 location_id = lua_interface->GetInt32Value(state, 2);
|
||||
int32 respawn_time = lua_interface->GetInt32Value(state, 3);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (zone) {
|
||||
zone->AddRespawn(location_id, respawn_time);
|
||||
lua_interface->SetBooleanValue(state, true);
|
||||
return 1;
|
||||
}
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state) {
|
||||
int32 location_id = lua_interface->GetInt32Value(state);
|
||||
int8 spawn_type = lua_interface->GetInt32Value(state, 2);
|
||||
int32 respawn_time = lua_interface->GetInt32Value(state, 3);
|
||||
int32 zone_id = lua_interface->GetInt32Value(state, 4);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (location_id && zone_id) {
|
||||
database.CreatePersistedRespawn(location_id,spawn_type,respawn_time,zone_id);
|
||||
lua_interface->SetBooleanValue(state, true);
|
||||
return 1;
|
||||
}
|
||||
lua_interface->SetBooleanValue(state, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -660,4 +660,7 @@ int EQ2Emu_lua_ToggleCharacterFlag(lua_State* state);
|
||||
int EQ2Emu_lua_GetSpellInitialTarget(lua_State* state);
|
||||
|
||||
int EQ2Emu_lua_DespawnByLocationID(lua_State* state);
|
||||
|
||||
int EQ2Emu_lua_AddRespawn(lua_State* state);
|
||||
int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state);
|
||||
#endif
|
@ -905,9 +905,9 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
|
||||
spell->caster->RemoveProc(0, spell);
|
||||
spell->caster->RemoveMaintainedSpell(spell);
|
||||
|
||||
int8 spell_type = spell->spell->GetSpellData()->spell_type;
|
||||
if(spell->caster->IsPlayer() && !removing_all_spells)
|
||||
if(spell->spell && spell->spell->GetSpellData() && spell->caster->IsPlayer() && !removing_all_spells)
|
||||
{
|
||||
int8 spell_type = spell->spell->GetSpellData()->spell_type;
|
||||
Player* player = (Player*)spell->caster;
|
||||
switch(spell_type)
|
||||
{
|
||||
@ -1562,6 +1562,9 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
|
||||
lua_register(state, "GetSpellInitialTarget", EQ2Emu_lua_GetSpellInitialTarget);
|
||||
|
||||
lua_register(state,"DespawnByLocationID", EQ2Emu_lua_DespawnByLocationID);
|
||||
|
||||
lua_register(state,"AddRespawn", EQ2Emu_lua_AddRespawn);
|
||||
lua_register(state,"CreatePersistedRespawn", EQ2Emu_lua_CreatePersistedRespawn);
|
||||
}
|
||||
|
||||
void LuaInterface::LogError(const char* error, ...) {
|
||||
|
@ -544,20 +544,30 @@ void Brain::AddToEncounter(Entity* entity) {
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(group_id);
|
||||
if (group)
|
||||
{
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
for (itr = members->begin(); itr != members->end(); itr++) {
|
||||
if ((*itr)->member)
|
||||
{
|
||||
bool success = AddToEncounter((*itr)->member->GetID());
|
||||
if((*itr)->client && success) {
|
||||
m_encounter_playerlist.insert(make_pair((*itr)->client->GetPlayer()->GetCharacterID(), (*itr)->client->GetPlayer()->GetID()));
|
||||
std::vector<int32> raidGroups;
|
||||
group->GetRaidGroups(&raidGroups);
|
||||
if(raidGroups.size() < 1)
|
||||
raidGroups.push_back(group_id);
|
||||
|
||||
std::vector<int32>::iterator group_itr;
|
||||
for(group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
|
||||
group = world.GetGroupManager()->GetGroup((*group_itr));
|
||||
if(group) {
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
for (itr = members->begin(); itr != members->end(); itr++) {
|
||||
if ((*itr)->member)
|
||||
{
|
||||
bool success = AddToEncounter((*itr)->member->GetID());
|
||||
if((*itr)->client && success) {
|
||||
m_encounter_playerlist.insert(make_pair((*itr)->client->GetPlayer()->GetCharacterID(), (*itr)->client->GetPlayer()->GetID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
else {
|
||||
|
@ -77,6 +77,8 @@ Player::Player(){
|
||||
spell_count = 0;
|
||||
spell_orig_packet = 0;
|
||||
spell_xor_packet = 0;
|
||||
raid_orig_packet = nullptr;
|
||||
raid_xor_packet = nullptr;
|
||||
resurrecting = false;
|
||||
spawn_id = 1;
|
||||
spawn_type = 4;
|
||||
@ -131,6 +133,8 @@ Player::Player(){
|
||||
need_trait_update = true;
|
||||
active_food_unique_id = 0;
|
||||
active_drink_unique_id = 0;
|
||||
raidsheet_changed = false;
|
||||
hassent_raid = false;
|
||||
}
|
||||
Player::~Player(){
|
||||
SetSaveSpellEffects(true);
|
||||
@ -181,6 +185,8 @@ Player::~Player(){
|
||||
safe_delete_array(spawn_tmp_pos_xor_packet);
|
||||
safe_delete_array(spell_xor_packet);
|
||||
safe_delete_array(spell_orig_packet);
|
||||
safe_delete_array(raid_orig_packet);
|
||||
safe_delete_array(raid_xor_packet);
|
||||
DestroyQuests();
|
||||
WritePlayerStatistics();
|
||||
RemovePlayerStatistics();
|
||||
@ -1067,7 +1073,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
|
||||
player->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__);
|
||||
for (int i = 0; i < 45; i++) {
|
||||
if (i < 30) {
|
||||
maintained_target = player->GetZone()->GetSpawnByID(info_struct->maintained_effects[i].target);
|
||||
maintained_target = player->GetZone() ? player->GetZone()->GetSpawnByID(info_struct->maintained_effects[i].target) : nullptr;
|
||||
packet->setSubstructDataByName("maintained_effects", "name", info_struct->maintained_effects[i].name, i, 0);
|
||||
if (maintained_target)
|
||||
packet->setSubstructDataByName("maintained_effects", "target", player->GetIDWithPlayerSpawn(maintained_target), i, 0);
|
||||
@ -3055,6 +3061,162 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EQ2Packet* Player::GetRaidUpdatePacket(int16 version) {
|
||||
std::unique_lock lock(raid_update_mutex);
|
||||
|
||||
std::vector<int32> raidGroups;
|
||||
PacketStruct* packet = configReader.getStruct("WS_RaidUpdate", version);
|
||||
EQ2Packet* ret = 0;
|
||||
Entity* member = 0;
|
||||
int8 det_count = 0;
|
||||
int8 total_groups = 0;
|
||||
if (packet) {
|
||||
int16 ptr = 0;
|
||||
// Get the packet size
|
||||
PacketStruct* packet2 = configReader.getStruct("Substruct_RaidMember", version);
|
||||
int32 total_bytes = packet2->GetTotalPacketSize();
|
||||
safe_delete(packet2);
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
if (GetGroupMemberInfo()) {
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(GetGroupMemberInfo()->group_id);
|
||||
if (group)
|
||||
{
|
||||
group->GetRaidGroups(&raidGroups);
|
||||
std::vector<int32>::iterator raid_itr;
|
||||
int32 group_pos = 0;
|
||||
for(raid_itr = raidGroups.begin(); raid_itr != raidGroups.end(); raid_itr++) {
|
||||
group = world.GetGroupManager()->GetGroup((*raid_itr));
|
||||
if(!group)
|
||||
continue;
|
||||
total_groups++;
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
GroupMemberInfo* info = 0;
|
||||
int x = 1;
|
||||
int lastpos = 1;
|
||||
bool gotleader = false;
|
||||
for (itr = members->begin(); itr != members->end(); itr++) {
|
||||
info = *itr;
|
||||
|
||||
if(!info)
|
||||
continue;
|
||||
|
||||
member = info->member;
|
||||
|
||||
std::string prop_name("group_member");
|
||||
if(!gotleader && info->leader) {
|
||||
lastpos = x;
|
||||
x = 0;
|
||||
gotleader = true;
|
||||
}
|
||||
else if(lastpos) {
|
||||
x = lastpos;
|
||||
lastpos = 0;
|
||||
}
|
||||
prop_name.append(std::to_string(x) + "_" + std::to_string(group_pos));
|
||||
x++;
|
||||
if (member && member->GetZone() == GetZone()) {
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "spawn_id", GetIDWithPlayerSpawn(member), 0);
|
||||
|
||||
if (member->HasPet()) {
|
||||
if (member->GetPet())
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "pet_id", GetIDWithPlayerSpawn(member->GetPet()), 0);
|
||||
else
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "pet_id", GetIDWithPlayerSpawn(member->GetCharmedPet()), 0);
|
||||
}
|
||||
else
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "pet_id", 0xFFFFFFFF, 0);
|
||||
|
||||
//Send detriment counts as 255 if all dets of that type are incurable
|
||||
det_count = member->GetTraumaCount();
|
||||
if (det_count > 0) {
|
||||
if (!member->HasCurableDetrimentType(DET_TYPE_TRAUMA))
|
||||
det_count = 255;
|
||||
}
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "trauma_count", det_count, 0);
|
||||
|
||||
det_count = member->GetArcaneCount();
|
||||
if (det_count > 0) {
|
||||
if (!member->HasCurableDetrimentType(DET_TYPE_ARCANE))
|
||||
det_count = 255;
|
||||
}
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "arcane_count", det_count, 0);
|
||||
|
||||
det_count = member->GetNoxiousCount();
|
||||
if (det_count > 0) {
|
||||
if (!member->HasCurableDetrimentType(DET_TYPE_NOXIOUS))
|
||||
det_count = 255;
|
||||
}
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "noxious_count", det_count, 0);
|
||||
|
||||
det_count = member->GetElementalCount();
|
||||
if (det_count > 0) {
|
||||
if (!member->HasCurableDetrimentType(DET_TYPE_ELEMENTAL))
|
||||
det_count = 255;
|
||||
}
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "elemental_count", det_count, 0);
|
||||
|
||||
det_count = member->GetCurseCount();
|
||||
if (det_count > 0) {
|
||||
if (!member->HasCurableDetrimentType(DET_TYPE_CURSE))
|
||||
det_count = 255;
|
||||
}
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "curse_count", det_count, 0);
|
||||
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "zone_status", 1, 0);
|
||||
}
|
||||
else {
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "pet_id", 0xFFFFFFFF, 0);
|
||||
//packet->setSubstructDataByName(prop_name.c_str(), "unknown5", 1, 0, 1); // unknown5 > 1 = name is blue
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "zone_status", 2, 0);
|
||||
}
|
||||
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "name", info->name.c_str(), 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "hp_current", info->hp_current, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "hp_max", info->hp_max, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "hp_current2", info->hp_current, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "power_current", info->power_current, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "power_max", info->power_max, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "level_current", info->level_current, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "level_max", info->level_max, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "zone", info->zone.c_str(), 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "race_id", info->race_id, 0);
|
||||
packet->setSubstructDataByName(prop_name.c_str(), "class_id", info->class_id, 0);
|
||||
}
|
||||
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
group_pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
//packet->PrintPacket();
|
||||
|
||||
hassent_raid = true;
|
||||
string* data = packet->serializeString();
|
||||
int32 size = data->length();
|
||||
|
||||
uchar* tmp = new uchar[size];
|
||||
if(!raid_xor_packet){
|
||||
raid_orig_packet = new uchar[size];
|
||||
raid_xor_packet = new uchar[size];
|
||||
memcpy(raid_orig_packet, (uchar*)data->c_str(), size);
|
||||
size = Pack(tmp, (uchar*)data->c_str(), size, size, version);
|
||||
}
|
||||
else{
|
||||
memcpy(raid_xor_packet, (uchar*)data->c_str(), size);
|
||||
Encode(raid_xor_packet, raid_orig_packet, size);
|
||||
size = Pack(tmp, raid_xor_packet, size, size, version);
|
||||
}
|
||||
|
||||
ret = new EQ2Packet(OP_UpdateRaidMsg, tmp, size);
|
||||
safe_delete_array(tmp);
|
||||
safe_delete(packet);
|
||||
//DumpPacket(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PlayerInfo::~PlayerInfo(){
|
||||
RemoveOldPackets();
|
||||
@ -3946,6 +4108,14 @@ bool Player::GetCharSheetChanged(){
|
||||
return charsheet_changed;
|
||||
}
|
||||
|
||||
void Player::SetRaidSheetChanged(bool val){
|
||||
raidsheet_changed = val;
|
||||
}
|
||||
|
||||
bool Player::GetRaidSheetChanged(){
|
||||
return raidsheet_changed;
|
||||
}
|
||||
|
||||
bool Player::AdventureXPEnabled(){
|
||||
return (GetInfoStruct()->get_flags() & (1 << CF_COMBAT_EXPERIENCE_ENABLED));
|
||||
}
|
||||
@ -5828,6 +5998,11 @@ void Player::SetReturningFromLD(bool val){
|
||||
spell_orig_packet=0;
|
||||
spell_xor_packet=0;
|
||||
spell_count = 0;
|
||||
|
||||
safe_delete_array(raid_orig_packet);
|
||||
safe_delete_array(raid_xor_packet);
|
||||
raid_orig_packet=0;
|
||||
raid_xor_packet=0;
|
||||
|
||||
reset_character_flag(CF_IS_SITTING);
|
||||
if (GetActivityStatus() & ACTIVITY_STATUS_CAMPING)
|
||||
@ -5981,40 +6156,6 @@ void Player::DeleteMail(int32 mail_id, bool from_database) {
|
||||
}
|
||||
}
|
||||
|
||||
ZoneServer* Player::GetGroupMemberInZone(int32 zone_id) {
|
||||
ZoneServer* ret = nullptr;
|
||||
|
||||
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
||||
// If the player has a group and destination zone id
|
||||
if (gmi && zone_id) {
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
|
||||
if (group)
|
||||
{
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
// Loop through the group members
|
||||
for (itr = members->begin(); itr != members->end(); itr++) {
|
||||
// If a group member matches a target
|
||||
if ((*itr)->member && (*itr)->member != this && (*itr)->member->GetZone() && (*itr)->member->GetZone()->GetInstanceID() > 0 &&
|
||||
(*itr)->member->GetZone()->GetZoneID() == zone_id) {
|
||||
// toggle the flag and break the loop
|
||||
ret = (*itr)->member->GetZone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* CharacterInstances */
|
||||
|
||||
CharacterInstances::CharacterInstances() {
|
||||
@ -6154,7 +6295,8 @@ void CharacterInstances::ProcessInstanceTimers(Player* player) {
|
||||
|
||||
if (data->zone_instance_type == SOLO_PERSIST_INSTANCE || data->zone_instance_type == GROUP_PERSIST_INSTANCE || data->zone_instance_type == RAID_PERSIST_INSTANCE) {
|
||||
// Check max duration (last success + success time)
|
||||
if (Timer::GetUnixTimeStamp() >= (data->last_success_timestamp + data->success_lockout_time)) {
|
||||
// if the zone does not have a success lockout time, we should not apply this logic
|
||||
if (data->success_lockout_time > 0 && (Timer::GetUnixTimeStamp() >= (data->last_success_timestamp + data->success_lockout_time))) {
|
||||
// Max duration has passed, instance has expired lets remove the player from it,
|
||||
// this will also delete the instace if all players have been removed from it
|
||||
database.DeleteCharacterFromInstance(player->GetCharacterID(), data->instance_id);
|
||||
@ -7117,8 +7259,8 @@ void Player::SaveSpellEffects()
|
||||
target_char_id = ((Player*)spawn)->GetCharacterID();
|
||||
else if (spawn && spawn->IsPet() && ((Entity*)spawn)->GetOwner() == (Entity*)this)
|
||||
target_char_id = 0xFFFFFFFF;
|
||||
|
||||
int32 caster_char_id = (info->maintained_effects[i].spell->caster && info->maintained_effects[i].spell->caster->IsPlayer()) ? ((Player*)info->maintained_effects[i].spell->caster)->GetCharacterID() : 0;
|
||||
|
||||
int32 caster_char_id = info->maintained_effects[i].spell->initial_caster_char_id;
|
||||
|
||||
int32 timestamp = 0xFFFFFFFF;
|
||||
if(info->maintained_effects[i].spell->spell->GetSpellData() && !info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
|
||||
|
@ -437,6 +437,8 @@ public:
|
||||
PlayerInfo* GetPlayerInfo();
|
||||
void SetCharSheetChanged(bool val);
|
||||
bool GetCharSheetChanged();
|
||||
void SetRaidSheetChanged(bool val);
|
||||
bool GetRaidSheetChanged();
|
||||
void AddFriend(const char* name, bool save);
|
||||
bool IsFriend(const char* name);
|
||||
void RemoveFriend(const char* name);
|
||||
@ -552,8 +554,9 @@ public:
|
||||
int8 GetSpellTier(int32 id);
|
||||
void SetSpellStatus(Spell* spell, int8 status);
|
||||
void RemoveSpellStatus(Spell* spell, int8 status);
|
||||
EQ2Packet* GetSpellBookUpdatePacket(int16 version);
|
||||
EQ2Packet* GetSpellSlotMappingPacket(int16 version);
|
||||
EQ2Packet* GetSpellBookUpdatePacket(int16 version);
|
||||
EQ2Packet* GetRaidUpdatePacket(int16 version);
|
||||
int32 GetCharacterID();
|
||||
void SetCharacterID(int32 new_id);
|
||||
EQ2Packet* GetQuickbarPacket(int16 version);
|
||||
@ -775,7 +778,6 @@ public:
|
||||
void SetAwayMessage(string val) { away_message = val; }
|
||||
void SetRangeAttack(bool val);
|
||||
bool GetRangeAttack();
|
||||
ZoneServer* GetGroupMemberInZone(int32 zone_id);
|
||||
bool AddMail(Mail* mail);
|
||||
MutexMap<int32, Mail*>* GetMail();
|
||||
Mail* GetMail(int32 mail_id);
|
||||
@ -1112,6 +1114,7 @@ public:
|
||||
map<int8, int32> m_levelXPReq;
|
||||
|
||||
mutable std::shared_mutex spell_packet_update_mutex;
|
||||
mutable std::shared_mutex raid_update_mutex;
|
||||
private:
|
||||
bool reset_mentorship;
|
||||
bool range_attack;
|
||||
@ -1129,7 +1132,9 @@ private:
|
||||
map<Spawn*, bool> current_quest_flagged;
|
||||
PlayerFaction factions;
|
||||
map<int32, Quest*> completed_quests;
|
||||
bool charsheet_changed;
|
||||
std::atomic<bool> charsheet_changed;
|
||||
std::atomic<bool> raidsheet_changed;
|
||||
std::atomic<bool> hassent_raid;
|
||||
map<int32, string> spawn_vis_packet_list;
|
||||
map<int32, string> spawn_info_packet_list;
|
||||
map<int32, string> spawn_pos_packet_list;
|
||||
@ -1140,6 +1145,9 @@ private:
|
||||
uchar* spell_orig_packet;
|
||||
uchar* spell_xor_packet;
|
||||
int16 spell_count;
|
||||
|
||||
uchar* raid_orig_packet;
|
||||
uchar* raid_xor_packet;
|
||||
//float speed;
|
||||
int16 target_id;
|
||||
Spawn* combat_target;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -33,7 +33,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
using namespace std;
|
||||
|
||||
// GroupOptions isn't used yet
|
||||
struct GroupOptions{
|
||||
struct GroupOptions {
|
||||
int8 loot_method;
|
||||
int8 loot_items_rarity;
|
||||
int8 auto_split;
|
||||
@ -59,9 +59,15 @@ struct GroupMemberInfo {
|
||||
int8 race_id;
|
||||
int8 class_id;
|
||||
bool leader;
|
||||
Client* client;
|
||||
Entity* member;
|
||||
Client* client;
|
||||
Entity* member;
|
||||
int32 mentor_target_char_id;
|
||||
bool is_client;
|
||||
int32 zone_id;
|
||||
int32 instance_id;
|
||||
string client_peer_address;
|
||||
int16 client_peer_port;
|
||||
bool is_raid_looter;
|
||||
};
|
||||
|
||||
/// <summary>Represents a players group in game</summary>
|
||||
@ -73,19 +79,22 @@ public:
|
||||
/// <summary>Adds a new member to the players group</summary>
|
||||
/// <param name='member'>Entity to add to the group, can be a Player or NPC</param>
|
||||
/// <returns>True if the member was added</returns>
|
||||
bool AddMember(Entity* member);
|
||||
|
||||
bool AddMember(Entity* member, bool is_leader);
|
||||
bool AddMemberFromPeer(std::string name, bool isleader, bool isclient, int8 class_id, sint32 hp_cur, sint32 hp_max, int16 level_cur, int16 level_max,
|
||||
sint32 power_cur, sint32 power_max, int8 race_id, std::string zonename, int32 mentor_target_char_id, int32 zone_id, int32 instance_id,
|
||||
std::string peer_client_address, int16 peer_client_port, bool is_raid_looter);
|
||||
/// <summary>Removes a member from the players group</summary>
|
||||
/// <param name='member'>Entity to remove from the player group</param>
|
||||
/// <returns>True if the member was removed</param>
|
||||
bool RemoveMember(Entity* member);
|
||||
bool RemoveMember(std::string name, bool is_client, int32 charID);
|
||||
|
||||
/// <summary>Removes all members from the group and destroys the group</summary>
|
||||
void Disband();
|
||||
|
||||
/// <summary>Sends updates to all the clients in the group</summary>
|
||||
/// <param name='exclude'>Client to exclude from the update</param>
|
||||
void SendGroupUpdate(Client* exclude = 0);
|
||||
void SendGroupUpdate(Client* exclude = 0, bool forceRaidUpdate = false);
|
||||
|
||||
/// <summary>Gets the total number of members in the group</summary>
|
||||
/// <returns>int32, number of members in the group</returns>
|
||||
@ -98,26 +107,40 @@ public:
|
||||
|
||||
void SimpleGroupMessage(const char* message);
|
||||
void SendGroupMessage(int8 type, const char* message, ...);
|
||||
void GroupChatMessage(Spawn* from, int32 language, const char* message);
|
||||
void GroupChatMessage(Spawn* from, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
|
||||
void GroupChatMessage(std::string fromName, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
|
||||
bool MakeLeader(Entity* new_leader);
|
||||
|
||||
std::string GetLeaderName();
|
||||
|
||||
bool ShareQuestWithGroup(Client* quest_sharer, Quest* quest);
|
||||
|
||||
|
||||
void RemoveClientReference(Client* remove);
|
||||
void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked=false);
|
||||
void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked = false);
|
||||
Entity* GetGroupMemberByPosition(Entity* seeker, int32 mapped_position);
|
||||
|
||||
void SetDefaultGroupOptions(GroupOptions* options=nullptr);
|
||||
|
||||
|
||||
void SetDefaultGroupOptions(GroupOptions* options = nullptr);
|
||||
bool GetDefaultGroupOptions(GroupOptions* options);
|
||||
|
||||
GroupOptions* GetGroupOptions() { return &group_options; }
|
||||
int8 GetLastLooterIndex() { return group_options.last_looted_index; }
|
||||
void SetNextLooterIndex(int8 new_index) { group_options.last_looted_index = new_index; }
|
||||
|
||||
int32 GetID() { return m_id; }
|
||||
|
||||
void GetRaidGroups(std::vector<int32>* groups);
|
||||
void ReplaceRaidGroups(std::vector<int32>* groups);
|
||||
bool IsInRaidGroup(int32 groupID, bool isLeaderGroup = false);
|
||||
void AddGroupToRaid(int32 groupID);
|
||||
void RemoveGroupFromRaid(int32 groupID);
|
||||
bool IsGroupRaid();
|
||||
void ClearGroupRaid();
|
||||
Mutex MGroupMembers; // Mutex for the group members
|
||||
private:
|
||||
GroupOptions group_options;
|
||||
int32 m_id; // ID of this group
|
||||
deque<GroupMemberInfo*> m_members; // List of members in this group
|
||||
|
||||
std::vector<int32> m_raidgroups;
|
||||
mutable std::shared_mutex MRaidGroups; // mutex for std vector
|
||||
};
|
||||
|
||||
/// <summary>Responsible for managing all the player groups in the world</summary>
|
||||
@ -130,17 +153,19 @@ public:
|
||||
/// <param name='group_id'>ID of the group to add a member to</param>
|
||||
/// <param name='member'>Entity* to add to the group</param>
|
||||
/// <returns>True if the member was added to the group</returns>
|
||||
bool AddGroupMember(int32 group_id, Entity* member);
|
||||
bool AddGroupMember(int32 group_id, Entity* member, bool is_leader = false);
|
||||
bool AddGroupMemberFromPeer(int32 group_id, GroupMemberInfo* info);
|
||||
|
||||
/// <summary>Removes a member from a group</summary>
|
||||
/// <param name='group_id'>ID of the group to remove a member from</param>
|
||||
/// <param name='member'>Entity* to remove from the group</param>
|
||||
/// <returns>True if the member was removed from the group</returns>
|
||||
bool RemoveGroupMember(int32 group_id, Entity* member);
|
||||
bool RemoveGroupMember(int32 group_id, std::string name, bool is_client, int32 charID);
|
||||
|
||||
/// <summary>Creates a new group with the provided Entity* as the leader</summary>
|
||||
/// <param name='leader'>The Entity* that will be the leader of the group</param>
|
||||
void NewGroup(Entity* leader);
|
||||
int32 NewGroup(Entity* leader, GroupOptions* goptions, int32 override_group_id = 0);
|
||||
|
||||
/// <summary>Removes the group from the group manager</summary>
|
||||
/// <param name='group_id'>ID of the group to remove</param>
|
||||
@ -151,11 +176,12 @@ public:
|
||||
/// <param name='member'>Player or NPC that is the target of the invite</param>
|
||||
/// <returns>Error code if invite was unsuccessful, 0 if successful</returns>
|
||||
int8 Invite(Player* leader, Entity* member);
|
||||
bool AddInvite(Player* leader, Entity* member);
|
||||
|
||||
/// <summary>Handles accepting of a group invite</summary>
|
||||
/// <param name='member'>Entity* that is accepting the invite</param>
|
||||
/// <returns>Error code if accepting the invite failed, 0 if successful<returns>
|
||||
int8 AcceptInvite(Entity* member);
|
||||
int8 AcceptInvite(Entity* member, int32* group_override_id = nullptr, bool auto_add_group = true);
|
||||
|
||||
/// <summary>Handles declining of a group invite</summary>
|
||||
/// <param name='member'>Entity* that is declining the invite</param>
|
||||
@ -169,7 +195,7 @@ public:
|
||||
/// <summary>Send updates to all the clients in the group</summary>
|
||||
/// <param name='group_id'>ID of the group to send updates to</param>
|
||||
/// <param name='exclude'>Client* to exclude from the update, usually the one that triggers the update</param>
|
||||
void SendGroupUpdate(int32 group_id, Client* exclude = 0);
|
||||
void SendGroupUpdate(int32 group_id, Client* exclude = 0, bool forceRaidUpdate = false);
|
||||
|
||||
|
||||
PlayerGroup* GetGroup(int32 group_id);
|
||||
@ -188,6 +214,8 @@ public:
|
||||
|
||||
void ClearPendingInvite(Entity* member);
|
||||
|
||||
std::string HasPendingInvite(Entity* member);
|
||||
|
||||
void RemoveGroupBuffs(int32 group_id, Client* client);
|
||||
|
||||
int32 GetGroupSize(int32 group_id);
|
||||
@ -198,7 +226,9 @@ public:
|
||||
void SimpleGroupMessage(int32 group_id, const char* message);
|
||||
void SendGroupMessage(int32 group_id, int8 type, const char* message, ...);
|
||||
void GroupMessage(int32 group_id, const char* message, ...);
|
||||
void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message);
|
||||
void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
|
||||
void GroupChatMessage(int32 group_id, std::string fromName, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
|
||||
void SendGroupChatMessage(int32 group_id, int16 channel, const char* message, ...);
|
||||
bool MakeLeader(int32 group_id, Entity* new_leader);
|
||||
void UpdateGroupBuffs();
|
||||
|
||||
@ -208,11 +238,29 @@ public:
|
||||
bool IsSpawnInGroup(int32 group_id, string name); // used in follow
|
||||
Player* GetGroupLeader(int32 group_id);
|
||||
|
||||
void UpdateGroupMemberInfoFromPeer(int32 group_id, std::string name, bool is_client, GroupMemberInfo* updateinfo);
|
||||
void SendPeerGroupData(std::string peerId);
|
||||
|
||||
void ClearGroupRaid(int32 groupID);
|
||||
void RemoveGroupFromRaid(int32 groupID, int32 targetGroupID);
|
||||
bool IsInRaidGroup(int32 groupID, int32 targetGroupID, bool isLeaderGroup = false);
|
||||
bool GetDefaultGroupOptions(int32 group_id, GroupOptions* options);
|
||||
void GetRaidGroups(int32 group_id, std::vector<int32>* groups);
|
||||
void ReplaceRaidGroups(int32 groupID, std::vector<int32>* newGroups);
|
||||
void SetGroupOptions(int32 groupID, GroupOptions* options);
|
||||
void SendWhoGroupMembers(Client* client, int32 groupID);
|
||||
void SendWhoRaidMembers(Client* client, int32 groupID);
|
||||
int8 AcceptRaidInvite(std::string acceptorName, int32 groupID);
|
||||
bool SendRaidInvite(Client* sender, Entity* target);
|
||||
void SplitWithGroupOrRaid(Client* client, int32 coin_plat, int32 coin_gold, int32 coin_silver, int32 coin_copper);
|
||||
bool IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, Client* client, int32 zoneID, int32 instanceID = 0);
|
||||
void ClearGroupRaidLooterFlag(int32 groupID);
|
||||
private:
|
||||
int32 m_nextGroupID; // Used to generate a new unique id for new groups
|
||||
|
||||
map<int32, PlayerGroup*> m_groups; // int32 is the group id, PlayerGroup* is a pointer to the actual group
|
||||
map<string, string> m_pendingInvites; // First string is the person invited to the group, second string is the leader of the group
|
||||
map<string, string> m_raidPendingInvites; // First string is the other group leader invited to the group, second string is the leader of the raid
|
||||
|
||||
mutable std::shared_mutex MGroups; // Mutex for the group map (m_groups)
|
||||
Mutex MPendingInvites; // Mutex for the pending invites map (m_pendingInvites)
|
||||
|
@ -1320,6 +1320,9 @@ EQ2Packet* Spawn::serialize(Player* player, int16 version){
|
||||
Spawn* Spawn::GetTarget(){
|
||||
Spawn* ret = 0;
|
||||
|
||||
if(!GetZone())
|
||||
return 0;
|
||||
|
||||
// only attempt to get a spawn if we had a target stored
|
||||
if (target != 0)
|
||||
{
|
||||
@ -4933,14 +4936,7 @@ void Spawn::AddIgnoredWidget(int32 id) {
|
||||
void Spawn::SendGroupUpdate() {
|
||||
if (IsEntity() && ((Entity*)this)->GetGroupMemberInfo()) {
|
||||
((Entity*)this)->UpdateGroupMemberInfo();
|
||||
if (IsPlayer()) {
|
||||
Client* client = ((Player*)this)->GetClient();
|
||||
if(client) {
|
||||
world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id, client);
|
||||
}
|
||||
}
|
||||
else
|
||||
world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id);
|
||||
world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4996,7 +4992,7 @@ bool Spawn::HasSpawnLootWindowCompleted(int32 spawn_id) {
|
||||
|
||||
bool Spawn::HasSpawnNeedGreedEntry(int32 item_id, int32 spawn_id) {
|
||||
for (auto [itr, rangeEnd] = need_greed_items.equal_range(item_id); itr != rangeEnd; itr++) {
|
||||
LogWrite(LOOT__DEBUG, 8, "Loot", "%s: HasSpawnNeedGreedEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second.first);
|
||||
LogWrite(LOOT__DEBUG, 1, "Loot", "%s: HasSpawnNeedGreedEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second.first);
|
||||
if (spawn_id == itr->second.first) {
|
||||
return true;
|
||||
}
|
||||
@ -5006,7 +5002,7 @@ bool Spawn::HasSpawnNeedGreedEntry(int32 item_id, int32 spawn_id) {
|
||||
|
||||
bool Spawn::HasSpawnLottoEntry(int32 item_id, int32 spawn_id) {
|
||||
for (auto [itr, rangeEnd] = lotto_items.equal_range(item_id); itr != rangeEnd; itr++) {
|
||||
LogWrite(LOOT__DEBUG, 8, "Loot", "%s: HasSpawnLottoEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second);
|
||||
LogWrite(LOOT__DEBUG, 1, "Loot", "%s: HasSpawnLottoEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second);
|
||||
if (spawn_id == itr->second) {
|
||||
return true;
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ void SpellProcess::RemoveCaster(Spawn* caster){
|
||||
if(spell->caster == caster) {
|
||||
spell->caster = nullptr;
|
||||
}
|
||||
if(spell->initial_target == caster->GetID())
|
||||
spell->initial_target = 0;
|
||||
spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
@ -2051,14 +2053,16 @@ void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, boo
|
||||
|
||||
bool foundMatch = false;
|
||||
|
||||
spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
|
||||
for (i = 0; i < spell->targets.size(); i++){
|
||||
if (spawn->GetID() == spell->targets.at(i)){
|
||||
foundMatch = true;
|
||||
break;
|
||||
if(spawn != spell->caster) {
|
||||
spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
|
||||
for (i = 0; i < spell->targets.size(); i++){
|
||||
if (spawn->GetID() == spell->targets.at(i)){
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
if(foundMatch) {
|
||||
if (spawn->IsEntity())
|
||||
((Entity*)spawn)->RemoveSpellEffect(spell);
|
||||
|
450
source/WorldServer/Web/HTTPSClient.cpp
Normal file
450
source/WorldServer/Web/HTTPSClient.cpp
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
|
||||
EQ2Emu 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EQ2Emu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "HTTPSClient.h"
|
||||
#include "PeerManager.h"
|
||||
|
||||
#include "../net.h"
|
||||
#include "../../common/Log.h"
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost_net = boost::asio; // From <boost/asio.hpp>
|
||||
extern NetConnection net;
|
||||
extern PeerManager peer_manager;
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
std::string base64_encode(const std::string& input) {
|
||||
std::string encoded_string;
|
||||
unsigned char const* bytes_to_encode = reinterpret_cast<const unsigned char*>(input.c_str());
|
||||
size_t in_len = input.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (i = 0; (i < 4); i++)
|
||||
encoded_string += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
encoded_string += base64_chars[char_array_4[j]];
|
||||
|
||||
while ((i++ < 3))
|
||||
encoded_string += '=';
|
||||
}
|
||||
|
||||
return encoded_string;
|
||||
}
|
||||
|
||||
HTTPSClient::HTTPSClient(const std::string& certFile, const std::string& keyFile)
|
||||
: certFile(certFile), keyFile(keyFile) {}
|
||||
|
||||
std::shared_ptr<boost::asio::ssl::context> HTTPSClient::createSSLContext() {
|
||||
auto sslCtx = std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv13_client);
|
||||
sslCtx->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
|
||||
sslCtx->use_certificate_file(certFile, boost::asio::ssl::context::pem);
|
||||
sslCtx->use_private_key_file(keyFile, boost::asio::ssl::context::pem);
|
||||
sslCtx->set_verify_mode(ssl::verify_peer);
|
||||
sslCtx->set_default_verify_paths();
|
||||
return sslCtx;
|
||||
}
|
||||
|
||||
void HTTPSClient::parseAndStoreCookies(const http::response<http::string_body>& res) {
|
||||
if (res.count(http::field::set_cookie)) {
|
||||
std::istringstream stream(res[http::field::set_cookie].to_string());
|
||||
std::string token;
|
||||
|
||||
// Parse "Set-Cookie" field for name-value pairs
|
||||
while (std::getline(stream, token, ';')) {
|
||||
auto pos = token.find('=');
|
||||
if (pos != std::string::npos) {
|
||||
std::string name = token.substr(0, pos);
|
||||
std::string value = token.substr(pos + 1);
|
||||
cookies[name] = value; // Store each cookie
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string HTTPSClient::buildCookieHeader() const {
|
||||
std::string cookieHeader;
|
||||
for (const auto& [name, value] : cookies) {
|
||||
cookieHeader += name + "=" + value;
|
||||
}
|
||||
return cookieHeader;
|
||||
}
|
||||
|
||||
std::string HTTPSClient::sendRequest(const std::string& server, const std::string& port, const std::string& target) {
|
||||
try {
|
||||
boost::asio::io_context ioContext;
|
||||
|
||||
// SSL and TCP setup
|
||||
auto sslCtx = createSSLContext();
|
||||
auto stream = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(ioContext, *sslCtx);
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(ioContext);
|
||||
auto results = resolver->resolve(server, port);
|
||||
|
||||
// Persistent objects to manage response, request, and buffer
|
||||
auto res = std::make_shared<http::response<http::string_body>>();
|
||||
auto buffer = std::make_shared<boost::beast::flat_buffer>();
|
||||
auto req = std::make_shared<http::request<http::string_body>>(http::verb::get, target, 11);
|
||||
|
||||
// SNI hostname (required for many hosts)
|
||||
if (!SSL_set_tlsext_host_name(stream->native_handle(), server.c_str())) {
|
||||
throw boost::beast::system_error(
|
||||
boost::beast::error_code(static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()));
|
||||
}
|
||||
|
||||
// Prepare request headers
|
||||
req->set(http::field::host, server);
|
||||
req->set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||
req->set(boost::beast::http::field::connection, "close");
|
||||
req->set(http::field::content_type, "application/json");
|
||||
if (!cookies.empty()) {
|
||||
req->set(http::field::cookie, buildCookieHeader());
|
||||
}
|
||||
else {
|
||||
std::string credentials = net.GetCmdUser() + ":" + net.GetCmdPassword();
|
||||
std::string encodedCredentials = base64_encode(credentials);
|
||||
req->set(http::field::authorization, "Basic " + encodedCredentials);
|
||||
}
|
||||
|
||||
// Step 1: Asynchronous connect with timeout
|
||||
auto connect_timer = std::make_shared<boost::asio::steady_timer>(ioContext);
|
||||
connect_timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
connect_timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel(); // Cancel operation on timeout
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = std::make_shared<boost::asio::steady_timer>(ioContext, std::chrono::seconds(2));
|
||||
boost::asio::async_connect(stream->lowest_layer(), results,
|
||||
[stream, connect_timer, req, buffer, res, timer, server, port, target](boost::system::error_code ec, const auto&) {
|
||||
connect_timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Asynchronous handshake with timeout
|
||||
timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel();
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
stream->async_handshake(boost::asio::ssl::stream_base::client,
|
||||
[stream, timer, req, buffer, res, server, port, target](boost::system::error_code ec) {
|
||||
timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Asynchronous write request
|
||||
timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel();
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
http::async_write(*stream, *req,
|
||||
[stream, buffer, res, timer, server, port, target](boost::system::error_code ec, std::size_t) {
|
||||
timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4: Asynchronous read response
|
||||
timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel();
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
http::async_read(*stream, *buffer, *res,
|
||||
[stream, timer, res, server, port, target](boost::system::error_code ec, std::size_t) {
|
||||
timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 5: Shutdown the stream
|
||||
stream->async_shutdown([stream, server, port](boost::system::error_code ec) {
|
||||
if (ec && ec != boost::asio::error::eof) {
|
||||
// ignore these
|
||||
//std::cerr << "Shutdown error: " << ec.message() << std::endl;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ioContext.run();
|
||||
|
||||
// Store cookies from the response
|
||||
if (res->base().count(http::field::set_cookie) > 0) {
|
||||
auto set_cookie_value = res->base()[http::field::set_cookie].to_string();
|
||||
std::istringstream stream(set_cookie_value);
|
||||
std::string token;
|
||||
|
||||
// Parse "Set-Cookie" field for name-value pairs
|
||||
while (std::getline(stream, token, ';')) {
|
||||
auto pos = token.find('=');
|
||||
if (pos != std::string::npos) {
|
||||
std::string name = token.substr(0, pos);
|
||||
std::string value = token.substr(pos + 1);
|
||||
cookies[name] = value; // Store each cookie
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res->body() == "Unauthorized") {
|
||||
cookies.clear();
|
||||
}
|
||||
|
||||
// Return the response body, if available
|
||||
return res->body();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Request Error %s for %s:%s/%s", __FUNCTION__, e.what() ? e.what() : "??", server.c_str(), port.c_str(), target.c_str());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::string HTTPSClient::sendPostRequest(const std::string& server, const std::string& port, const std::string& target, const std::string& jsonPayload) {
|
||||
try {
|
||||
boost::asio::io_context ioContext;
|
||||
|
||||
// SSL and TCP setup
|
||||
auto sslCtx = createSSLContext();
|
||||
auto stream = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>(ioContext, *sslCtx);
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(ioContext);
|
||||
auto results = resolver->resolve(server, port);
|
||||
|
||||
// Persistent objects to manage response, request, and buffer
|
||||
auto res = std::make_shared<http::response<http::string_body>>();
|
||||
auto buffer = std::make_shared<boost::beast::flat_buffer>();
|
||||
auto req = std::make_shared<http::request<http::string_body>>(http::verb::post, target, 11);
|
||||
|
||||
// SNI hostname (required for many hosts)
|
||||
if (!SSL_set_tlsext_host_name(stream->native_handle(), server.c_str())) {
|
||||
throw boost::beast::system_error(
|
||||
boost::beast::error_code(static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()));
|
||||
}
|
||||
|
||||
// Prepare HTTP POST request with JSON payload
|
||||
req->set(http::field::host, server);
|
||||
req->set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||
req->set(boost::beast::http::field::connection, "close");
|
||||
req->set(http::field::content_type, "application/json");
|
||||
if (!cookies.empty()) {
|
||||
req->set(http::field::cookie, buildCookieHeader());
|
||||
}
|
||||
else {
|
||||
std::string credentials = net.GetCmdUser() + ":" + net.GetCmdPassword();
|
||||
std::string encodedCredentials = base64_encode(credentials);
|
||||
req->set(http::field::authorization, "Basic " + encodedCredentials);
|
||||
}
|
||||
|
||||
req->body() = jsonPayload;
|
||||
req->prepare_payload();
|
||||
|
||||
// Step 1: Asynchronous connect with timeout
|
||||
auto connect_timer = std::make_shared<boost::asio::steady_timer>(ioContext);
|
||||
connect_timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
connect_timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel(); // Cancel operation on timeout
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = std::make_shared<boost::asio::steady_timer>(ioContext, std::chrono::seconds(2));
|
||||
boost::asio::async_connect(stream->lowest_layer(), results,
|
||||
[stream, connect_timer, req, buffer, res, timer, server, port, target](boost::system::error_code ec, const auto&) {
|
||||
connect_timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Asynchronous handshake with timeout
|
||||
timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel();
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
stream->async_handshake(boost::asio::ssl::stream_base::client,
|
||||
[stream, timer, req, buffer, res, server, port, target](boost::system::error_code ec) {
|
||||
timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Asynchronous write request
|
||||
timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel();
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
http::async_write(*stream, *req,
|
||||
[stream, buffer, res, timer, server, port, target](boost::system::error_code ec, std::size_t) {
|
||||
timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4: Asynchronous read response
|
||||
timer->expires_after(std::chrono::seconds(2));
|
||||
|
||||
timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
stream->lowest_layer().cancel();
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
}
|
||||
});
|
||||
|
||||
http::async_read(*stream, *buffer, *res,
|
||||
[stream, timer, res, server, port, target](boost::system::error_code ec, std::size_t) {
|
||||
timer->cancel();
|
||||
if (ec) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
|
||||
peer_manager.SetPeerErrorState(server, port);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 5: Shutdown the stream
|
||||
stream->async_shutdown([stream, server, port](boost::system::error_code ec) {
|
||||
if (ec && ec != boost::asio::error::eof) {
|
||||
// ignore these
|
||||
//std::cerr << "Shutdown error: " << ec.message() << std::endl;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ioContext.run();
|
||||
|
||||
// Store cookies from the response
|
||||
if (res->base().count(http::field::set_cookie) > 0) {
|
||||
auto set_cookie_value = res->base()[http::field::set_cookie].to_string();
|
||||
std::istringstream stream(set_cookie_value);
|
||||
std::string token;
|
||||
|
||||
// Parse "Set-Cookie" field for name-value pairs
|
||||
while (std::getline(stream, token, ';')) {
|
||||
auto pos = token.find('=');
|
||||
if (pos != std::string::npos) {
|
||||
std::string name = token.substr(0, pos);
|
||||
std::string value = token.substr(pos + 1);
|
||||
cookies[name] = value; // Store each cookie
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res->body() == "Unauthorized") {
|
||||
cookies.clear();
|
||||
}
|
||||
|
||||
// Return the response body, if available
|
||||
return res->body();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Request Error %s for %s:%s/%s", __FUNCTION__, e.what() ? e.what() : "??", server.c_str(), port.c_str(), target.c_str());
|
||||
return {};
|
||||
}
|
||||
}
|
61
source/WorldServer/Web/HTTPSClient.h
Normal file
61
source/WorldServer/Web/HTTPSClient.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
|
||||
EQ2Emu 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EQ2Emu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HTTPSCLIENT_H
|
||||
#define HTTPSCLIENT_H
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ssl = boost::asio::ssl;
|
||||
namespace asio = boost::asio;
|
||||
namespace beast = boost::beast;
|
||||
namespace http = beast::http;
|
||||
|
||||
class HTTPSClient {
|
||||
public:
|
||||
HTTPSClient(const std::string& certFile, const std::string& keyFile);
|
||||
|
||||
// Send a request with stored cookies and return response as string
|
||||
std::string sendRequest(const std::string& server, const std::string& port, const std::string& target);
|
||||
|
||||
// Send a POST request with a JSON payload and return response as string
|
||||
std::string sendPostRequest(const std::string& server, const std::string& port, const std::string& target, const std::string& jsonPayload);
|
||||
|
||||
std::string getServer() const { return server; }
|
||||
std::string getPort() const { return port; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> cookies;
|
||||
|
||||
std::string certFile;
|
||||
std::string keyFile;
|
||||
std::string server;
|
||||
std::string port;
|
||||
|
||||
void parseAndStoreCookies(const http::response<http::string_body>& res);
|
||||
std::shared_ptr<boost::asio::ssl::context> createSSLContext(); // New helper function
|
||||
std::string buildCookieHeader() const;
|
||||
};
|
||||
|
||||
#endif // HTTPSCLIENT_H
|
773
source/WorldServer/Web/HTTPSClientPool.cpp
Normal file
773
source/WorldServer/Web/HTTPSClientPool.cpp
Normal file
@ -0,0 +1,773 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
|
||||
EQ2Emu 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EQ2Emu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "HTTPSClientPool.h"
|
||||
#include "PeerManager.h"
|
||||
#include "../net.h"
|
||||
#include "../World.h"
|
||||
#include "../LoginServer.h"
|
||||
#include "../WorldDatabase.h"
|
||||
#include "../Guilds/Guild.h"
|
||||
#include "../../common/Log.h"
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <regex>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
extern NetConnection net;
|
||||
extern PeerManager peer_manager;
|
||||
extern World world;
|
||||
extern LoginServer loginserver;
|
||||
extern ZoneList zone_list;
|
||||
extern WorldDatabase database;
|
||||
extern GuildList guild_list;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
|
||||
// Handler functions for each endpoint
|
||||
void AddCharAuth(boost::property_tree::ptree tree) {
|
||||
int32 success = 0;
|
||||
std::string charName(""), zoneName("");
|
||||
int32 acct_id = 0;
|
||||
int32 zoneId = 0, instanceId = 0;
|
||||
bool firstLogin = false;
|
||||
std::string worldAddr(""), internalWorldAddr(""), clientIP("");
|
||||
int16 worldPort = 0;
|
||||
int32 charID = 0;
|
||||
int32 worldID = 0, fromID = 0;
|
||||
int32 loginKey = 0;
|
||||
if (auto char_name = tree.get_optional<std::string>("character_name")) {
|
||||
charName = char_name.get();
|
||||
}
|
||||
if (auto successful = tree.get_optional<int32>("success")) {
|
||||
success = successful.get();
|
||||
}
|
||||
if (auto account_id = tree.get_optional<int32>("account_id")) {
|
||||
acct_id = account_id.get();
|
||||
}
|
||||
if (auto zone_name = tree.get_optional<std::string>("zone_name")) {
|
||||
zoneName = zone_name.get();
|
||||
}
|
||||
if (auto zone_id = tree.get_optional<int32>("zone_id")) {
|
||||
zoneId = zone_id.get();
|
||||
}
|
||||
if (auto instance_id = tree.get_optional<int32>("instance_id")) {
|
||||
instanceId = instance_id.get();
|
||||
}
|
||||
if (auto first_login = tree.get_optional<bool>("first_login")) {
|
||||
firstLogin = first_login.get();
|
||||
}
|
||||
|
||||
if (auto peerclientaddr = tree.get_optional<std::string>("peer_client_address")) {
|
||||
worldAddr = peerclientaddr.get();
|
||||
}
|
||||
if (auto peerclient_internaladdr = tree.get_optional<std::string>("peer_client_internal_address")) {
|
||||
internalWorldAddr = peerclient_internaladdr.get();
|
||||
}
|
||||
if (auto peerclientport = tree.get_optional<int16>("peer_client_port")) {
|
||||
worldPort = peerclientport.get();
|
||||
}
|
||||
if (auto clientip = tree.get_optional<std::string>("client_ip")) {
|
||||
clientIP = clientip.get();
|
||||
}
|
||||
if (auto character_id = tree.get_optional<int32>("character_id")) {
|
||||
charID = character_id.get();
|
||||
}
|
||||
if (auto login_key = tree.get_optional<int32>("login_key")) {
|
||||
loginKey = login_key.get();
|
||||
}
|
||||
if (auto world_id = tree.get_optional<int32>("world_id")) {
|
||||
worldID = world_id.get();
|
||||
}
|
||||
if (auto from_id = tree.get_optional<int32>("from_id")) {
|
||||
fromID = from_id.get();
|
||||
}
|
||||
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Handling AddCharAuth %s (%u) for zone %u instance %u", __FUNCTION__, charName.c_str(), charID, zoneId, instanceId);
|
||||
|
||||
if (firstLogin) {
|
||||
loginserver.SendCharApprovedLogin(success, worldAddr, internalWorldAddr, clientIP, worldPort, acct_id, charID, loginKey, worldID, fromID);
|
||||
}
|
||||
else if (acct_id > 0 && charName.length() > 0) {
|
||||
world.ClientAuthApproval(success, charName, acct_id, zoneName, zoneId, instanceId, firstLogin);
|
||||
}
|
||||
}
|
||||
|
||||
void StartZone(boost::property_tree::ptree tree) {
|
||||
std::string peerWebAddress("");
|
||||
int16 peerWebPort = 0;
|
||||
if (auto addr = tree.get_optional<std::string>("peer_web_address")) {
|
||||
peerWebAddress = addr.get();
|
||||
}
|
||||
if (auto port = tree.get_optional<int16>("peer_web_port")) {
|
||||
peerWebPort = port.get();
|
||||
}
|
||||
|
||||
std::string id = peer_manager.isPeer(peerWebAddress, peerWebPort);
|
||||
|
||||
if (id.size() > 0) {
|
||||
std::string strPort = std::to_string(peerWebPort);
|
||||
auto client = peer_https_pool.getOrCreateClient(id, peerWebAddress, strPort);
|
||||
if (client) {
|
||||
auto responseZones = client->sendRequest(peerWebAddress, strPort, "/zones"); // Assumes HTTPSClient has a get method
|
||||
// Load the JSON data into the property tree
|
||||
std::istringstream json_stream(responseZones);
|
||||
if (json_stream.str().empty()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Stream Empty for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
|
||||
}
|
||||
else if (json_stream.fail()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Failed State for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
|
||||
}
|
||||
else if (json_stream.bad()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Stream Bad for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
|
||||
}
|
||||
else if (json_stream.eof()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Stream EOF for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
|
||||
}
|
||||
else {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json(json_stream, pt);
|
||||
peer_manager.updateZoneTree(id, pt);
|
||||
LogWrite(PEERING__DEBUG, 5, "Peering", "%s: StartZone Update for %s:%s/zones complete, zone tree updated.", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendGlobalMessage(boost::property_tree::ptree tree) {
|
||||
int32 success = 0;
|
||||
int8 language = 0;
|
||||
int16 in_channel = 0;
|
||||
std::string toName(""), fromName(""), msg(""), awaymsg("");
|
||||
int8 away_language = 0;
|
||||
int32 group_id = 0;
|
||||
if (auto successful = tree.get_optional<int32>("success")) {
|
||||
success = successful.get();
|
||||
}
|
||||
if (auto name = tree.get_optional<std::string>("to_name")) {
|
||||
toName = name.get();
|
||||
}
|
||||
if (auto name = tree.get_optional<std::string>("from_name")) {
|
||||
fromName = name.get();
|
||||
}
|
||||
if (auto message = tree.get_optional<std::string>("message")) {
|
||||
msg = message.get();
|
||||
}
|
||||
if (auto from_language = tree.get_optional<int8>("from_language")) {
|
||||
language = from_language.get();
|
||||
}
|
||||
if (auto channel = tree.get_optional<int16>("channel")) {
|
||||
in_channel = channel.get();
|
||||
}
|
||||
|
||||
if (auto msg = tree.get_optional<std::string>("away_message")) {
|
||||
awaymsg = msg.get();
|
||||
}
|
||||
if (auto away_lang = tree.get_optional<int8>("away_language")) {
|
||||
away_language = away_lang.get();
|
||||
}
|
||||
if (auto group = tree.get_optional<int32>("group_id")) {
|
||||
group_id = group.get();
|
||||
}
|
||||
|
||||
switch (in_channel) {
|
||||
case CHANNEL_PRIVATE_TELL: {
|
||||
Client* from_client = zone_list.GetClientByCharName(fromName.c_str());
|
||||
if (from_client) {
|
||||
if (success) {
|
||||
from_client->HandleTellMessage(from_client->GetPlayer()->GetName(), msg.c_str(), toName.c_str(), language);
|
||||
if (awaymsg.size() > 0) {
|
||||
from_client->HandleTellMessage(toName.c_str(), awaymsg.c_str(), from_client->GetPlayer()->GetName(), away_language);
|
||||
}
|
||||
}
|
||||
else {
|
||||
from_client->SimpleMessage(CHANNEL_COLOR_CHAT_RELATIONSHIP, "That character does not exist.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleNewGroup(boost::property_tree::ptree tree) {
|
||||
|
||||
int32 success = 0;
|
||||
if (auto successful = tree.get_optional<int32>("success")) {
|
||||
success = successful.get();
|
||||
}
|
||||
if (!net.is_primary) {
|
||||
// we are a peer we ask primary for a new group and get the response
|
||||
GroupOptions options;
|
||||
std::string leader(""), member("");
|
||||
int32 group_id = 0;
|
||||
int32 member_entity_id = 0;
|
||||
if (auto leader_name = tree.get_optional<std::string>("leader_name")) {
|
||||
leader = leader_name.get();
|
||||
}
|
||||
if (auto member_name = tree.get_optional<std::string>("member_name")) {
|
||||
member = member_name.get();
|
||||
}
|
||||
if (auto lootmethod = tree.get_optional<int8>("loot_method")) {
|
||||
options.loot_method = lootmethod.get();
|
||||
}
|
||||
if (auto itemrarity = tree.get_optional<int8>("loot_item_rarity")) {
|
||||
options.loot_items_rarity = itemrarity.get();
|
||||
}
|
||||
if (auto autosplit = tree.get_optional<int8>("auto_split")) {
|
||||
options.auto_split = autosplit.get();
|
||||
}
|
||||
if (auto defaultyell = tree.get_optional<int8>("default_yell")) {
|
||||
options.default_yell = defaultyell.get();
|
||||
}
|
||||
if (auto grouplockmethod = tree.get_optional<int8>("group_lock_method")) {
|
||||
options.group_lock_method = grouplockmethod.get();
|
||||
}
|
||||
if (auto groupautolock = tree.get_optional<int8>("group_auto_lock")) {
|
||||
options.group_autolock = groupautolock.get();
|
||||
}
|
||||
if (auto soloautolock = tree.get_optional<int8>("solo_auto_lock")) {
|
||||
options.solo_autolock = soloautolock.get();
|
||||
}
|
||||
if (auto autoloot = tree.get_optional<int8>("auto_loot_method")) {
|
||||
options.auto_loot_method = autoloot.get();
|
||||
}
|
||||
if (auto lastlootindex = tree.get_optional<int8>("last_looted_index")) {
|
||||
options.last_looted_index = lastlootindex.get();
|
||||
}
|
||||
if (auto groupid = tree.get_optional<int32>("group_id")) {
|
||||
group_id = groupid.get();
|
||||
}
|
||||
|
||||
if (auto entityid = tree.get_optional<int32>("member_entity_id")) {
|
||||
member_entity_id = entityid.get();
|
||||
}
|
||||
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Add New Group %u with member %s (%u) leader %s", __FUNCTION__, group_id, member.c_str(), member_entity_id, leader.c_str());
|
||||
Client* member_client = zone_list.GetClientByCharName(member.c_str());
|
||||
Client* client_leader = zone_list.GetClientByCharName(leader.c_str());
|
||||
if (success) {
|
||||
if (!member_client) {
|
||||
if (client_leader && client_leader->GetPlayer()->GetZone()) {
|
||||
Spawn* spawn = client_leader->GetPlayer()->GetZone()->GetSpawnByID(member_entity_id);
|
||||
if (spawn && spawn->IsEntity()) {
|
||||
world.GetGroupManager()->AcceptInvite((Entity*)spawn, &group_id);
|
||||
}
|
||||
else {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Failed Adding New Group %u, entity %s (%u) did not exist.", __FUNCTION__, group_id, member.c_str(), member_entity_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Failed Adding New Group %u, member %s (%u) did not exist.", __FUNCTION__, group_id, member.c_str(), member_entity_id);
|
||||
}
|
||||
}
|
||||
else if (!client_leader) {
|
||||
if (member_client)
|
||||
member_client->HandleGroupAcceptResponse(2);
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Failed Adding New Group %u, leader %s did not exist.", __FUNCTION__, group_id, leader.c_str());
|
||||
}
|
||||
else {
|
||||
world.GetGroupManager()->AcceptInvite(member_client->GetPlayer(), &group_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HandleCreateGuild(boost::property_tree::ptree tree) {
|
||||
|
||||
bool success = false;
|
||||
int32 guildID = 0;
|
||||
std::string leaderName("");
|
||||
if (auto successful = tree.get_optional<bool>("success")) {
|
||||
success = successful.get();
|
||||
}
|
||||
|
||||
if (auto guild_id = tree.get_optional<int32>("guild_id")) {
|
||||
guildID = guild_id.get();
|
||||
}
|
||||
|
||||
if (auto name = tree.get_optional<std::string>("leader_name")) {
|
||||
leaderName = name.get();
|
||||
}
|
||||
|
||||
if (net.is_primary) {
|
||||
// we send out to peers
|
||||
}
|
||||
else if (guildID) {
|
||||
database.LoadGuild(guildID);
|
||||
Guild* guild = guild_list.GetGuild(guildID);
|
||||
Client* leader = zone_list.GetClientByCharName(leaderName.c_str());
|
||||
if (leader && guild && !leader->GetPlayer()->GetGuild()) {
|
||||
guild->AddNewGuildMember(leader, 0, GUILD_RANK_LEADER);
|
||||
database.SaveGuildMembers(guild);
|
||||
if (leader && leader->GetPlayer()->GetGroupMemberInfo()) {
|
||||
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
||||
|
||||
PlayerGroup* group = world.GetGroupManager()->GetGroup(leader->GetPlayer()->GetGroupMemberInfo()->group_id);
|
||||
if (group)
|
||||
{
|
||||
GroupMemberInfo* gmi = nullptr;
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
||||
deque<GroupMemberInfo*>* members = group->GetMembers();
|
||||
for (itr = members->begin(); itr != members->end(); itr++) {
|
||||
gmi = *itr;
|
||||
if (gmi->client && gmi->client != leader && !gmi->client->GetPlayer()->GetGuild())
|
||||
guild->InvitePlayer(gmi->client, leader->GetPlayer()->GetName());
|
||||
}
|
||||
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define a type for handler functions
|
||||
using HandlerFunction = std::function<void(boost::property_tree::ptree tree)>;
|
||||
|
||||
// Set up the endpoint-to-function map
|
||||
std::unordered_map<std::string, HandlerFunction> endpointHandlers = {
|
||||
{"/addcharauth", AddCharAuth},
|
||||
{"/startzone", StartZone},
|
||||
{"/sendglobalmessage", SendGlobalMessage},
|
||||
{"/newgroup", HandleNewGroup},
|
||||
{"/createguild", HandleCreateGuild}
|
||||
};
|
||||
|
||||
HTTPSClientPool::HTTPSClientPool() {}
|
||||
|
||||
void HTTPSClientPool::init(const std::string& cert, const std::string& key) {
|
||||
certFile = cert;
|
||||
keyFile = key;
|
||||
pollingInterval = 1000;
|
||||
|
||||
int32 numThreads = 2;
|
||||
// Start worker threads
|
||||
for (int32 i = 0; i < numThreads; ++i) {
|
||||
workers.emplace_back(&HTTPSClientPool::workerFunction, this);
|
||||
}
|
||||
}
|
||||
|
||||
HTTPSClientPool::~HTTPSClientPool() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
stop = true;
|
||||
}
|
||||
condition.notify_all();
|
||||
for (std::thread& worker : workers) {
|
||||
if (worker.joinable()) {
|
||||
worker.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPSClientPool::addPeerClient(const std::string& peerId, const std::string& server, const std::string& port, const std::string& authEndpoint) {
|
||||
bool newClient = false;
|
||||
// Check if client already exists for the specified (server, port) pair
|
||||
auto it = clients.find(std::make_pair(server, port));
|
||||
if (it == clients.end()) {
|
||||
newClient = true;
|
||||
}
|
||||
|
||||
if (newClient) {
|
||||
auto client = getOrCreateClient(peerId, server, port);
|
||||
}
|
||||
}
|
||||
|
||||
boost::property_tree::ptree HTTPSClientPool::sendRequestToPeer(const std::string& peerId, const std::string& target) {
|
||||
auto client = getClient(peerId);
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
if (!client) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client for peer %s could not be found.", __FUNCTION__, peerId.c_str());
|
||||
return pt;
|
||||
}
|
||||
|
||||
// Retrieve the response from the client
|
||||
std::string responseBody = client->sendRequest(client->getServer(), client->getPort(), target);
|
||||
|
||||
if (responseBody.size() < 1) {
|
||||
|
||||
int16 peer_port = 0;
|
||||
try {
|
||||
peer_port = std::stoul(client->getPort());
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error for convering peer port for %s: %s.", __FUNCTION__, peerId.c_str(), e.what() ? e.what() : "??");
|
||||
}
|
||||
HealthStatus curStatus = peer_manager.getPeerStatus(client->getServer(), peer_port);
|
||||
switch (curStatus) {
|
||||
case HealthStatus::WARN: {
|
||||
peer_manager.updateHealth(peerId, HealthStatus::ERROR);
|
||||
break;
|
||||
}
|
||||
case HealthStatus::ERROR: {
|
||||
peer_manager.updateHealth(peerId, HealthStatus::SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
peer_manager.updateHealth(peerId, HealthStatus::WARN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Parse the response as JSON
|
||||
try {
|
||||
std::istringstream responseStream(responseBody);
|
||||
boost::property_tree::read_json(responseStream, pt);
|
||||
}
|
||||
catch (const boost::property_tree::json_parser_error& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Parsing error for %s: %s.", __FUNCTION__, peerId.c_str(), e.what() ? e.what() : "??");
|
||||
}
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
boost::property_tree::ptree HTTPSClientPool::sendPostRequestToPeer(const std::string& peerId, const std::string& target, const std::string& jsonPayload) {
|
||||
auto client = getClient(peerId);
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
if (!client) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client for peer %s could not be found.", __FUNCTION__, peerId.c_str());
|
||||
return pt;
|
||||
}
|
||||
|
||||
// Retrieve the response from the client
|
||||
std::string responseBody = client->sendPostRequest(client->getServer(), client->getPort(), target, jsonPayload);
|
||||
|
||||
// Parse the response as JSON
|
||||
try {
|
||||
std::istringstream responseStream(responseBody);
|
||||
boost::property_tree::read_json(responseStream, pt);
|
||||
}
|
||||
catch (const boost::property_tree::json_parser_error& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client for peer %s could not be found.", __FUNCTION__, peerId.c_str());
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
std::shared_ptr<HTTPSClient> HTTPSClientPool::getClient(const std::string& peerId) {
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
// Lookup the client by ID
|
||||
auto idIt = clientsById.find(peerId);
|
||||
if (idIt != clientsById.end()) {
|
||||
return idIt->second;
|
||||
}
|
||||
return nullptr; // Return nullptr if no client exists with the given ID
|
||||
}
|
||||
|
||||
std::shared_ptr<HTTPSClient> HTTPSClientPool::getOrCreateClient(const std::string& id, const std::string& server, const std::string& port) {
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
// Create a key based on (server, port)
|
||||
auto clientKey = std::make_pair(server, port);
|
||||
|
||||
// Check if client already exists for the specified (server, port) pair
|
||||
auto it = clients.find(clientKey);
|
||||
if (it != clients.end()) {
|
||||
// Client already exists, return the existing client
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// No existing client for this (server, port), create and store a new HTTPSClient
|
||||
auto client = std::make_shared<HTTPSClient>(certFile, keyFile);
|
||||
clients[clientKey] = client;
|
||||
clientsById[id] = client;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void HTTPSClientPool::pollPeerHealth(const std::string& server, const std::string& port) {
|
||||
int16 web_worldport = 0;
|
||||
try {
|
||||
web_worldport = std::stoul(port);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Getting port for peer %s:%s could not be complete.", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
std::string id = peer_manager.isPeer(server, web_worldport);
|
||||
if (id.size() < 1) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error finding peer %s:%s.", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else {
|
||||
auto client = getOrCreateClient(id, port, server + ":" + port);
|
||||
int16 interval = pollingInterval;
|
||||
while (running.load()) {
|
||||
interval++;
|
||||
if (interval > pollingInterval) {
|
||||
HealthStatus curStatus = peer_manager.getPeerStatus(server, web_worldport);
|
||||
id = peer_manager.isPeer(server, web_worldport);
|
||||
try {
|
||||
auto response = client->sendRequest(server, port, "/status"); // Assumes HTTPSClient has a get method
|
||||
//std::cout << "Health check response from " << server << ":" << port << " - " << response << std::endl;
|
||||
|
||||
boost::property_tree::ptree json_tree;
|
||||
std::istringstream json_stream(response);
|
||||
boost::property_tree::read_json(json_stream, json_tree);
|
||||
std::string online_status;
|
||||
int16 peer_priority = 65535;
|
||||
bool peer_primary = false;
|
||||
if (auto status = json_tree.get_optional<std::string>("world_status")) {
|
||||
online_status = status.get();
|
||||
}
|
||||
if (auto priority = json_tree.get_optional<int16>("peer_priority")) {
|
||||
peer_priority = priority.get();
|
||||
}
|
||||
if (auto isprimary = json_tree.get_optional<bool>("peer_primary")) {
|
||||
peer_primary = isprimary.get();
|
||||
}
|
||||
peer_manager.updatePriority(id, peer_priority);
|
||||
|
||||
if (peer_primary && net.is_primary) {
|
||||
peer_manager.handlePrimaryConflict(id);
|
||||
std::shared_ptr<Peer> hasPrimary = peer_manager.getHealthyPrimaryPeerPtr();
|
||||
if (hasPrimary) { // demote self
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY status, demoting self.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
|
||||
net.SetPrimary(false);
|
||||
}
|
||||
}
|
||||
|
||||
switch (curStatus) {
|
||||
case HealthStatus::STARTUP: {
|
||||
pollPeerHealthData(client, id, server, port);
|
||||
if (online_status == "offline") {
|
||||
std::shared_ptr<Peer> peer = peer_manager.getPeerById(id);
|
||||
if (peer) {
|
||||
peer->wasOffline = true;
|
||||
if (peer->sentInitialPeerData) {
|
||||
peer->sentInitialPeerData = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (online_status == "online") {
|
||||
peer_manager.updateHealth(id, HealthStatus::OK);
|
||||
std::shared_ptr<Peer> peer = peer_manager.getPeerById(id);
|
||||
if (net.is_primary) {
|
||||
if (peer) {
|
||||
if (peer->wasOffline && !peer->sentInitialPeerData) {
|
||||
world.GetGroupManager()->SendPeerGroupData(id);
|
||||
}
|
||||
peer->sentInitialPeerData = true;
|
||||
}
|
||||
}
|
||||
else if (peer) { // set as if we already sent the data since if we take over we don't want the peer trying to resubmit all groups
|
||||
peer->wasOffline = false;
|
||||
peer->sentInitialPeerData = true;
|
||||
}
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS OK/UP state.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
|
||||
}
|
||||
if (!net.is_primary && !peer_manager.hasPrimary() && peer_priority < net.GetPeerPriority()) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
|
||||
peer_manager.setPrimary(id);
|
||||
net.SetPrimary(false);
|
||||
}
|
||||
else if (!peer_manager.hasPrimary() && !net.is_primary && net.GetPeerPriority() <= peer_priority) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: I AM PRIMARY!", __FUNCTION__);
|
||||
net.SetPrimary();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HealthStatus::OK: {
|
||||
pollPeerHealthData(client, id, server, port);
|
||||
if (!net.is_primary && !peer_manager.hasPrimary() && peer_priority < net.GetPeerPriority()) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
|
||||
peer_manager.setPrimary(id);
|
||||
net.SetPrimary(false);
|
||||
}
|
||||
else if (!peer_manager.hasPrimary() && !net.is_primary && net.GetPeerPriority() <= peer_priority) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: I AM PRIMARY!!", __FUNCTION__);
|
||||
net.SetPrimary();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HealthStatus::WARN:
|
||||
case HealthStatus::ERROR:
|
||||
case HealthStatus::SHUTDOWN: {
|
||||
peer_manager.updateHealth(id, HealthStatus::STARTUP);
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS ENTERED STARTUP state.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
|
||||
|
||||
if (net.is_primary) {
|
||||
std::shared_ptr<Peer> peer = peer_manager.getPeerById(id);
|
||||
if (peer && peer->sentInitialPeerData == true) {
|
||||
peer->sentInitialPeerData = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
HealthStatus curStatus = peer_manager.getPeerStatus(server, web_worldport);
|
||||
switch (curStatus) {
|
||||
case HealthStatus::WARN: {
|
||||
peer_manager.updateHealth(id, HealthStatus::ERROR);
|
||||
break;
|
||||
}
|
||||
case HealthStatus::ERROR: {
|
||||
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Peer %s at %s:%s - HAS ERROR->SHUTDOWN state.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
|
||||
peer_manager.updateHealth(id, HealthStatus::SHUTDOWN);
|
||||
if (peer_manager.getHealthyPeer() == std::nullopt) {
|
||||
if (!net.is_primary && world.world_loaded) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: TAKING OVER AS PRIMARY, NO PEERS AVAILABLE TO CHECK", __FUNCTION__);
|
||||
net.SetPrimary();
|
||||
}
|
||||
}
|
||||
else if (!peer_manager.hasPrimary()) {
|
||||
std::string newPrimary = peer_manager.getPriorityPeer();
|
||||
if (newPrimary.size() > 0) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: NEW PRIMARY %s", __FUNCTION__, newPrimary);
|
||||
peer_manager.setPrimary(newPrimary);
|
||||
net.SetPrimary(false);
|
||||
}
|
||||
else {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: NEW PRIMARY CANNOT BE ESTABLISHED!", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
peer_manager.updateHealth(id, HealthStatus::WARN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: ERROR POLLING %s:%s reason: %s", __FUNCTION__, server.c_str(), port.c_str(), e.what() ? e.what() : "??");
|
||||
}
|
||||
interval = 0;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPSClientPool::pollPeerHealthData(auto client, const std::string& id, const std::string& server, const std::string& port) {
|
||||
if (client == nullptr) {
|
||||
|
||||
}
|
||||
auto responseZones = client->sendRequest(server, port, "/zones"); // Assumes HTTPSClient has a get method
|
||||
// Load the JSON data into the property tree
|
||||
std::istringstream json_stream(responseZones);
|
||||
if (json_stream.str().empty()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Empty for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else if (json_stream.fail()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Failed State for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else if (json_stream.bad()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Bad for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else if (json_stream.eof()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream EOF for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json(json_stream, pt);
|
||||
peer_manager.updateZoneTree(id, pt);
|
||||
LogWrite(PEERING__DEBUG, 5, "Peering", "%s: Polling for %s:%s/zones complete, zone tree updated.", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
auto responseClients = client->sendRequest(server, port, "/clients"); // Assumes HTTPSClient has a get method
|
||||
|
||||
// Load the JSON data into the property tree
|
||||
std::istringstream json_stream2(responseClients);
|
||||
if (json_stream2.str().empty()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Empty for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else if (json_stream2.fail()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Failed State for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else if (json_stream2.bad()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Bad for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else if (json_stream2.eof()) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream EOF for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
|
||||
}
|
||||
else {
|
||||
boost::property_tree::ptree pt2;
|
||||
boost::property_tree::read_json(json_stream2, pt2);
|
||||
peer_manager.updateClientTree(id, pt2);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPSClientPool::startPolling() {
|
||||
running.store(true);
|
||||
|
||||
for (const auto& clientPair : clients) {
|
||||
auto server = clientPair.first.first;
|
||||
auto port = clientPair.first.second;
|
||||
|
||||
std::async(std::launch::async, &HTTPSClientPool::pollPeerHealth, this, server, port);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPSClientPool::stopPolling() {
|
||||
running.store(false);
|
||||
}
|
||||
|
||||
void HTTPSClientPool::sendPostRequestToPeerAsync(const std::string& peerId, const std::string& server, const std::string& port, const std::string& target, const std::string& payload) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
taskQueue.emplace([this, peerId, server, port, target, payload]() {
|
||||
std::shared_ptr<HTTPSClient> client = getClient(peerId);
|
||||
if (client) {
|
||||
std::string response = client->sendPostRequest(server, port, target, payload);
|
||||
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
try {
|
||||
std::istringstream responseStream(response);
|
||||
boost::property_tree::read_json(responseStream, pt);
|
||||
}
|
||||
catch (const boost::property_tree::json_parser_error& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Parsing error for %s (%s:%s): %s.", __FUNCTION__, peerId.c_str(), server.c_str(), port.c_str(), e.what() ? e.what() : "??");
|
||||
}
|
||||
if (endpointHandlers.find(target) != endpointHandlers.end()) {
|
||||
endpointHandlers[target](pt); // Call the corresponding handler
|
||||
}
|
||||
else {
|
||||
//std::cout << "No handler for endpoint: " << endpoint << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client not found for %s (%s:%s).", __FUNCTION__, peerId.c_str(), server.c_str(), port.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
condition.notify_one();
|
||||
}
|
||||
|
||||
void HTTPSClientPool::workerFunction() {
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
condition.wait(lock, [this] { return stop || !taskQueue.empty(); });
|
||||
if (stop && taskQueue.empty()) {
|
||||
return;
|
||||
}
|
||||
task = std::move(taskQueue.front());
|
||||
taskQueue.pop();
|
||||
}
|
||||
task();
|
||||
}
|
||||
}
|
92
source/WorldServer/Web/HTTPSClientPool.h
Normal file
92
source/WorldServer/Web/HTTPSClientPool.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
|
||||
EQ2Emu 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EQ2Emu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HTTPSCLIENTPOOL_H
|
||||
#define HTTPSCLIENTPOOL_H
|
||||
|
||||
#include "HTTPSClient.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <boost/property_tree/ptree.hpp> // Include Boost property tree
|
||||
|
||||
struct pair_hash {
|
||||
template <class T1, class T2>
|
||||
std::size_t operator()(const std::pair<T1, T2>& pair) const {
|
||||
return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
|
||||
}
|
||||
};
|
||||
|
||||
class HTTPSClientPool {
|
||||
public:
|
||||
HTTPSClientPool();
|
||||
~HTTPSClientPool();
|
||||
|
||||
// init cert and key file
|
||||
void init(const std::string& cert, const std::string& key);
|
||||
|
||||
// Pre-authenticate and add a client to the pool
|
||||
void addPeerClient(const std::string& peerId, const std::string& server, const std::string& port, const std::string& authEndpoint);
|
||||
|
||||
std::shared_ptr<HTTPSClient> getOrCreateClient(const std::string& id, const std::string& server, const std::string& port);
|
||||
|
||||
// Send a request to a peer by ID and parse response as a ptree
|
||||
boost::property_tree::ptree sendRequestToPeer(const std::string& peerId, const std::string& target);
|
||||
boost::property_tree::ptree sendPostRequestToPeer(const std::string& peerId, const std::string& target, const std::string& jsonPayload);
|
||||
void pollPeerHealthData(auto client, const std::string& id, const std::string& server, const std::string& port);
|
||||
|
||||
void startPolling(); // Starts asynchronous polling of peers
|
||||
void stopPolling(); // Stops the polling process
|
||||
|
||||
// Sends a POST request asynchronously by adding it to the task queue
|
||||
void sendPostRequestToPeerAsync(const std::string& peerId, const std::string& server, const std::string& port, const std::string& target, const std::string& payload);
|
||||
|
||||
// Worker thread function
|
||||
void workerFunction();
|
||||
|
||||
bool isPolling() { return running; }
|
||||
private:
|
||||
std::shared_ptr<HTTPSClient> getClient(const std::string& peerId);
|
||||
|
||||
std::unordered_map<std::pair<std::string, std::string>, std::shared_ptr<HTTPSClient>, pair_hash> clients;
|
||||
std::unordered_map<std::string, std::shared_ptr<HTTPSClient>> clientsById; // New map for ID-based lookup
|
||||
std::string certFile;
|
||||
std::string keyFile;
|
||||
int pollingInterval; // Polling interval in milliseconds
|
||||
|
||||
std::queue<std::function<void()>> taskQueue; // Queue of tasks to execute
|
||||
std::mutex queueMutex; // Mutex to protect access to the task queue
|
||||
std::condition_variable condition; // Condition variable to signal worker threads
|
||||
std::vector<std::thread> workers; // Worker threads
|
||||
bool stop = false; // Flag to stop workers
|
||||
|
||||
std::atomic<bool> running; // Flag to control polling loop
|
||||
void pollPeerHealth(const std::string& server, const std::string& port); // Polls individual peer
|
||||
};
|
||||
|
||||
#endif // HTTPSCLIENTPOOL_H
|
772
source/WorldServer/Web/PeerManager.cpp
Normal file
772
source/WorldServer/Web/PeerManager.cpp
Normal file
@ -0,0 +1,772 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
|
||||
EQ2Emu 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EQ2Emu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PeerManager.h"
|
||||
#include "../../common/Log.h"
|
||||
#include "../net.h"
|
||||
#include "../PlayerGroups.h"
|
||||
#include "HTTPSClientPool.h"
|
||||
|
||||
extern NetConnection net;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
|
||||
// HealthCheck method definitions
|
||||
void HealthCheck::updateStatus(HealthStatus newStatus) {
|
||||
status = newStatus;
|
||||
lastReceived = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
std::chrono::duration<double> HealthCheck::timeSinceLastCheck() const {
|
||||
return std::chrono::system_clock::now() - lastReceived;
|
||||
}
|
||||
|
||||
ZoneChangeDetails::ZoneChangeDetails(std::string peer_id, std::string peer_world_address, std::string peer_internal_world_address, int16 peer_world_port,
|
||||
std::string peer_web_address, int16 peer_web_port, std::string zone_file_name, std::string zone_name, int32 zone_id,
|
||||
int32 instance_id, float safe_x, float safe_y, float safe_z, float safe_heading, bool lock_state, sint16 min_status,
|
||||
int16 min_level, int16 max_level, int16 min_version, int32 default_lockout_time, int32 default_reenter_time, int8 instance_type, int32 num_players)
|
||||
: peerId(std::move(peer_id)), peerWorldAddress(peer_world_address), peerInternalWorldAddress(peer_internal_world_address), peerWorldPort(peer_world_port),
|
||||
peerWebAddress(peer_web_address), peerWebPort(peer_web_port), zoneFileName(zone_file_name), zoneName(zone_name), zoneId(zone_id), instanceId(instance_id),
|
||||
safeX(safe_x), safeY(safe_y), safeZ(safe_z), safeHeading(safe_heading), lockState(lock_state), minStatus(min_status), minLevel(min_level),
|
||||
maxLevel(max_level), minVersion(min_version), defaultLockoutTime(default_lockout_time), defaultReenterTime(default_reenter_time), instanceType(instance_type), numPlayers(num_players) {
|
||||
zonePtr = nullptr;
|
||||
}
|
||||
|
||||
ZoneChangeDetails::ZoneChangeDetails(ZoneChangeDetails* copy_details) : peerId(copy_details->peerId), peerWorldAddress(copy_details->peerWorldAddress), peerWorldPort(copy_details->peerWorldPort),
|
||||
peerWebAddress(copy_details->peerWebAddress), peerWebPort(copy_details->peerWebPort), zoneFileName(copy_details->zoneFileName), zoneName(copy_details->zoneName),
|
||||
zoneId(copy_details->zoneId), instanceId(copy_details->instanceId), safeX(copy_details->safeX), safeY(copy_details->safeY), safeZ(copy_details->safeZ),
|
||||
safeHeading(copy_details->safeHeading), lockState(copy_details->lockState), minStatus(copy_details->minStatus), minLevel(copy_details->minLevel),
|
||||
maxLevel(copy_details->maxLevel), minVersion(copy_details->minVersion), defaultLockoutTime(copy_details->defaultLockoutTime),
|
||||
defaultReenterTime(copy_details->defaultReenterTime), instanceType(copy_details->instanceType), numPlayers(copy_details->numPlayers),
|
||||
peerAuthorized(copy_details->peerAuthorized), zoneKey(copy_details->zoneKey), authDispatchedTime(copy_details->authDispatchedTime),
|
||||
zoningPastAuth(copy_details->zoningPastAuth), zonePtr(copy_details->zonePtr) {
|
||||
|
||||
}
|
||||
|
||||
// PeerManager method definitions
|
||||
void PeerManager::addPeer(std::string id, PeeringStatus status, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port) {
|
||||
std::shared_ptr<Peer> peer = std::make_shared<Peer>(id, PeeringStatus::SECONDARY, client_address, client_internal_address, client_port, web_address, web_port);
|
||||
peers.emplace(id, peer);
|
||||
}
|
||||
|
||||
void PeerManager::updateHealth(const std::string& id, HealthStatus newStatus) {
|
||||
if (peers.find(id) != peers.end()) {
|
||||
peers[id]->healthCheck.updateStatus(newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::updatePriority(const std::string& id, int16 priority) {
|
||||
if (peers.find(id) != peers.end()) {
|
||||
peers[id]->peerPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::updateZoneTree(const std::string& id, const boost::property_tree::ptree& newTree) {
|
||||
auto it = peers.find(id);
|
||||
if (it != peers.end()) {
|
||||
std::lock_guard<std::mutex> lock(it->second->dataMutex);
|
||||
*(it->second->zone_tree) = newTree;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::updateClientTree(const std::string& id, const boost::property_tree::ptree& newTree) {
|
||||
auto it = peers.find(id);
|
||||
if (it != peers.end()) {
|
||||
std::lock_guard<std::mutex> lock(it->second->dataMutex);
|
||||
*(it->second->client_tree) = newTree;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::setZonePeerData(ZoneChangeDetails* opt_details, std::string peerId, std::string peerWorldAddress, std::string peerInternalWorldAddress, int16 peerWorldPort,
|
||||
std::string peerWebAddress, int16 peerWebPort, std::string zoneFileName,
|
||||
std::string zoneName, int32 zoneId, int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
|
||||
int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers) {
|
||||
if (opt_details) {
|
||||
opt_details->peerId = peerId;
|
||||
opt_details->peerWorldAddress = peerWorldAddress;
|
||||
opt_details->peerInternalWorldAddress = peerInternalWorldAddress;
|
||||
opt_details->peerWorldPort = peerWorldPort;
|
||||
opt_details->peerWebAddress = peerWebAddress;
|
||||
opt_details->peerWebPort = peerWebPort;
|
||||
opt_details->zoneFileName = zoneFileName;
|
||||
opt_details->zoneName = zoneName;
|
||||
opt_details->zoneId = zoneId;
|
||||
opt_details->instanceId = instanceId;
|
||||
opt_details->safeX = safeX;
|
||||
opt_details->safeY = safeY;
|
||||
opt_details->safeZ = safeZ;
|
||||
opt_details->safeHeading = safeHeading;
|
||||
opt_details->lockState = lockState;
|
||||
opt_details->minStatus = minStatus;
|
||||
opt_details->minLevel = minLevel;
|
||||
opt_details->maxLevel = maxLevel;
|
||||
opt_details->minVersion = minVersion;
|
||||
opt_details->minVersion = minVersion;
|
||||
opt_details->defaultLockoutTime = defaultLockoutTime;
|
||||
opt_details->defaultReenterTime = defaultReenterTime;
|
||||
opt_details->instanceType = instanceType;
|
||||
opt_details->numPlayers = numPlayers;
|
||||
opt_details->zonePtr = nullptr;
|
||||
opt_details->peerAuthorized = false;
|
||||
opt_details->zoneKey = 0;
|
||||
opt_details->authDispatchedTime = 0;
|
||||
opt_details->zoningPastAuth = false;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::setZonePeerDataSelf(ZoneChangeDetails* opt_details, std::string zoneFileName, std::string zoneName, int32 zoneId,
|
||||
int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
|
||||
int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType,
|
||||
int32 numPlayers, void* zonePtr) {
|
||||
if (opt_details) {
|
||||
opt_details->peerId = "self";
|
||||
opt_details->peerWorldAddress = net.GetWorldAddress();
|
||||
opt_details->peerInternalWorldAddress = net.GetInternalWorldAddress();
|
||||
opt_details->peerWorldPort = net.GetWorldPort();
|
||||
opt_details->peerWebAddress = net.GetWebWorldAddress();
|
||||
opt_details->peerWebPort = net.GetWebWorldPort();
|
||||
opt_details->zoneFileName = zoneFileName;
|
||||
opt_details->zoneName = zoneName;
|
||||
opt_details->zoneId = zoneId;
|
||||
opt_details->instanceId = instanceId;
|
||||
opt_details->safeX = safeX;
|
||||
opt_details->safeY = safeY;
|
||||
opt_details->safeZ = safeZ;
|
||||
opt_details->safeHeading = safeHeading;
|
||||
opt_details->lockState = lockState;
|
||||
opt_details->minStatus = minStatus;
|
||||
opt_details->minLevel = minLevel;
|
||||
opt_details->maxLevel = maxLevel;
|
||||
opt_details->minVersion = minVersion;
|
||||
opt_details->defaultLockoutTime = defaultLockoutTime;
|
||||
opt_details->defaultReenterTime = defaultReenterTime;
|
||||
opt_details->instanceType = instanceType;
|
||||
opt_details->numPlayers = numPlayers;
|
||||
opt_details->zonePtr = zonePtr;
|
||||
opt_details->peerAuthorized = true;
|
||||
opt_details->zoneKey = 0;
|
||||
opt_details->authDispatchedTime = 0;
|
||||
opt_details->zoningPastAuth = true;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& zone : peer->zone_tree->get_child("Zones")) {
|
||||
// Access each field within the current zone
|
||||
std::string zone_name = zone.second.get<std::string>("zone_name");
|
||||
std::string zone_file_name = zone.second.get<std::string>("zone_file_name");
|
||||
int32 zone_id = zone.second.get<int32>("zone_id");
|
||||
int32 instance_id = zone.second.get<int32>("instance_id");
|
||||
bool shutting_down = zone.second.get<std::string>("shutting_down") == "true";
|
||||
bool instance_zone = zone.second.get<std::string>("instance_zone") == "true";
|
||||
int32 num_players = zone.second.get<int32>("num_players");
|
||||
bool city_zone = zone.second.get<std::string>("city_zone") == "true";
|
||||
float safe_x = zone.second.get<float>("safe_x");
|
||||
float safe_y = zone.second.get<float>("safe_y");
|
||||
float safe_z = zone.second.get<float>("safe_z");
|
||||
float safe_heading = zone.second.get<float>("safe_heading");
|
||||
bool lock_state = zone.second.get<bool>("lock_state");
|
||||
sint16 min_status = zone.second.get<sint16>("min_status");
|
||||
int16 min_level = zone.second.get<int16>("min_level");
|
||||
int16 max_level = zone.second.get<int16>("max_level");
|
||||
int16 min_version = zone.second.get<int16>("min_version");
|
||||
int32 default_lockout_time = zone.second.get<int32>("default_lockout_time");
|
||||
int32 default_reenter_time = zone.second.get<int32>("default_reenter_time");
|
||||
int8 instance_type = zone.second.get<int8>("instance_type");
|
||||
bool always_loaded = zone.second.get<bool>("always_loaded");
|
||||
|
||||
if (only_always_loaded && !always_loaded)
|
||||
continue;
|
||||
|
||||
if (!shutting_down) {
|
||||
bool match = false;
|
||||
if (instance_zone && inc_instance_id > 0 && instance_id == inc_instance_id) {
|
||||
match = true;
|
||||
}
|
||||
else if (!instance_zone && inc_instance_id == 0 && inc_zone_id > 0 && zone_id == inc_zone_id) {
|
||||
match = true;
|
||||
}
|
||||
else if (!instance_zone && inc_zone_name.length() > 0 && strncasecmp(zone_name.c_str(), inc_zone_name.c_str(), inc_zone_name.length()) == 0) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
setZonePeerData(opt_details, peerId, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, zone_file_name, zone_name, zone_id, instance_id,
|
||||
safe_x, safe_y, safe_z, safe_heading, lock_state, min_status, min_level, max_level, min_version, default_lockout_time, default_reenter_time, instance_type, num_players);
|
||||
return peerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error Parsing Zones for %s:%u", __FUNCTION__, peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void PeerManager::handlePrimaryConflict(const std::string& reconnectingPeerId) {
|
||||
// Compare IDs or priorities to decide on the primary role
|
||||
auto currentPrimary = getCurrentPrimary();
|
||||
auto reconnectingPeer = getPeerById(reconnectingPeerId);
|
||||
|
||||
if (currentPrimary && (currentPrimary->peerPriority > reconnectingPeer->peerPriority || currentPrimary->healthCheck.status != HealthStatus::OK)) {
|
||||
// Demote the current primary
|
||||
if (reconnectingPeer && currentPrimary->healthCheck.status == HealthStatus::OK) {
|
||||
setPrimary(reconnectingPeerId);
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s forced to primary", __FUNCTION__, reconnectingPeer->id);
|
||||
if (currentPrimary) {
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Demoted to secondary", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Demote the reconnecting peer
|
||||
if (currentPrimary && currentPrimary->healthCheck.status == HealthStatus::OK) {
|
||||
setPrimary(currentPrimary->id);
|
||||
LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s forced to primary", __FUNCTION__, currentPrimary->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::setPrimary(const std::string& id) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
peer->peeringStatus = (peerId == id) ? PeeringStatus::PRIMARY : PeeringStatus::SECONDARY;
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerManager::hasPrimary() {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->peeringStatus == PeeringStatus::PRIMARY)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerManager::hasPriorityPeer(int16 priority) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->peerPriority == priority && peer->healthCheck.status == HealthStatus::OK)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PeerManager::getPriorityPeer() {
|
||||
int16 peerPriority = 65535;
|
||||
std::string id = "";
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->healthCheck.status > HealthStatus::ERROR && peer->healthCheck.status <= HealthStatus::OK &&
|
||||
peer->peerPriority > 0 && peer->peerPriority < peerPriority) {
|
||||
peerPriority = peer->peerPriority;
|
||||
id = peer->id;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void PeerManager::updatePeer(const std::string& web_address, int16 web_port, const std::string& client_address, const std::string& client_internal_address, int16 client_port, bool is_primary) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->webAddr == web_address && peer->webPort == web_port) {
|
||||
peer->worldAddr = client_address;
|
||||
peer->worldPort = client_port;
|
||||
peer->internalWorldAddr = client_internal_address;
|
||||
if (is_primary) {
|
||||
peer->peeringStatus = PeeringStatus::PRIMARY;
|
||||
}
|
||||
else {
|
||||
peer->peeringStatus = PeeringStatus::SECONDARY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string PeerManager::isPeer(const std::string& web_address, int16 web_port) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->webAddr == web_address && peer->webPort == web_port) {
|
||||
return peerId;
|
||||
}
|
||||
}
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
|
||||
HealthStatus PeerManager::getPeerStatus(const std::string& web_address, int16 web_port) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if (peer->webAddr == web_address && peer->webPort == web_port) {
|
||||
return peer->healthCheck.status;
|
||||
}
|
||||
}
|
||||
return HealthStatus::UNKNOWN;
|
||||
}
|
||||
|
||||
bool PeerManager::hasPeers() {
|
||||
return (peers.size() > 0);
|
||||
}
|
||||
|
||||
std::string PeerManager::assignUniqueNameForSecondary(const std::string& baseName, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port) {
|
||||
int suffix = 1;
|
||||
std::string uniqueName = baseName + std::to_string(suffix);
|
||||
|
||||
while (peers.find(uniqueName) != peers.end()) {
|
||||
uniqueName = baseName + std::to_string(suffix);
|
||||
++suffix;
|
||||
}
|
||||
|
||||
addPeer(uniqueName, PeeringStatus::SECONDARY, client_address, client_internal_address, client_port, web_address, web_port);
|
||||
|
||||
updateHealth(uniqueName, HealthStatus::STARTUP);
|
||||
|
||||
return uniqueName;
|
||||
}
|
||||
|
||||
std::optional<std::string> PeerManager::getHealthyPeer() const {
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status == HealthStatus::OK) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer> PeerManager::getHealthyPeerPtr() const {
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status == HealthStatus::OK) {
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer> PeerManager::getHealthyPrimaryPeerPtr() const {
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->peeringStatus == PeeringStatus::PRIMARY && peer->healthCheck.status == HealthStatus::OK) {
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer> PeerManager::getHealthyPeerWithLeastClients() const {
|
||||
std::vector<std::shared_ptr<Peer>> healthyPeers;
|
||||
|
||||
// Seed random generator
|
||||
std::srand(static_cast<unsigned>(std::time(nullptr)));
|
||||
|
||||
// Step 1: Collect healthy peers
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
|
||||
// if setup to distribute to peers only, skip primary
|
||||
if (peer->peerPriority == 0 && peer->peeringStatus == PeeringStatus::PRIMARY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->healthCheck.status == HealthStatus::OK) {
|
||||
healthyPeers.push_back(peer);
|
||||
}
|
||||
}
|
||||
|
||||
if (healthyPeers.empty()) {
|
||||
return nullptr; // No healthy peers found
|
||||
}
|
||||
|
||||
std::string treeList("Zones");
|
||||
|
||||
// Step 2: Determine minimum number of "Clients" for healthy peers
|
||||
size_t minClientCount = std::numeric_limits<size_t>::max();
|
||||
for (const auto& peer : healthyPeers) {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex); // Lock for thread-safe access
|
||||
std::shared_ptr<boost::property_tree::ptree> tree = peer->zone_tree;
|
||||
if (auto clientOpt = tree->get_child_optional(treeList.c_str())) {
|
||||
size_t clientCount = clientOpt.get().size();
|
||||
if (clientCount < minClientCount) {
|
||||
minClientCount = clientCount;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Consider peers without "Clients" node as having 0 clients
|
||||
minClientCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Collect all healthy peers with minClientCount
|
||||
std::vector<std::shared_ptr<Peer>> minClientPeers;
|
||||
for (const auto& peer : healthyPeers) {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
std::shared_ptr<boost::property_tree::ptree> tree = peer->zone_tree;
|
||||
size_t clientCount = 0;
|
||||
if (auto clientOpt = tree->get_child_optional(treeList.c_str())) {
|
||||
clientCount = clientOpt.get().size();
|
||||
}
|
||||
if (clientCount == minClientCount) {
|
||||
minClientPeers.push_back(peer);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Select a random peer from the minClientPeers
|
||||
if (!minClientPeers.empty()) {
|
||||
size_t randomIndex = std::rand() % minClientPeers.size();
|
||||
return minClientPeers[randomIndex];
|
||||
}
|
||||
|
||||
return nullptr; // Fallback if no peers match the criteria
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer> PeerManager::getPeerById(const std::string& id) const {
|
||||
auto it = peers.find(id);
|
||||
if (it != peers.end()) {
|
||||
return it->second; // Return the shared_ptr<Peer> if found
|
||||
}
|
||||
return nullptr; // Return nullptr if the peerId doesn't exist
|
||||
}
|
||||
// Function to get a unique integer for a peer
|
||||
int32 PeerManager::getUniqueGroupId() {
|
||||
std::lock_guard<std::mutex> lock(idMutex);
|
||||
uniqueGroupID++;
|
||||
if (uniqueGroupID == 0)
|
||||
uniqueGroupID++;
|
||||
|
||||
return uniqueGroupID;
|
||||
}
|
||||
|
||||
bool PeerManager::sendPrimaryNewGroupRequest(std::string leader, std::string member, int32 entity_id, GroupOptions* options) {
|
||||
std::shared_ptr<Peer> primary = getHealthyPrimaryPeerPtr();
|
||||
if (primary) {
|
||||
boost::property_tree::ptree root;
|
||||
|
||||
root.put("peer_web_address", std::string(net.GetWebWorldAddress()));
|
||||
root.put("peer_web_port", std::to_string(net.GetWebWorldPort()));
|
||||
root.put("group_id", 0);
|
||||
root.put("leader_name", leader);
|
||||
root.put("member_name", member);
|
||||
root.put("member_entity_id", entity_id);
|
||||
populateGroupOptions(root, options);
|
||||
root.put("is_update", false);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Leader %s, Member %s(%u)", __FUNCTION__, leader.c_str(), member.c_str(), entity_id);
|
||||
peer_https_pool.sendPostRequestToPeerAsync(primary->id, primary->webAddr, std::to_string(primary->webPort), "/newgroup", jsonPayload);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function to populate the ptree with GroupOptions
|
||||
void PeerManager::populateGroupOptions(boost::property_tree::ptree& root, GroupOptions* options) {
|
||||
if (!options) return; // Handle null pointer gracefully
|
||||
root.put("loot_method", options->loot_method);
|
||||
root.put("loot_items_rarity", options->loot_items_rarity);
|
||||
root.put("auto_split", options->auto_split);
|
||||
root.put("default_yell", options->default_yell);
|
||||
root.put("group_lock_method", options->group_lock_method);
|
||||
root.put("group_autolock", options->group_autolock);
|
||||
root.put("solo_autolock", options->solo_autolock);
|
||||
root.put("auto_loot_method", options->auto_loot_method);
|
||||
root.put("last_looted_index", options->last_looted_index);
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersNewGroupRequest(std::string peer_creation_address, int16 peer_creation_port,
|
||||
int32 group_id, std::string leader, std::string member, GroupOptions* options,
|
||||
std::string peerId, std::vector<int32>* raidGroups, bool is_update) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("group_id", group_id);
|
||||
root.put("leader_name", leader);
|
||||
root.put("member_name", member);
|
||||
populateGroupOptions(root, options);
|
||||
root.put("peer_web_address", peer_creation_address);
|
||||
root.put("peer_web_port", peer_creation_port);
|
||||
if (raidGroups) {
|
||||
std::vector<int32>::iterator group_itr;
|
||||
int8 group_count = 0;
|
||||
for (group_itr = raidGroups->begin(); group_itr != raidGroups->end(); group_itr++) {
|
||||
std::string fieldName("group_id_");
|
||||
fieldName.append(std::to_string(group_count));
|
||||
root.put(fieldName, (*group_itr));
|
||||
group_count++;
|
||||
}
|
||||
}
|
||||
root.put("is_update", is_update);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: ToPeer: %s (Optional), NewGroup %u, IsUpdate: %u", __FUNCTION__, peerId.c_str(), group_id, is_update);
|
||||
|
||||
if (peerId.size() > 0) {
|
||||
std::shared_ptr<Peer> peer = getPeerById(peerId);
|
||||
|
||||
if (peer->healthCheck.status == HealthStatus::OK) {
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/newgroup", jsonPayload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
if (peer->webAddr == peer_creation_address && peer->webPort == peer_creation_port) // skip peer it was created on
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/newgroup", jsonPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersGroupMember(int32 group_id, GroupMemberInfo* info, bool is_update, std::string peerId) {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
boost::property_tree::ptree root;
|
||||
root.put("group_id", group_id);
|
||||
root.put("member_name", info->name);
|
||||
root.put("is_client", info->is_client);
|
||||
root.put("zone", info->zone);
|
||||
root.put("current_hp", info->hp_current);
|
||||
root.put("max_hp", info->hp_max);
|
||||
root.put("current_power", info->power_current);
|
||||
root.put("max_power", info->power_max);
|
||||
root.put("level_current", info->level_current);
|
||||
root.put("level_max", info->level_max);
|
||||
root.put("race_id", info->race_id);
|
||||
root.put("class_id", info->class_id);
|
||||
root.put("is_leader", info->leader);
|
||||
root.put("mentor_target_char_id", info->mentor_target_char_id);
|
||||
root.put("client_peer_address", info->client_peer_address);
|
||||
root.put("client_peer_port", info->client_peer_port);
|
||||
root.put("is_raid_looter", info->is_raid_looter);
|
||||
root.put("is_update", is_update);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Send Member %s ToPeer: %s (Optional), NewGroup %u, IsUpdate: %u", __FUNCTION__, info->name.c_str(), peerId.c_str(), group_id, is_update);
|
||||
|
||||
|
||||
if (peerId.size() > 0) {
|
||||
std::shared_ptr<Peer> peer = getPeerById(peerId);
|
||||
|
||||
if (peer->healthCheck.status == HealthStatus::OK) {
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addgroupmember", jsonPayload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addgroupmember", jsonPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersRemoveGroupMember(int32 group_id, std::string name, int32 char_id, bool is_client) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("group_id", group_id);
|
||||
root.put("member_name", name);
|
||||
root.put("is_client", is_client);
|
||||
root.put("character_id", char_id);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Member %s ToPeer: %s (Optional), Group %u", __FUNCTION__, name.c_str(), group_id);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removegroupmember", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersDisbandGroup(int32 group_id) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("group_id", group_id);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Disband Group %u", __FUNCTION__, group_id);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/disbandgroup", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerManager::sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name) {
|
||||
std::shared_ptr<Peer> primary = getHealthyPrimaryPeerPtr();
|
||||
if (primary) {
|
||||
boost::property_tree::ptree root;
|
||||
|
||||
root.put("peer_web_address", std::string(net.GetWebWorldAddress()));
|
||||
root.put("peer_web_port", std::to_string(net.GetWebWorldPort()));
|
||||
root.put("guild_name", guild_name);
|
||||
root.put("leader_name", leader_name);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Create Guild Request %s, Leader %s", __FUNCTION__, guild_name.c_str(), leader_name.c_str());
|
||||
peer_https_pool.sendPostRequestToPeerAsync(primary->id, primary->webAddr, std::to_string(primary->webPort), "/createguild", jsonPayload);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PeerManager::sendPeersAddGuildMember(int32 character_id, int32 guild_id, std::string invited_by, int32 join_timestamp, int8 rank) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("guild_id", guild_id);
|
||||
root.put("character_id", character_id);
|
||||
root.put("invited_by", invited_by);
|
||||
root.put("join_timestamp", join_timestamp);
|
||||
root.put("rank", rank);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Add Guild Member, Guild: %u, CharID: %u, InvitedBy: %s", __FUNCTION__, guild_id, character_id, invited_by.c_str());
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addguildmember", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersRemoveGuildMember(int32 character_id, int32 guild_id, std::string removed_by) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("guild_id", guild_id);
|
||||
root.put("character_id", character_id);
|
||||
root.put("removed_by", removed_by);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Guild Member, Guild: %u, CharID: %u, RemovedBy: %s", __FUNCTION__, guild_id, character_id, removed_by.c_str());
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removeguildmember", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersCreateGuild(int32 guild_id) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("guild_id", guild_id);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peers Guild Create, Guild: %u", __FUNCTION__, guild_id);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
// primary creates the guild, skip it
|
||||
if (peer->healthCheck.status != HealthStatus::OK || peer->peeringStatus == PeeringStatus::PRIMARY)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/createguild", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersGuildPermission(int32 guild_id, int8 rank, int8 permission, int8 value_) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("guild_id", guild_id);
|
||||
root.put("rank", rank);
|
||||
root.put("permission", permission);
|
||||
root.put("value", value_);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peers Guild Permission, Guild: %u, Rank: %u, Permission: %u, Value: %u", __FUNCTION__, guild_id, rank, permission, value_);
|
||||
for (const auto& [id, peer] : peers) {
|
||||
// primary creates the guild, skip it
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/setguildpermission", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersGuildEventFilter(int32 guild_id, int8 event_id, int8 category, int8 value_) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("guild_id", guild_id);
|
||||
root.put("event_id", event_id);
|
||||
root.put("category", category);
|
||||
root.put("value", value_);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peers Guild Event Filter, Guild: %u, EventID: %u, Category: %u, Value: %u", __FUNCTION__, guild_id, event_id, category, value_);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
// primary creates the guild, skip it
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/setguildeventfilter", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::SetPeerErrorState(std::string address, std::string port) {
|
||||
std::string id = isPeer(address, (int16)std::atol(port.c_str()));
|
||||
if (id.size() > 0)
|
||||
updateHealth(id, HealthStatus::ERROR);
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer> PeerManager::getCurrentPrimary() {
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->peeringStatus == PeeringStatus::PRIMARY) {
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
return nullptr; // Or throw an error if no primary found
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersMessage(const std::string& endpoint, int32 command, int32 sub_command) {
|
||||
|
||||
boost::property_tree::ptree root;
|
||||
|
||||
root.put("reload_command", command);
|
||||
root.put("sub_command", sub_command);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), endpoint.c_str(), jsonPayload);
|
||||
}
|
||||
}
|
227
source/WorldServer/Web/PeerManager.h
Normal file
227
source/WorldServer/Web/PeerManager.h
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
|
||||
EQ2Emu 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
EQ2Emu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PEERMANAGER_H
|
||||
#define PEERMANAGER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include "../../common/types.h"
|
||||
|
||||
class Client;
|
||||
|
||||
enum HealthStatus {
|
||||
UNKNOWN = 0,
|
||||
WARN = 1,
|
||||
ERROR = 2,
|
||||
STARTUP = 3,
|
||||
OK = 4,
|
||||
SHUTDOWN = 5
|
||||
};
|
||||
|
||||
enum class PeeringStatus {
|
||||
PRIMARY,
|
||||
SECONDARY
|
||||
};
|
||||
|
||||
struct HealthCheck {
|
||||
HealthStatus status;
|
||||
std::chrono::system_clock::time_point lastReceived;
|
||||
|
||||
void updateStatus(HealthStatus newStatus);
|
||||
std::chrono::duration<double> timeSinceLastCheck() const;
|
||||
};
|
||||
|
||||
struct GroupOptions;
|
||||
struct GroupMemberInfo;
|
||||
struct WhoAllPeerPlayer;
|
||||
struct GuildMember;
|
||||
|
||||
struct ZoneChangeDetails {
|
||||
std::string peerId;
|
||||
std::string peerWorldAddress;
|
||||
std::string peerInternalWorldAddress;
|
||||
int16 peerWorldPort;
|
||||
std::string peerWebAddress;
|
||||
int16 peerWebPort;
|
||||
std::string zoneFileName;
|
||||
std::string zoneName;
|
||||
int32 zoneId;
|
||||
int32 instanceId;
|
||||
float safeX;
|
||||
float safeY;
|
||||
float safeZ;
|
||||
float safeHeading;
|
||||
bool lockState;
|
||||
sint16 minStatus;
|
||||
int16 minLevel;
|
||||
int16 maxLevel;
|
||||
int16 minVersion;
|
||||
|
||||
int32 defaultLockoutTime;
|
||||
int32 defaultReenterTime;
|
||||
int8 instanceType;
|
||||
int32 numPlayers;
|
||||
void* zonePtr;
|
||||
bool peerAuthorized;
|
||||
int32 zoneKey;
|
||||
int32 authDispatchedTime;
|
||||
bool zoningPastAuth;
|
||||
ZoneChangeDetails() = default;
|
||||
ZoneChangeDetails(ZoneChangeDetails* copy_details);
|
||||
ZoneChangeDetails(std::string peer_id, std::string peer_world_address, std::string peer_internal_world_address, int16 peer_world_port,
|
||||
std::string peer_web_address, int16 peer_web_port, std::string zone_file_name, std::string zone_name, int32 zone_id,
|
||||
int32 instance_id, float safe_x, float safe_y, float safe_z, float safe_heading, bool lock_state, sint16 min_status,
|
||||
int16 min_level, int16 max_level, int16 min_version, int32 default_lockout_time, int32 default_reenter_time, int8 instance_type, int32 num_players);
|
||||
};
|
||||
|
||||
struct Peer {
|
||||
std::string id;
|
||||
PeeringStatus peeringStatus;
|
||||
HealthCheck healthCheck;
|
||||
std::string worldAddr;
|
||||
std::string internalWorldAddr;
|
||||
int16 worldPort;
|
||||
int16 peerPriority;
|
||||
std::string webAddr;
|
||||
int16 webPort;
|
||||
std::shared_ptr<boost::property_tree::ptree> zone_tree;
|
||||
std::shared_ptr<boost::property_tree::ptree> client_tree;
|
||||
std::atomic<bool> sentInitialPeerData;
|
||||
std::atomic<bool> wasOffline;
|
||||
mutable std::mutex dataMutex; // Mutex to protect access to ptree
|
||||
// Default constructor
|
||||
Peer()
|
||||
: zone_tree(std::make_shared<boost::property_tree::ptree>()),
|
||||
client_tree(std::make_shared<boost::property_tree::ptree>()) {
|
||||
healthCheck.status = HealthStatus::UNKNOWN;
|
||||
peerPriority = 65535;
|
||||
sentInitialPeerData = false;
|
||||
wasOffline = false;
|
||||
}
|
||||
|
||||
Peer(std::string peerId, PeeringStatus status, std::string client_address,
|
||||
std::string client_internal_address, int16 client_port,
|
||||
std::string web_address, int16 web_port)
|
||||
: id(std::move(peerId)), peeringStatus(status), worldAddr(std::move(client_address)),
|
||||
internalWorldAddr(std::move(client_internal_address)), worldPort(client_port),
|
||||
webAddr(std::move(web_address)), webPort(web_port),
|
||||
zone_tree(std::make_shared<boost::property_tree::ptree>()),
|
||||
client_tree(std::make_shared<boost::property_tree::ptree>()) {
|
||||
healthCheck.status = HealthStatus::STARTUP;
|
||||
peerPriority = 65535;
|
||||
sentInitialPeerData = false;
|
||||
wasOffline = false;
|
||||
}
|
||||
|
||||
// Example function to output data as JSON string (for debug or logging)
|
||||
std::string getZoneDataAsJson() const {
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, *zone_tree); // Dereference data
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// Example function to output data as JSON string (for debug or logging)
|
||||
std::string getClientDataAsJson() const {
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, *client_tree); // Dereference data
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class PeerManager {
|
||||
private:
|
||||
std::map<std::string, std::shared_ptr<Peer>> peers;
|
||||
std::atomic<int32> uniqueGroupID{ 1 }; // Shared counter for unique IDs
|
||||
std::mutex idMutex;
|
||||
|
||||
public:
|
||||
void addPeer(std::string id, PeeringStatus status, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port);
|
||||
void updateHealth(const std::string& id, HealthStatus newStatus);
|
||||
void updatePriority(const std::string& id, int16 priority);
|
||||
void updateZoneTree(const std::string& id, const boost::property_tree::ptree& newTree);
|
||||
void updateClientTree(const std::string& id, const boost::property_tree::ptree& newTree);
|
||||
void setZonePeerData(ZoneChangeDetails* opt_details, std::string peerId, std::string peerWorldAddress, std::string peerInternalWorldAddress, int16 peerWorldPort,
|
||||
std::string peerWebAddress, int16 peerWebPort, std::string zoneFileName, std::string zoneName, int32 zoneId,
|
||||
int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
|
||||
int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers);
|
||||
void setZonePeerDataSelf(ZoneChangeDetails* opt_details, std::string zoneFileName, std::string zoneName, int32 zoneId,
|
||||
int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
|
||||
int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers, void* zonePtr = nullptr);
|
||||
bool IsClientConnectedPeer(int32 account_id);
|
||||
std::string GetCharacterPeerId(std::string charName);
|
||||
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);
|
||||
void setZonePeerData(ZoneChangeDetails* opt_details);
|
||||
void setPrimary(const std::string& id);
|
||||
bool hasPrimary();
|
||||
bool hasPriorityPeer(int16 priority);
|
||||
std::string getPriorityPeer();
|
||||
void updatePeer(const std::string& web_address, int16 web_port, const std::string& client_address, const std::string& client_internal_address, int16 client_port, bool is_primary = false);
|
||||
std::string isPeer(const std::string& web_address, int16 web_port);
|
||||
HealthStatus getPeerStatus(const std::string& web_address, int16 web_port);
|
||||
bool hasPeers();
|
||||
std::string assignUniqueNameForSecondary(const std::string& baseName, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port);
|
||||
std::optional<std::string> getHealthyPeer() const;
|
||||
std::shared_ptr<Peer> getHealthyPeerPtr() const;
|
||||
std::shared_ptr<Peer> getHealthyPrimaryPeerPtr() const;
|
||||
std::shared_ptr<Peer> getHealthyPeerWithLeastClients() const;
|
||||
std::shared_ptr<Peer> getPeerById(const std::string& id) const;
|
||||
int32 getUniqueGroupId();
|
||||
bool sendPrimaryNewGroupRequest(std::string leader, std::string member, int32 entity_id, GroupOptions* options);
|
||||
void sendPeersGroupMember(int32 group_id, GroupMemberInfo* info, bool is_update = false, std::string peerId = "");
|
||||
void sendPeersRemoveGroupMember(int32 group_id, std::string name, int32 char_id, bool is_client);
|
||||
void populateGroupOptions(boost::property_tree::ptree& root, GroupOptions* options);
|
||||
void sendPeersNewGroupRequest(std::string peer_creation_address, int16 peer_creation_port,
|
||||
int32 group_id, std::string leader, std::string member, GroupOptions* options,
|
||||
std::string peerId = "", std::vector<int32>* raidGroups = nullptr, bool is_update = false);
|
||||
|
||||
void sendPeersDisbandGroup(int32 group_id);
|
||||
bool sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name);
|
||||
void sendPeersAddGuildMember(int32 character_id, int32 guild_id, std::string invited_by, int32 join_timestamp, int8 rank);
|
||||
void sendPeersRemoveGuildMember(int32 character_id, int32 guild_id, std::string removed_by);
|
||||
void sendPeersCreateGuild(int32 guild_id);
|
||||
void sendPeersGuildPermission(int32 guild_id, int8 rank, int8 permission, int8 value_);
|
||||
void sendPeersGuildEventFilter(int32 guild_id, int8 event_id, int8 category, int8 value_);
|
||||
|
||||
void SetPeerErrorState(std::string address, std::string port);
|
||||
void handlePrimaryConflict(const std::string& reconnectingPeerId);
|
||||
std::shared_ptr<Peer> getCurrentPrimary();
|
||||
|
||||
void sendPeersMessage(const std::string& endpoint, int32 command, int32 sub_command = 0);
|
||||
|
||||
void sendZonePlayerList(std::vector<string>* queries, std::vector<WhoAllPeerPlayer>* peer_list, bool isGM);
|
||||
|
||||
bool GetClientGuildDetails(int32 matchCharID, GuildMember* member_details);
|
||||
|
||||
};
|
||||
|
||||
#endif // PEERMANAGER_H
|
File diff suppressed because it is too large
Load Diff
@ -52,6 +52,9 @@
|
||||
|
||||
#include "Player.h"
|
||||
|
||||
#include "Web/PeerManager.h"
|
||||
#include "Web/HTTPSClientPool.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
@ -88,14 +91,17 @@ WorldDatabase database;
|
||||
GuildList guild_list;
|
||||
Chat chat;
|
||||
Player player;
|
||||
|
||||
|
||||
extern ConfigReader configReader;
|
||||
extern LoginServer loginserver;
|
||||
extern World world;
|
||||
extern ZoneList zone_list;
|
||||
extern RuleManager rule_manager;
|
||||
extern LuaInterface* lua_interface;
|
||||
extern PeerManager peer_manager;
|
||||
extern sint32 numclients;
|
||||
extern PeerManager peer_manager;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
|
||||
World::World() : save_time_timer(300000), time_tick_timer(3000), vitality_timer(3600000), player_stats_timer(60000), server_stats_timer(60000), /*remove_grouped_player(30000),*/ guilds_timer(60000), lotto_players_timer(500), watchdog_timer(10000) {
|
||||
save_time_timer.Start();
|
||||
@ -239,21 +245,68 @@ void World::init(std::string web_ipaddr, int16 web_port, std::string cert_file,
|
||||
//PopulateTOVStatMap();
|
||||
group_buff_updates.Start(rule_manager.GetGlobalRule(R_Client, GroupSpellsTimer)->GetInt32());
|
||||
|
||||
bool web_success = false;
|
||||
if(web_ipaddr.size() > 0 && web_port > 0) {
|
||||
try {
|
||||
world_webserver = new WebServer(web_ipaddr, web_port, cert_file, key_file, key_password, hardcode_user, hardcode_password);
|
||||
|
||||
// status providers
|
||||
world_webserver->register_route("/status", World::Web_worldhandle_status);
|
||||
world_webserver->register_route("/clients", World::Web_worldhandle_clients);
|
||||
world_webserver->register_route("/zones", World::Web_worldhandle_zones);
|
||||
|
||||
// administrative commands
|
||||
world_webserver->register_route("/setadminstatus", World::Web_worldhandle_setadminstatus);
|
||||
world_webserver->register_route("/reloadrules", World::Web_worldhandle_reloadrules);
|
||||
world_webserver->register_route("/reloadcommand", World::Web_worldhandle_reloadcommand);
|
||||
|
||||
// peering capabilities
|
||||
world_webserver->register_route("/addpeer", World::Web_worldhandle_addpeer);
|
||||
world_webserver->register_route("/addcharauth", World::Web_worldhandle_addcharauth);
|
||||
world_webserver->register_route("/startzone", World::Web_worldhandle_startzone);
|
||||
|
||||
world_webserver->register_route("/sendglobalmessage", World::Web_worldhandle_sendglobalmessage);
|
||||
|
||||
world_webserver->register_route("/newgroup", World::Web_worldhandle_newgroup);
|
||||
world_webserver->register_route("/addgroupmember", World::Web_worldhandle_addgroupmember);
|
||||
world_webserver->register_route("/removegroupmember", World::Web_worldhandle_removegroupmember);
|
||||
world_webserver->register_route("/disbandgroup", World::Web_worldhandle_disbandgroup);
|
||||
|
||||
world_webserver->register_route("/createguild", World::Web_worldhandle_createguild);
|
||||
world_webserver->register_route("/addguildmember", World::Web_worldhandle_addguildmember);
|
||||
world_webserver->register_route("/removeguildmember", World::Web_worldhandle_removeguildmember);
|
||||
world_webserver->register_route("/setguildpermission", World::Web_worldhandle_setguildpermission);
|
||||
world_webserver->register_route("/setguildeventfilter", World::Web_worldhandle_setguildeventfilter);
|
||||
world_webserver->run();
|
||||
LogWrite(INIT__INFO, 0, "Init", "World Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port);
|
||||
web_success = true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(INIT__ERROR, 0, "Init", "World Web Server failed to listen on %s:%u due to reason %s", web_ipaddr.c_str(), web_port, e.what());
|
||||
}
|
||||
}
|
||||
if(!web_success) {
|
||||
LogWrite(INIT__WARNING, 0, "Init", "World Web Server not started/configured, cannot attempt peering.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
std::map<std::string, int16> peers = net.GetWebPeers();
|
||||
std::map<std::string, int16>::iterator peer_itr;
|
||||
if(peers.size() > 0) {
|
||||
net.is_primary = false;
|
||||
for(peer_itr = peers.begin(); peer_itr != peers.end(); peer_itr++) {
|
||||
if(net.GetWebWorldAddress() == peer_itr->first && net.GetWebWorldPort() == peer_itr->second)
|
||||
continue; // no good you can't add yourself
|
||||
std::string portNum = std::to_string(peer_itr->second);
|
||||
std::string peerName = "eq2emu_" + peer_itr->first + "_" + portNum;
|
||||
peer_manager.addPeer(peerName, PeeringStatus::SECONDARY, "", "", 0, peer_itr->first, peer_itr->second);
|
||||
peer_https_pool.addPeerClient(peerName, peer_itr->first, std::to_string(peer_itr->second), "/addpeer");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(INIT__ERROR, 0, "Init", "World Web Server failed to listen on %s:%u due to reason %s", web_ipaddr.c_str(), web_port, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -480,30 +533,59 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
|
||||
}
|
||||
if(channel == CHANNEL_PRIVATE_TELL){
|
||||
Client* find_client = zone_list.GetClientByCharName(to);
|
||||
if(!find_client || find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName()))
|
||||
if(find_client && find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName()))
|
||||
return false;
|
||||
else if(find_client == from)
|
||||
|
||||
std::string peerId = peer_manager.GetCharacterPeerId(std::string(to));
|
||||
if(peerId.size() > 0) {
|
||||
std::shared_ptr<Peer> peer = peer_manager.getPeerById(peerId);
|
||||
if(peer != nullptr) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("from_name", from->GetPlayer()->GetName());
|
||||
root.put("to_name", to);
|
||||
root.put("message", message);
|
||||
root.put("from_language", current_language_id);
|
||||
root.put("channel", channel);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peer %s of Tell from %s to %s", __FUNCTION__, peerId.c_str(), from->GetPlayer()->GetName(), to);
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/sendglobalmessage", jsonPayload);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!find_client) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(find_client == from)
|
||||
{
|
||||
from->Message(CHANNEL_COLOR_RED,"You must be very lonely...(ERROR: Cannot send tell to self)");
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* whoto = find_client->GetPlayer()->GetName();
|
||||
find_client->HandleTellMessage(from, message, whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
from->HandleTellMessage(from, message, whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
find_client->HandleTellMessage(from->GetPlayer()->GetName(), message, whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
from->HandleTellMessage(from->GetPlayer()->GetName(), message, whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
if (find_client->GetPlayer()->get_character_flag(CF_AFK)) {
|
||||
find_client->HandleTellMessage(find_client, find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
from->HandleTellMessage(find_client, find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
find_client->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
from->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(channel == CHANNEL_GROUP_SAY) {
|
||||
GroupMemberInfo* gmi = from->GetPlayer()->GetGroupMemberInfo();
|
||||
if(gmi)
|
||||
if(gmi) {
|
||||
world.GetGroupManager()->GroupMessage(gmi->group_id, message);
|
||||
peer_manager.SendPeersChannelMessage(gmi->group_id, "", std::string(message), CHANNEL_GROUP_SAY, from->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(channel == CHANNEL_OUT_OF_CHARACTER) {
|
||||
peer_manager.SendPeersChannelMessage(0, std::string(from->GetPlayer()->GetName()), std::string(message), CHANNEL_OUT_OF_CHARACTER, from->GetPlayer()->GetCurrentLanguage());
|
||||
}
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* zs = 0;
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
@ -517,6 +599,18 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZoneList::SendZoneWideChannelMessage(std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language) {
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* zs = 0;
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end();zone_iter++){
|
||||
zs = *zone_iter;
|
||||
if(zs)
|
||||
zs->HandleChatMessage(fromName, to, channel, message, distance, channel_name, language);
|
||||
}
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void ZoneList::LoadSpellProcess(){
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* zone = 0;
|
||||
@ -580,82 +674,487 @@ void ZoneList::Remove(ZoneServer* zone) {
|
||||
zlist.remove(zone);
|
||||
MZoneList.releasewritelock(__FUNCTION__, __LINE__);
|
||||
|
||||
ZoneServer* alternativeZone = Get(zoneName, false, false);
|
||||
bool alternativeZone = GetZone(nullptr, 0, std::string(zoneName), false, false, false, false, true);
|
||||
if(!alternativeZone && !rule_manager.GetZoneRule(zone->GetZoneID(), R_World, MemoryCacheZoneMaps)->GetBool()) {
|
||||
world.RemoveMaps(std::string(zoneName));
|
||||
}
|
||||
}
|
||||
ZoneServer* ZoneList::Get(const char* zone_name, bool loadZone, bool skip_existing_zones, bool increment_zone) {
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* tmp = 0;
|
||||
ZoneServer* ret = 0;
|
||||
|
||||
if(!skip_existing_zones) {
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
|
||||
tmp = *zone_iter;
|
||||
if (!tmp->isZoneShuttingDown() && !tmp->IsInstanceZone() && strlen(zone_name) == strlen(tmp->GetZoneName()) &&
|
||||
strncasecmp(tmp->GetZoneName(), zone_name, strlen(zone_name))==0){
|
||||
if(tmp->NumPlayers() < 30 || tmp->IsCityZone()) {
|
||||
ret = tmp;
|
||||
if(increment_zone) {
|
||||
ret->IncrementIncomingClients();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if(!ret )
|
||||
{
|
||||
if ( loadZone )
|
||||
{
|
||||
ret = new ZoneServer(zone_name);
|
||||
database.LoadZoneInfo(ret);
|
||||
ret->Init();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ZoneServer* ZoneList::Get(int32 id, bool loadZone, bool skip_existing_zones, bool increment_zone) {
|
||||
bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name, bool loadZone, bool skip_existing_zones, bool increment_zone, bool check_peers, bool check_instances, bool only_always_loaded, bool skip_self) {
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* tmp = 0;
|
||||
ZoneServer* ret = 0;
|
||||
if(!skip_existing_zones) {
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
|
||||
tmp = *zone_iter;
|
||||
if(!tmp->isZoneShuttingDown() && !tmp->IsInstanceZone() && tmp->GetZoneID() == id){
|
||||
if(tmp->NumPlayers() < 30 || tmp->IsCityZone()) {
|
||||
ret = tmp;
|
||||
if(increment_zone) {
|
||||
ret->IncrementIncomingClients();
|
||||
if(!skip_self) {
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
|
||||
tmp = *zone_iter;
|
||||
if(!check_instances && tmp->IsInstanceZone())
|
||||
continue;
|
||||
|
||||
if(!tmp->isZoneShuttingDown() && ((opt_zone_id > 0 && tmp->GetZoneID() == opt_zone_id) || (opt_zone_name.length() > 0 && strncasecmp(tmp->GetZoneName(), opt_zone_name.c_str(), opt_zone_name.length())==0))){
|
||||
if(tmp->NumPlayers() < 30 || tmp->IsCityZone()) {
|
||||
ret = tmp;
|
||||
if(increment_zone) {
|
||||
ret->IncrementIncomingClients();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmp = nullptr;
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if(!ret && check_peers) {
|
||||
std::string peerId = peer_manager.getZonePeerId(opt_zone_name, opt_zone_id, 0, zone_details, only_always_loaded);
|
||||
if(peerId.size() > 0) {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Peer %s is providing zone %s for zone %s id %u", peerId.c_str(), zone_details->zoneName.c_str(), opt_zone_name.c_str(), opt_zone_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if(ret) {
|
||||
tmp = ret;
|
||||
}
|
||||
else if (loadZone) {
|
||||
string zonename = database.GetZoneName(id);
|
||||
if(zonename.length() >0){
|
||||
tmp = new ZoneServer(zonename.c_str());
|
||||
database.LoadZoneInfo(tmp);
|
||||
tmp->Init();
|
||||
if(opt_zone_name.length() < 1) {
|
||||
opt_zone_name = database.GetZoneName(opt_zone_id);
|
||||
}
|
||||
if(opt_zone_name.length() > 0){
|
||||
std::shared_ptr<Peer> peer = peer_manager.getHealthyPeerWithLeastClients();
|
||||
if(check_peers && peer != nullptr) {
|
||||
tmp = new ZoneServer(opt_zone_name.c_str());
|
||||
database.LoadZoneInfo(tmp);
|
||||
boost::property_tree::ptree root;
|
||||
root.put("instance_id", 0);
|
||||
root.put("zone_name", opt_zone_name);
|
||||
root.put("zone_id", std::to_string(opt_zone_id));
|
||||
root.put("always_loaded", only_always_loaded);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peer %s StartZone %s (%u), always loaded %u", __FUNCTION__, peer->id.c_str(), opt_zone_name.c_str(), opt_zone_id, only_always_loaded);
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/startzone", jsonPayload);
|
||||
peer_manager.setZonePeerData(zone_details, peer->id, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, 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());
|
||||
safe_delete(tmp);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
tmp = new ZoneServer(opt_zone_name.c_str());
|
||||
database.LoadZoneInfo(tmp);
|
||||
tmp->Init();
|
||||
tmp->SetAlwaysLoaded(only_always_loaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
|
||||
if(tmp) {
|
||||
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);
|
||||
if(zone_details) {
|
||||
zone_details->zonePtr = (void*)tmp;
|
||||
}
|
||||
}
|
||||
return (tmp != nullptr) ? true : false;
|
||||
}
|
||||
|
||||
bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id, bool loadZone, bool skip_existing_zones, bool increment_zone, bool check_peers) {
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* tmp = 0;
|
||||
ZoneServer* ret = 0;
|
||||
if(!skip_existing_zones) {
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
|
||||
tmp = *zone_iter;
|
||||
if(!tmp->isZoneShuttingDown() && tmp->IsInstanceZone() && tmp->GetInstanceID() == instance_id){
|
||||
ret = tmp;
|
||||
if(increment_zone) {
|
||||
ret->IncrementIncomingClients();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmp = nullptr;
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
if(!ret && check_peers) {
|
||||
std::string peerId = peer_manager.getZonePeerId("", 0, instance_id, zone_details);
|
||||
if(peerId.size() > 0) {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Peer %s is providing instanced zone %s for zone id %u instance id %u", peerId.c_str(), zone_details->zoneName, zone_id, instance_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ret) {
|
||||
tmp = ret;
|
||||
}
|
||||
else if ( loadZone && zone_id > 0 ){
|
||||
string zonename = database.GetZoneName(zone_id);
|
||||
if(zonename.length() > 0){
|
||||
std::shared_ptr<Peer> peer = peer_manager.getHealthyPeerWithLeastClients();
|
||||
if(check_peers && peer != nullptr && instance_id > 0) {
|
||||
tmp = new ZoneServer(zonename.c_str());
|
||||
database.LoadZoneInfo(tmp);
|
||||
boost::property_tree::ptree root;
|
||||
root.put("instance_id", instance_id);
|
||||
root.put("zone_name", zonename);
|
||||
root.put("zone_id", std::to_string(zone_id));
|
||||
root.put("always_loaded", false);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Notify Peer %s StartZone %s (%u), instance %u", __FUNCTION__, peer->id.c_str(), zonename.c_str(), zone_id, instance_id);
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/startzone", jsonPayload);
|
||||
peer_manager.setZonePeerData(zone_details, peer->id, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, std::string(tmp->GetZoneFile()), std::string(tmp->GetZoneName()), tmp->GetZoneID(),
|
||||
instance_id, 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());
|
||||
safe_delete(tmp);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
tmp = new ZoneServer(zonename.c_str());
|
||||
|
||||
// the player is trying to preload an already existing instance but it isn't loaded
|
||||
if ( instance_id > 0 )
|
||||
tmp->SetupInstance(instance_id);
|
||||
|
||||
database.LoadZoneInfo(tmp);
|
||||
tmp->Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(tmp) {
|
||||
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);
|
||||
zone_details->zonePtr = (void*)tmp;
|
||||
}
|
||||
return (tmp != nullptr) ? true : false;
|
||||
}
|
||||
|
||||
bool PeerManager::IsClientConnectedPeer(int32 account_id) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& zone : peer->client_tree->get_child("Clients")) {
|
||||
// Access each field within the current zone
|
||||
int32 client_acct_id = zone.second.get<int32>("account_id");
|
||||
bool is_linkdead = zone.second.get<bool>("is_linkdead");
|
||||
bool is_zoning = zone.second.get<bool>("is_zoning");
|
||||
bool in_zone = zone.second.get<bool>("in_zone");
|
||||
|
||||
if(client_acct_id == account_id) {
|
||||
if(is_zoning)
|
||||
return true;
|
||||
else if(is_linkdead)
|
||||
return false;
|
||||
else if(in_zone)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Clients Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PeerManager::GetCharacterPeerId(std::string charName) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& zone : peer->client_tree->get_child("Clients")) {
|
||||
// Access each field within the current zone
|
||||
|
||||
std::string character_name = zone.second.get<std::string>("character_name");
|
||||
|
||||
if(strncasecmp(character_name.c_str(), charName.c_str(), charName.length())==0) {
|
||||
return peer->id;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Clients Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("message", message);
|
||||
root.put("channel", channel);
|
||||
root.put("group_id", group_id);
|
||||
root.put("from_language", language_id);
|
||||
root.put("from_name", fromName);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
std::vector<int32> raidGroups;
|
||||
world.GetGroupManager()->GetRaidGroups(group_id, &raidGroups);
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& zone : peer->client_tree->get_child("Clients")) {
|
||||
// Access each field within the current zone
|
||||
|
||||
int32 player_group_id = zone.second.get<int32>("group_id");
|
||||
|
||||
|
||||
if(group_id == 0 || group_id == player_group_id || (std::find(raidGroups.begin(), raidGroups.end(), player_group_id) != raidGroups.end())) {
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/sendglobalmessage", jsonPayload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Clients Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("message", message);
|
||||
root.put("channel", channel);
|
||||
root.put("guild_id", guild_id);
|
||||
root.put("from_language", language_id);
|
||||
root.put("from_name", fromName);
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& zone : peer->client_tree->get_child("Clients")) {
|
||||
// Access each field within the current zone
|
||||
|
||||
int32 player_guild_id = zone.second.get<int32>("guild_id");
|
||||
|
||||
|
||||
if(guild_id == 0 || guild_id == player_guild_id) {
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/sendglobalmessage", jsonPayload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Clients Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendZonePeerList(Client* client) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& zone : peer->zone_tree->get_child("Zones")) {
|
||||
// Access each field within the current zone
|
||||
std::string zone_name = zone.second.get<std::string>("zone_name");
|
||||
std::string zone_file_name = zone.second.get<std::string>("zone_file_name");
|
||||
int32 zone_id = zone.second.get<int32>("zone_id");
|
||||
int32 instance_id = zone.second.get<int32>("instance_id");
|
||||
bool shutting_down = zone.second.get<std::string>("shutting_down") == "true";
|
||||
bool instance_zone = zone.second.get<std::string>("instance_zone") == "true";
|
||||
int32 num_players = zone.second.get<int32>("num_players");
|
||||
bool city_zone = zone.second.get<std::string>("city_zone") == "true";
|
||||
float safe_x = zone.second.get<float>("safe_x");
|
||||
float safe_y = zone.second.get<float>("safe_y");
|
||||
float safe_z = zone.second.get<float>("safe_z");
|
||||
float safe_heading = zone.second.get<float>("safe_heading");
|
||||
bool lock_state = zone.second.get<bool>("lock_state");
|
||||
sint16 min_status = zone.second.get<sint16>("min_status");
|
||||
int16 min_level = zone.second.get<int16>("min_level");
|
||||
int16 max_level = zone.second.get<int16>("max_level");
|
||||
int16 min_version = zone.second.get<int16>("min_version");
|
||||
int32 default_lockout_time = zone.second.get<int32>("default_lockout_time");
|
||||
int32 default_reenter_time = zone.second.get<int32>("default_reenter_time");
|
||||
int8 instance_type = zone.second.get<int8>("instance_type");
|
||||
|
||||
client->Message(CHANNEL_COLOR_YELLOW,"Zone (ID) (InstanceID): %s (%u) (%u), Peer: %s, NumPlayers: %u, Locked: %s, ShuttingDown: %s.",zone_name.c_str(),zone_id,
|
||||
instance_id,peer->id.c_str(), num_players, lock_state ? "true" : "false", shutting_down ? "true" : "false");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Zones Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
void PeerManager::sendZonePlayerList(std::vector<string>* queries, std::vector<WhoAllPeerPlayer>* peer_list, bool isGM) {
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
|
||||
bool add_player = true;
|
||||
bool found_match = false;
|
||||
int8 lower = 0;
|
||||
int8 upper = 0;
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& player : peer->client_tree->get_child("Clients")) {
|
||||
std::string char_name = player.second.get<std::string>("character_name");
|
||||
std::string subtitle = player.second.get<std::string>("subtitle");
|
||||
std::string zone_name = player.second.get<std::string>("zonedescription");
|
||||
int8 adventure_class = player.second.get<int8>("adventure_class");
|
||||
int8 tradeskill_class = player.second.get<int8>("tradeskill_class");
|
||||
int8 deity = player.second.get<int8>("deity");
|
||||
int8 race = player.second.get<int8>("race");
|
||||
sint16 status = player.second.get<sint16>("status");
|
||||
int flags = player.second.get<int>("flags");
|
||||
int flags2 = player.second.get<int>("flags2");
|
||||
int16 level = player.second.get<int16>("level");
|
||||
found_match = false;
|
||||
add_player = true;
|
||||
|
||||
for(int32 i=0;add_player && queries && i<queries->size();i++){
|
||||
if(queries->at(i) == "ALL")
|
||||
continue;
|
||||
if(queries->at(i).length() > 3 && classes.GetClassID(queries->at(i).c_str()) > 0){
|
||||
if(adventure_class != classes.GetClassID(queries->at(i).c_str()))
|
||||
add_player = false;
|
||||
found_match = true;
|
||||
}
|
||||
else if(queries->at(i).length() > 2 && races.GetRaceID(queries->at(i).c_str()) > 0){
|
||||
if(race != races.GetRaceID(queries->at(i).c_str()))
|
||||
add_player = false;
|
||||
found_match = true;
|
||||
}
|
||||
if(!found_match && queries->at(i) == "GOOD"){
|
||||
if(deity != 1)
|
||||
add_player = false;
|
||||
found_match = true;
|
||||
}
|
||||
else if(!found_match && queries->at(i) == "EVIL"){
|
||||
if(deity == 1)
|
||||
add_player = false;
|
||||
found_match = true;
|
||||
}
|
||||
if((queries->at(i) == "GUIDE") && (status > 0) && ((status >> 4) < 5))
|
||||
found_match = true;
|
||||
else if((queries->at(i) == "GM") && ((status >> 4) > 4))
|
||||
found_match = true;
|
||||
else if((queries->at(i) == "LFG") && (flags & (1 << CF_LFG)))
|
||||
found_match = true;
|
||||
else if((queries->at(i) == "LFW") && (flags & (1 << CF_LFW)))
|
||||
found_match = true;
|
||||
else if((queries->at(i) == "ROLEPLAYING") && (flags & (1 << CF_ROLEPLAYING)))
|
||||
found_match = true;
|
||||
else if(strspn(queries->at(i).c_str(),"0123456789") == queries->at(i).length()){
|
||||
try{
|
||||
if(lower == 0)
|
||||
lower = atoi(queries->at(i).c_str());
|
||||
else
|
||||
upper = atoi(queries->at(i).c_str());
|
||||
}
|
||||
catch(...){}
|
||||
found_match = true;
|
||||
}
|
||||
if(!found_match){
|
||||
std::string name = ToUpper(char_name);
|
||||
if(name.find(queries->at(i)) == name.npos)
|
||||
add_player = false;
|
||||
}
|
||||
}
|
||||
if(lower > 0 && upper > 0){
|
||||
if(level < lower || level > upper)
|
||||
add_player = false;
|
||||
}
|
||||
else if(lower > 0){
|
||||
if(level != lower)
|
||||
add_player = false;
|
||||
}
|
||||
if((flags2 & (1 << (CF_GM_HIDDEN - 32))) && !isGM) {
|
||||
add_player = false;
|
||||
found_match = true;
|
||||
}
|
||||
if(add_player)
|
||||
peer_list->push_back(WhoAllPeerPlayer(char_name, subtitle, zone_name, adventure_class, tradeskill_class, deity, race,
|
||||
status, flags, flags2, level));
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Clients Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PeerManager::GetClientGuildDetails(int32 matchCharID, GuildMember* member_details) {
|
||||
if(!member_details)
|
||||
return false;
|
||||
|
||||
for (auto& [peerId, peer] : peers) {
|
||||
if(peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(peer->dataMutex);
|
||||
for (const auto& player : peer->client_tree->get_child("Clients")) {
|
||||
std::string char_name = player.second.get<std::string>("character_name");
|
||||
std::string subtitle = player.second.get<std::string>("subtitle");
|
||||
std::string zone_name = player.second.get<std::string>("zonedescription");
|
||||
int8 adventure_class = player.second.get<int8>("adventure_class");
|
||||
int8 tradeskill_class = player.second.get<int8>("tradeskill_class");
|
||||
int8 deity = player.second.get<int8>("deity");
|
||||
int8 race = player.second.get<int8>("race");
|
||||
sint16 status = player.second.get<sint16>("status");
|
||||
int flags = player.second.get<int>("flags");
|
||||
int flags2 = player.second.get<int>("flags2");
|
||||
int16 level = player.second.get<int16>("level");
|
||||
int16 tradeskill_level = player.second.get<int16>("tradeskill_level");
|
||||
int32 character_id = player.second.get<int32>("character_id");
|
||||
int32 account_id = player.second.get<int32>("account_id");
|
||||
|
||||
if(character_id == matchCharID) {
|
||||
member_details->account_id = account_id;
|
||||
member_details->character_id = character_id;
|
||||
strncpy(member_details->name, char_name.c_str(), sizeof(member_details->name));
|
||||
member_details->guild_status = 0;
|
||||
member_details->points = 0.0;
|
||||
member_details->adventure_class = adventure_class;
|
||||
member_details->adventure_level = level;
|
||||
member_details->tradeskill_class = tradeskill_class;
|
||||
member_details->tradeskill_level = tradeskill_level;
|
||||
//gm->rank = rank; ?? not sure how yet
|
||||
member_details->zone = zone_name;
|
||||
//gm->join_date = Timer::GetUnixTimeStamp();
|
||||
//gm->last_login_date = gm->join_date;
|
||||
member_details->recruiter_id = 0;
|
||||
member_details->member_flags = GUILD_MEMBER_FLAGS_NOTIFY_LOGINS;
|
||||
member_details->recruiting_show_adventure_class = 1;
|
||||
member_details->recruiter_picture_data_size = 0;
|
||||
member_details->recruiter_picture_data = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWrite(PEERING__ERROR, 0, "Peering", "%s: Clients Parsing Error %s for %s:%u/%s", __FUNCTION__, e.what() ? e.what() : "??", peer->webAddr.c_str(), peer->webPort);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ZoneList::SendZoneList(Client* client) {
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
@ -664,57 +1163,12 @@ void ZoneList::SendZoneList(Client* client) {
|
||||
int zonesListed = 0;
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
|
||||
tmp = *zone_iter;
|
||||
if ( zonesListed > 20 )
|
||||
{
|
||||
client->Message(CHANNEL_COLOR_YELLOW,"Reached max zone list of 20.");
|
||||
break;
|
||||
}
|
||||
zonesListed++;
|
||||
client->Message(CHANNEL_COLOR_YELLOW,"Zone(ID): %s(%i), Instance ID: %i, Description: %s.",tmp->GetZoneName(),tmp->GetZoneID(),
|
||||
tmp->GetInstanceID(),tmp->GetZoneDescription());
|
||||
client->Message(CHANNEL_COLOR_YELLOW,"Zone (ID) (InstanceID): %s (%u) (%u), Description: %s, NumPlayers: %u, Locked: %s, ShuttingDown: %s.",tmp->GetZoneName(),tmp->GetZoneID(),
|
||||
tmp->GetInstanceID(),tmp->GetZoneDescription(), tmp->NumPlayers(), tmp->GetZoneLockState() ? "true" : "false", tmp->isZoneShuttingDown() ? "true" : "false");
|
||||
}
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
ZoneServer* ZoneList::GetByInstanceID(int32 id, int32 zone_id, bool skip_existing_zones, bool increment_zone) {
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
ZoneServer* tmp = 0;
|
||||
ZoneServer* ret = 0;
|
||||
if(!skip_existing_zones) {
|
||||
MZoneList.readlock(__FUNCTION__, __LINE__);
|
||||
if ( id > 0 )
|
||||
{
|
||||
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
|
||||
tmp = *zone_iter;
|
||||
if(!tmp->isZoneShuttingDown() && tmp->GetInstanceID() == id){
|
||||
ret = tmp;
|
||||
if(increment_zone) {
|
||||
ret->IncrementIncomingClients();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if(ret) {
|
||||
tmp = ret;
|
||||
}
|
||||
else if ( zone_id > 0 ){
|
||||
string zonename = database.GetZoneName(zone_id);
|
||||
if(zonename.length() > 0){
|
||||
tmp = new ZoneServer(zonename.c_str());
|
||||
|
||||
// the player is trying to preload an already existing instance but it isn't loaded
|
||||
if ( id > 0 )
|
||||
tmp->SetupInstance(id);
|
||||
|
||||
database.LoadZoneInfo(tmp);
|
||||
tmp->Init();
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
|
||||
peer_manager.sendZonePeerList(client);
|
||||
}
|
||||
|
||||
ZoneServer* ZoneList::GetByLowestPopulation(int32 zone_id) {
|
||||
@ -781,6 +1235,9 @@ bool ZoneList::ClientConnected(int32 account_id){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!ret) {
|
||||
ret = peer_manager.IsClientConnectedPeer(account_id);
|
||||
}
|
||||
MClientList.unlock();
|
||||
return ret;
|
||||
}
|
||||
@ -911,6 +1368,8 @@ void ZoneList::ProcessWhoQuery(vector<string>* queries, ZoneServer* zone, vector
|
||||
void ZoneList::ProcessWhoQuery(const char* query, Client* client){
|
||||
list<ZoneServer*>::iterator zone_iter;
|
||||
vector<Entity*> players;
|
||||
vector<WhoAllPeerPlayer> peer_players;
|
||||
vector<WhoAllPeerPlayer>::iterator peer_iter;
|
||||
vector<Entity*>::iterator spawn_iter;
|
||||
Entity* player = 0;
|
||||
//for now display all clients
|
||||
@ -931,6 +1390,9 @@ void ZoneList::ProcessWhoQuery(const char* query, Client* client){
|
||||
ProcessWhoQuery(queries, tmp, &players, isGM);
|
||||
}
|
||||
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
if(queries) {
|
||||
peer_manager.sendZonePlayerList(queries, &peer_players, isGM);
|
||||
}
|
||||
}
|
||||
else{
|
||||
ProcessWhoQuery(queries, client->GetCurrentZone(), &players, isGM);
|
||||
@ -940,7 +1402,9 @@ void ZoneList::ProcessWhoQuery(const char* query, Client* client){
|
||||
if(packet){
|
||||
packet->setDataByName("account_id", client->GetAccountID());
|
||||
packet->setDataByName("unknown", 0xFFFFFFFF);
|
||||
int8 num_characters = players.size();
|
||||
int16 num_characters = players.size();
|
||||
int16 num_players_peers = peer_players.size();
|
||||
int16 total_results = num_characters + num_players_peers;
|
||||
int8 max_who_results = 10;
|
||||
int8 max_who_results_status_override = 100;
|
||||
|
||||
@ -956,17 +1420,24 @@ void ZoneList::ProcessWhoQuery(const char* query, Client* client){
|
||||
|
||||
Variable* var1 = variables.FindVariable("max_who_results");
|
||||
if ( var1 ){
|
||||
max_who_results = atoi(var1->GetValue());
|
||||
max_who_results = atoul(var1->GetValue());
|
||||
}
|
||||
|
||||
if(num_characters > max_who_results && client->GetAdminStatus() < max_who_results_status_override){
|
||||
num_characters = max_who_results;
|
||||
if(total_results > max_who_results && client->GetAdminStatus() < max_who_results_status_override){
|
||||
total_results = max_who_results;
|
||||
if(num_characters > total_results)
|
||||
num_characters = total_results;
|
||||
if((num_characters+num_players_peers) > max_who_results) {
|
||||
int16 max_num_players_peers = max_who_results - num_characters;
|
||||
if(num_players_peers > max_num_players_peers)
|
||||
num_players_peers = max_num_players_peers;
|
||||
}
|
||||
packet->setDataByName("response", 3); //response 1 = error message, 3 == capped
|
||||
}
|
||||
else
|
||||
packet->setDataByName("response", 2);
|
||||
packet->setArrayLengthByName("num_characters", num_characters);
|
||||
packet->setDataByName("unknown10", 1);
|
||||
packet->setArrayLengthByName("num_characters", (int8)total_results);
|
||||
packet->setDataByName("display_zone", 1);
|
||||
int i=0;
|
||||
for(spawn_iter = players.begin(); spawn_iter!=players.end(); spawn_iter++, i++){
|
||||
if(i == num_characters)
|
||||
@ -1007,6 +1478,41 @@ void ZoneList::ProcessWhoQuery(const char* query, Client* client){
|
||||
packet->setArrayDataByName("guild", tmp_title, i);
|
||||
}
|
||||
}
|
||||
|
||||
int8 count = 0;
|
||||
for(peer_iter = peer_players.begin(); peer_iter!=peer_players.end(); peer_iter++, i++, count++){
|
||||
if(count == num_players_peers)
|
||||
break;
|
||||
int flags = (*peer_iter).flags;
|
||||
int flags2 = (*peer_iter).flags2;
|
||||
sint16 status = (*peer_iter).status;
|
||||
packet->setArrayDataByName("char_name", (*peer_iter).name.c_str(), i);
|
||||
packet->setArrayDataByName("level", (*peer_iter).level, i);
|
||||
packet->setArrayDataByName("admin_level", ((flags2 & (1 << (CF_HIDE_STATUS - 32))) && !isGM)?0:(status >> 4), i);
|
||||
packet->setArrayDataByName("class", (*peer_iter).adventureClass, i);
|
||||
packet->setArrayDataByName("unknown4", 0xFF, i); //probably tradeskill class
|
||||
packet->setArrayDataByName("flags", (((flags >> CF_ANONYMOUS) & 1) << 0 ) |
|
||||
(((flags >> CF_LFG) & 1) << 1 ) |
|
||||
(((flags >> CF_ANONYMOUS) & 1) << 2 ) |
|
||||
/*(((flags >> CF_HIDDEN) & 1) << 3 ) |*/
|
||||
(((flags >> CF_ROLEPLAYING) & 1) << 4 ) |
|
||||
(((flags >> CF_AFK) & 1) << 5 ) |
|
||||
(((flags >> CF_LFW) & 1) << 6 ) /*|
|
||||
(((flags >> CF_NOTA) & 1) << 7 )*/, i);
|
||||
packet->setArrayDataByName("race", (*peer_iter).race, i);
|
||||
packet->setArrayDataByName("zone", (*peer_iter).zoneName.c_str(), i);
|
||||
if((*peer_iter).subtitle.size() > 0) {
|
||||
size_t start = (*peer_iter).subtitle.find('<');
|
||||
size_t end = (*peer_iter).subtitle.find('>');
|
||||
std::string result = (*peer_iter).subtitle;
|
||||
// Check if both '<' and '>' are found and in the correct order
|
||||
if (start != std::string::npos && end != std::string::npos && start < end) {
|
||||
result = (*peer_iter).subtitle.substr(start + 1, end - start - 1);
|
||||
}
|
||||
packet->setArrayDataByName("guild", result.c_str(), i);
|
||||
}
|
||||
}
|
||||
|
||||
client->QueuePacket(packet->serialize());
|
||||
safe_delete(packet);
|
||||
}
|
||||
@ -1595,7 +2101,7 @@ bool World::RejoinGroup(Client* client, int32 group_id){
|
||||
|
||||
info = *itr;
|
||||
|
||||
if (info && info->name == name)
|
||||
if (info && info->name == name && info->is_client)
|
||||
{
|
||||
info->client = client;
|
||||
info->member = client->GetPlayer();
|
||||
@ -1795,7 +2301,7 @@ void World::AddBonuses(Item* item, ItemStatsValues* values, int16 type, sint32 v
|
||||
}
|
||||
}
|
||||
|
||||
void World::CreateGuild(const char* guild_name, Client* leader, int32 group_id) {
|
||||
int32 World::CreateGuild(const char* guild_name, Client* leader, int32 group_id) {
|
||||
deque<GroupMemberInfo*>::iterator itr;
|
||||
GroupMemberInfo* gmi;
|
||||
Guild *guild;
|
||||
@ -1807,7 +2313,7 @@ void World::CreateGuild(const char* guild_name, Client* leader, int32 group_id)
|
||||
guild->SetFormedDate(Timer::GetUnixTimeStamp());
|
||||
database.LoadGuildDefaultRanks(guild);
|
||||
database.LoadGuildDefaultEventFilters(guild);
|
||||
database.SaveGuild(guild, true);
|
||||
database.SaveGuild(guild, true); // sets the guild id
|
||||
database.SaveGuildEvents(guild);
|
||||
database.SaveGuildRanks(guild);
|
||||
database.SaveGuildEventFilters(guild);
|
||||
@ -1834,6 +2340,8 @@ void World::CreateGuild(const char* guild_name, Client* leader, int32 group_id)
|
||||
|
||||
GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return guild->GetID();
|
||||
}
|
||||
|
||||
void World::SaveGuilds() {
|
||||
@ -2988,4 +3496,40 @@ void World::PurgeNPCSpells() {
|
||||
}
|
||||
|
||||
npc_spell_list.clear();
|
||||
}
|
||||
|
||||
void World::ClientAuthApproval(int32 success, std::string charName, int32 account_id, std::string zone_name, int32 zone_id, int32 instance_id, bool first_login) {
|
||||
Client* find_client = zone_list.GetClientByCharName(charName.c_str());
|
||||
if(find_client) {
|
||||
if(success) {
|
||||
find_client->ApproveZone();
|
||||
}
|
||||
else {
|
||||
int32 zone_success = 0;
|
||||
ZoneChangeDetails details;
|
||||
if(instance_id || zone_id || zone_name.length() > 0) {
|
||||
if(!instance_id) {
|
||||
if((zone_list.GetZone(&details, zone_id, zone_name, true, true, false, false)))
|
||||
zone_success = 1;
|
||||
}
|
||||
else {
|
||||
if((zone_list.GetZoneByInstance(&details, instance_id, zone_id, true, true, false, false)))
|
||||
zone_success = 1;
|
||||
}
|
||||
}
|
||||
if(zone_success) {
|
||||
LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Sending to zone_auth.AddAuth...", __FUNCTION__);
|
||||
int32 key = static_cast<unsigned int>(MakeRandomFloat(0.01,1.0) * UINT32_MAX);
|
||||
|
||||
details.zoneKey = key;
|
||||
details.authDispatchedTime = key;
|
||||
zone_auth.AddAuth(new ZoneAuthRequest(find_client->GetAccountID(), find_client->GetPlayer()->GetName(), key));
|
||||
find_client->SetZoningDetails(&details);
|
||||
find_client->ApproveZone();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// can't find client
|
||||
}
|
||||
}
|
@ -43,6 +43,8 @@
|
||||
#include "./Zone/region_map.h"
|
||||
#include "./Zone/map.h"
|
||||
|
||||
#include "./Web/PeerManager.h"
|
||||
|
||||
using namespace std;
|
||||
struct MerchantInfo{
|
||||
vector<int32> inventory_ids;
|
||||
@ -381,6 +383,35 @@ struct VoiceOverStruct{
|
||||
int8 garble_link_id;
|
||||
};
|
||||
|
||||
struct WhoAllPeerPlayer {
|
||||
std::string name;
|
||||
std::string subtitle;
|
||||
std::string zoneName;
|
||||
int8 adventureClass;
|
||||
int8 tradeskillClass;
|
||||
int8 deity;
|
||||
int8 race;
|
||||
sint16 status;
|
||||
int flags;
|
||||
int flags2;
|
||||
int16 level;
|
||||
|
||||
WhoAllPeerPlayer(std::string inName, std::string inSubtitle, std::string inZoneName, int8 inAdvClass, int8 inTradeskillClass, int8 inDeity, int8 inRace,
|
||||
sint16 inStatus, int inFlags, int inFlags2, int16 inLevel)
|
||||
{
|
||||
name = inName;
|
||||
subtitle = inSubtitle;
|
||||
zoneName = inZoneName;
|
||||
adventureClass = inAdvClass;
|
||||
tradeskillClass = inTradeskillClass;
|
||||
deity = inDeity;
|
||||
race = inRace;
|
||||
status = inStatus;
|
||||
flags = inFlags;
|
||||
flags2 = inFlags2;
|
||||
level = inLevel;
|
||||
}
|
||||
};
|
||||
class ZoneList {
|
||||
public:
|
||||
ZoneList();
|
||||
@ -388,9 +419,13 @@ class ZoneList {
|
||||
|
||||
void Add(ZoneServer* zone);
|
||||
void Remove(ZoneServer* zone);
|
||||
ZoneServer* Get(int32 id, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true);
|
||||
ZoneServer* Get(const char* zone_name, bool loadZone=true, bool skip_existing_zones = false, bool increment_zone = true);
|
||||
ZoneServer* GetByInstanceID(int32 id, int32 zone_id=0, bool skip_existing_zones = false, bool increment_zone = true);
|
||||
bool GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name = "", bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true, bool check_instances = false, bool only_always_loaded = false, bool skip_self = false);
|
||||
bool GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id = 0, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true);
|
||||
|
||||
bool IsClientConnectedPeer(int32 account_id);
|
||||
//ZoneServer* Get(int32 id, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true);
|
||||
//ZoneServer* Get(const char* zone_name, bool loadZone=true, bool skip_existing_zones = false, bool increment_zone = true);
|
||||
//ZoneServer* GetByInstanceID(int32 id, int32 zone_id=0, bool skip_existing_zones = false, bool increment_zone = true);
|
||||
|
||||
/// <summary>Get the instance for the given zone id with the lowest population</summary>
|
||||
/// <param name='zone_id'>The id of the zone to look up</param>
|
||||
@ -467,6 +502,7 @@ class ZoneList {
|
||||
void Depop();
|
||||
void Repop();
|
||||
void DeleteSpellProcess();
|
||||
void SendZoneWideChannelMessage(std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language);
|
||||
void LoadSpellProcess();
|
||||
void ProcessWhoQuery(const char* query, Client* client);
|
||||
void ProcessWhoQuery(vector<string>* queries, ZoneServer* zone, vector<Entity*>* players, bool isGM);
|
||||
@ -481,6 +517,7 @@ class ZoneList {
|
||||
void SendTimeUpdate();
|
||||
|
||||
void PopulateClientList(http::response<http::string_body>& res);
|
||||
void PopulateZoneList(http::response<http::string_body>& res);
|
||||
private:
|
||||
Mutex MClientList;
|
||||
Mutex MZoneList;
|
||||
@ -564,7 +601,7 @@ public:
|
||||
//bool MakeLeader(Client* leader, string new_leader);
|
||||
|
||||
void AddBonuses(Item* item, ItemStatsValues* values, int16 type, sint32 value, Entity* entity);
|
||||
void CreateGuild(const char* guild_name, Client* leader = 0, int32 group_id = 0);
|
||||
int32 CreateGuild(const char* guild_name, Client* leader = 0, int32 group_id = 0);
|
||||
void SaveGuilds();
|
||||
void PickRandomLottoDigits(int32* digits);
|
||||
void AddLottoPlayer(int32 character_id, int32 end_time);
|
||||
@ -648,12 +685,28 @@ public:
|
||||
|
||||
void PurgeNPCSpells();
|
||||
|
||||
void ClientAuthApproval(int32 success, std::string charName, int32 account_id, std::string zone_name, int32 zoning_id, int32 instance_id, bool first_login);
|
||||
|
||||
|
||||
static void Web_worldhandle_status(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_clients(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_setadminstatus(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_reloadrules(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
|
||||
static void Web_worldhandle_reloadcommand(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_addpeer(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_zones(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_addcharauth(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_startzone(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_sendglobalmessage(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_newgroup(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_addgroupmember(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_removegroupmember(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_disbandgroup(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_createguild(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_addguildmember(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_removeguildmember(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_setguildpermission(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_setguildeventfilter(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
Mutex MVoiceOvers;
|
||||
|
||||
static sint64 newValue;
|
||||
|
@ -52,7 +52,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "../common/version.h"
|
||||
#include "SpellProcess.h"
|
||||
#include "races.h"
|
||||
|
||||
#include "Web/PeerManager.h"
|
||||
|
||||
extern Classes classes;
|
||||
extern Commands commands;
|
||||
@ -74,6 +74,7 @@ extern MasterCollectionList master_collection_list;
|
||||
extern RuleManager rule_manager;
|
||||
extern MasterLanguagesList master_languages_list;
|
||||
extern ChestTrapList chest_trap_list;
|
||||
extern PeerManager peer_manager;
|
||||
|
||||
//devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
|
||||
#if defined(__GNUC__)
|
||||
@ -1256,9 +1257,9 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){
|
||||
"ON ss.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID());
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
int32 signXpackFlag = atoul(row[28]);
|
||||
@ -1352,9 +1353,9 @@ void WorldDatabase::LoadWidgets(ZoneServer* zone){
|
||||
"ON sw.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID());
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
int32 widgetXpackFlag = atoul(row[33]);
|
||||
int32 widgetHolidayFlag = atoul(row[34]);
|
||||
@ -1541,9 +1542,9 @@ void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){
|
||||
"ON sg.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID());
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
|
||||
int32 gsXpackFlag = atoul(row[21]);
|
||||
@ -1839,7 +1840,9 @@ SOGA chars looked ok in LoginServer screen tho... odd.
|
||||
if ( LoadCharacterInstances(client) )
|
||||
client->UpdateCharacterInstances();
|
||||
|
||||
if ( instanceid > 0 )
|
||||
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);
|
||||
@ -1881,6 +1884,45 @@ SOGA chars looked ok in LoginServer screen tho... odd.
|
||||
LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name);
|
||||
return false;
|
||||
}
|
||||
std::string WorldDatabase::loadCharacterFromLogin(ZoneChangeDetails* details, int32 char_id, int32 account_id) {
|
||||
std::string name("");
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name, current_zone_id, instance_id FROM characters where id=%u and account_id=%u AND deleted = 0", char_id, account_id);
|
||||
// no character found
|
||||
if ( result == NULL ) {
|
||||
LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character from login for char id '%u'", char_id);
|
||||
return name;
|
||||
}
|
||||
|
||||
if (mysql_num_rows(result) == 1){
|
||||
row = mysql_fetch_row(result);
|
||||
|
||||
int32 current_zone_id = atoul(row[1]);
|
||||
int32 instance_id = atoul(row[2]);
|
||||
LogWrite(PLAYER__DEBUG, 0, "Player", "Loading character from login for '%s' (char_id: %u)", row[0], char_id);
|
||||
|
||||
int32 success = 0;
|
||||
details->peerId = std::string("");
|
||||
details->instanceId = instance_id;
|
||||
details->zoneId = current_zone_id;
|
||||
if(instance_id || current_zone_id) {
|
||||
if(!instance_id) {
|
||||
if((zone_list.GetZone(details, current_zone_id, "", false, false, false, true)))
|
||||
success = 1;
|
||||
}
|
||||
else {
|
||||
if((zone_list.GetZoneByInstance(details, instance_id, current_zone_id, false, false, false, true)))
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
if(success) {
|
||||
name = std::string(row[0]);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadCharacterQuestRewards(Client* client) {
|
||||
Query query;
|
||||
@ -3179,17 +3221,57 @@ string WorldDatabase::GetExpansionIDByVersion(int16 version)
|
||||
|
||||
|
||||
void WorldDatabase::LoadSpecialZones(){
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Starting static zones...");
|
||||
Query query;
|
||||
ZoneServer* zone = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded FROM zones where always_loaded = 1");
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded, peer_priority FROM zones where always_loaded = 1");
|
||||
if(result && mysql_num_rows(result) > 0) {
|
||||
MYSQL_ROW row;
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
zone = new ZoneServer(row[1]);
|
||||
LoadZoneInfo(zone);
|
||||
zone->Init();
|
||||
int16 peer_priority_req = 0;
|
||||
if(row[3]) {
|
||||
peer_priority_req = atoul(row[3]);
|
||||
}
|
||||
|
||||
ZoneChangeDetails zone_details_peer;
|
||||
ZoneChangeDetails zone_details_self;
|
||||
bool gotZonePeer = zone_list.GetZone(&zone_details_peer, 0, std::string(row[1]), /*loadZone*/false, /*skip_existing_zones*/false, /*increment_zone*/false, /*check_peers*/true, /*check_instances*/false,
|
||||
/*only_always_loaded*/true, /*skip_self*/true);
|
||||
bool gotZoneSelf = zone_list.GetZone(&zone_details_self, 0, std::string(row[1]), /*loadZone*/false, /*skip_existing_zones*/false, /*increment_zone*/false, /*check_peers*/false, /*check_instances*/false,
|
||||
/*only_always_loaded*/true, /*skip_self*/false);
|
||||
bool hasPriorityPeer = peer_manager.hasPriorityPeer(peer_priority_req);
|
||||
if(!gotZonePeer && !gotZoneSelf && peer_priority_req == net.GetPeerPriority()) {
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Starting static zone %s.", row[1]);
|
||||
zone = new ZoneServer(row[1]);
|
||||
LoadZoneInfo(zone);
|
||||
zone->Init();
|
||||
|
||||
zone->SetAlwaysLoaded(atoi(row[2]) == 1);
|
||||
zone->SetAlwaysLoaded(atoul(row[2]) == 1);
|
||||
}
|
||||
else if(!gotZonePeer && gotZoneSelf && zone_details_self.zonePtr && !((ZoneServer*)zone_details_self.zonePtr)->AlwaysLoaded()) {
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Making zone %s static as we lost peer to handle the zone.", row[1]);
|
||||
((ZoneServer*)zone_details_self.zonePtr)->SetAlwaysLoaded(true);
|
||||
}
|
||||
else if(gotZonePeer && gotZoneSelf) {
|
||||
std::shared_ptr<Peer> peer = peer_manager.getPeerById(zone_details_peer.peerId);
|
||||
if(peer && peer->healthCheck.status == HealthStatus::OK) {
|
||||
if(peer_priority_req == peer->peerPriority || peer->peerPriority < net.GetPeerPriority()) {
|
||||
ZoneServer* zone = (ZoneServer*)zone_details_self.zonePtr;
|
||||
if(zone) {
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Static zone %s will be spundown due to another peer taking over.", row[1]);
|
||||
zone->SetAlwaysLoaded(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!gotZonePeer && !gotZoneSelf && net.is_primary && (!hasPriorityPeer || peer_priority_req == USHRT_MAX)) {
|
||||
gotZonePeer = zone_list.GetZone(&zone_details_peer, 0, std::string(row[1]), /*loadZone*/true, /*skip_existing_zones*/true, /*increment_zone*/false, /*check_peers*/true, /*check_instances*/false,
|
||||
/*only_always_loaded*/true, /*skip_self*/true);
|
||||
if(!gotZonePeer) {
|
||||
bool gotZoneSelf = zone_list.GetZone(&zone_details_self, 0, std::string(row[1]), /*loadZone*/true, /*skip_existing_zones*/true, /*increment_zone*/false, /*check_peers*/false, /*check_instances*/false,
|
||||
/*only_always_loaded*/true, /*skip_self*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3341,7 +3423,7 @@ int32 WorldDatabase::LoadSpawnLocationGroups(ZoneServer* zone){
|
||||
int32 WorldDatabase::ProcessSpawnLocations(ZoneServer* zone, const char* sql_query, int8 type){
|
||||
int32 number = 0;
|
||||
Query query;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, sql_query, zone->GetZoneID());
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, sql_query, zone->GetZoneID(), zone->GetInstanceID());
|
||||
if(result && mysql_num_rows(result) > 0) {
|
||||
MYSQL_ROW row;
|
||||
int32 spawn_location_id = 0xFFFFFFFF;
|
||||
@ -3432,16 +3514,28 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone)
|
||||
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter LoadSpawns");
|
||||
|
||||
npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
|
||||
signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
|
||||
ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
|
||||
spawn_groups = LoadSpawnLocationGroups(zone);
|
||||
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
|
||||
spawn_group_chances = LoadSpawnGroupChances(zone);
|
||||
LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s)", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances);
|
||||
if(zone->GetInstanceID() == 0) {
|
||||
npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
|
||||
signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
|
||||
ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
|
||||
spawn_groups = LoadSpawnLocationGroups(zone);
|
||||
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
|
||||
spawn_group_chances = LoadSpawnGroupChances(zone);
|
||||
}
|
||||
else {
|
||||
npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
|
||||
signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
|
||||
ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
|
||||
spawn_groups = LoadSpawnLocationGroups(zone);
|
||||
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
|
||||
spawn_group_chances = LoadSpawnGroupChances(zone);
|
||||
|
||||
}
|
||||
LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s)", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances);
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit LoadSpawns");
|
||||
|
||||
}
|
||||
@ -3985,13 +4079,15 @@ void WorldDatabase::UpdateStartingZone(int32 char_id, int8 class_id, int8 race_i
|
||||
|
||||
if(is_instance) // should only be true if we get a result
|
||||
{
|
||||
// this will force a pre-load
|
||||
ZoneServer* instance_zone = zone_list.GetByInstanceID(0, zone_id);
|
||||
if (instance_zone) {
|
||||
string zone_name = GetZoneName(zone_id);
|
||||
if(zone_name.size() > 0) {
|
||||
ZoneServer* tmp = new ZoneServer(zone_name.c_str());
|
||||
instance_id = CreateNewInstance(zone_id);
|
||||
AddCharacterInstance(char_id, instance_id, string(instance_zone->GetZoneName()), instance_zone->GetInstanceType(), Timer::GetUnixTimeStamp(), 0, instance_zone->GetDefaultLockoutTime(), instance_zone->GetDefaultReenterTime());
|
||||
// make sure we inherit the instance id setup in the AddCharacterInstance
|
||||
instance_zone->SetupInstance(instance_id);
|
||||
tmp->SetInstanceID(instance_id);
|
||||
LoadZoneInfo(tmp);
|
||||
instance_id = CreateNewInstance(zone_id);
|
||||
AddCharacterInstance(char_id, instance_id, string(tmp->GetZoneName()), tmp->GetInstanceType(), Timer::GetUnixTimeStamp(), 0, tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime());
|
||||
safe_delete(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4246,12 +4342,20 @@ void WorldDatabase::Save(Client* client){
|
||||
return;
|
||||
|
||||
int32 instance_id = 0;
|
||||
if ( client->GetCurrentZone ( ) != NULL )
|
||||
if(client->IsZoning() && client->GetZoningID()) {
|
||||
instance_id = client->GetZoningInstanceID();
|
||||
}
|
||||
else if ( client->GetCurrentZone ( ) != NULL )
|
||||
instance_id = client->GetCurrentZone()->GetInstanceID();
|
||||
|
||||
|
||||
int32 zone_id = 0;
|
||||
if(client->GetCurrentZone())
|
||||
|
||||
if(client->IsZoning() && client->GetZoningID()) {
|
||||
zone_id = client->GetZoningID();
|
||||
}
|
||||
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 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(),
|
||||
@ -5084,9 +5188,11 @@ int32 WorldDatabase::LoadPlayerSkillbar(Client* client){
|
||||
bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){
|
||||
Guild *guild;
|
||||
|
||||
if((guild = guild_list.GetGuild(GetGuildIDByCharacterID(character_id))))
|
||||
if((guild = guild_list.GetGuild(GetGuildIDByCharacterID(character_id)))) {
|
||||
guild->RemoveGuildMember(character_id);
|
||||
|
||||
peer_manager.sendPeersRemoveGuildMember(character_id, guild->GetID(), "*deleted_character*");
|
||||
}
|
||||
|
||||
Query query;
|
||||
//devn00b: Update this whole thing we were missing many tables. This is ugly but swapped 99% of the delete to async to lighten server load.
|
||||
|
||||
@ -5706,10 +5812,9 @@ int32 WorldDatabase::CheckSpawnRemoveInfo(map<int32,int32>* inmap, int32 spawn_l
|
||||
|
||||
if ( inmap != NULL )
|
||||
{
|
||||
for(iter=inmap->begin();iter!=inmap->end();iter++)
|
||||
{
|
||||
if ( iter->first == spawn_location_entry_id )
|
||||
return (int32)iter->second;
|
||||
iter = inmap->find(spawn_location_entry_id);
|
||||
if(iter != inmap->end()) {
|
||||
return (int32)iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5977,6 +6082,84 @@ bool WorldDatabase::LoadCharacterInstances(Client* client)
|
||||
return addedInstance;
|
||||
}
|
||||
|
||||
|
||||
bool WorldDatabase::DeletePersistedRespawn(int32 zone_id, int32 spawn_location_entry_id)
|
||||
{
|
||||
if( !database_new.Query("DELETE FROM persisted_respawns WHERE zone_id = %u AND spawn_location_entry_id = %u", zone_id, spawn_location_entry_id) )
|
||||
{
|
||||
LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeletePersistedRespawn() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
LogWrite(INSTANCE__DEBUG, 0, "Instance", "Deleted persisted spawn: %u for zone_id %u", spawn_location_entry_id, zone_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 WorldDatabase::CreatePersistedRespawn(int32 spawn_location_entry_id, int32 spawn_type, int32 respawn_time, int32 zone_id)
|
||||
{
|
||||
int32 ret = 0;
|
||||
|
||||
LogWrite(ZONE__DEBUG, 5, "Instance", "-- Creating new persisted Location Entry ID: %u, Type: %u, Respawn: %u, ZoneID: %u", spawn_location_entry_id, spawn_type, respawn_time, zone_id);
|
||||
|
||||
if( !database_new.Query("INSERT INTO persisted_respawns (spawn_location_entry_id, spawn_type, zone_id, respawn_time) values(%u, %u, %u, %u)", spawn_location_entry_id, spawn_type, zone_id, respawn_time) )
|
||||
LogWrite(ZONE__ERROR, 0, "Instance", "Error in CreatePersistedRespawn() query '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
|
||||
else
|
||||
ret = database_new.LastInsertID();
|
||||
|
||||
// potentially spammy, if it calls for every spawn added. Set to level 3 or 5?
|
||||
if( ret > 0 )
|
||||
LogWrite(ZONE__DEBUG, 5, "Instance", "Created new persisted respawn entry: %u for zone_id %u", ret, zone_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
map<int32,int32>* WorldDatabase::GetPersistedSpawns(int32 zone_id, int8 type)
|
||||
{
|
||||
DatabaseResult result;
|
||||
map<int32,int32>* ret = NULL;
|
||||
|
||||
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
|
||||
|
||||
LogWrite(INSTANCE__DEBUG, 0, "Instance", "Loading persisted spawns for zone_id: %u, spawn_type: %u", zone_id, type);
|
||||
|
||||
if( !database_new.Select(&result, "SELECT spawn_location_entry_id, respawn_time FROM persisted_respawns WHERE zone_id = %u AND spawn_type = %u", zone_id, type) )
|
||||
{
|
||||
LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceRemovedSpawns() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( result.GetNumRows() > 0 )
|
||||
{
|
||||
ret = new map<int32,int32>;
|
||||
|
||||
while( result.Next() )
|
||||
{
|
||||
int32 spawn_location_entry_id = result.GetInt32Str("spawn_location_entry_id");
|
||||
/*
|
||||
respawnTime == 0 - never respawn
|
||||
respawnTime = 1 - spawn now
|
||||
respawnTime > 1 (continue timer)
|
||||
*/
|
||||
int32 respawntime = result.GetInt32Str("respawn_time");
|
||||
|
||||
LogWrite(INSTANCE__DEBUG, 5, "Instance", "Found persisted spawn point: %u, respawn time: %u", spawn_location_entry_id, respawntime);
|
||||
|
||||
ret->insert(make_pair(spawn_location_entry_id, respawntime));
|
||||
}
|
||||
}
|
||||
else
|
||||
LogWrite(INSTANCE__DEBUG, 0, "Instance", "No persisted spawns found for zone_id: %u, spawn_type: %u", zone_id, type);
|
||||
|
||||
}
|
||||
|
||||
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void WorldDatabase::UpdateLoginEquipment()
|
||||
{
|
||||
LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Updating `character_items` CRC in %s", __FUNCTION__);
|
||||
|
@ -322,6 +322,7 @@ public:
|
||||
void LoadDataFromRow(DatabaseResult *result, Item* item);
|
||||
void LoadCharacterItemList(int32 account_id, int32 char_id, Player* player, int16);
|
||||
bool loadCharacter(const char* name, int32 account_id, Client* client);
|
||||
std::string loadCharacterFromLogin(ZoneChangeDetails* details, int32 char_id, int32 account_id);
|
||||
bool LoadCharacterStats(int32 id, int32 account_id, Client* client);
|
||||
void LoadCharacterQuestRewards(Client* client);
|
||||
void LoadCharacterQuestTemporaryRewards(Client* client, int32 quest_id);
|
||||
@ -411,6 +412,10 @@ public:
|
||||
bool DeleteInstanceSpawnRemoved(int32 instance_id, int32 spawn_location_entry_id);
|
||||
bool DeleteCharacterFromInstance(int32 char_id, int32 instance_id);
|
||||
bool LoadCharacterInstances(Client* client);
|
||||
|
||||
bool DeletePersistedRespawn(int32 zone_id, int32 spawn_location_entry_id);
|
||||
int32 CreatePersistedRespawn(int32 spawn_location_entry_id, int32 spawn_type, int32 respawn_time, int32 zone_id);
|
||||
map<int32,int32>* GetPersistedSpawns(int32 zone_id, int8 type);
|
||||
//
|
||||
|
||||
MutexMap<int32, LoginEquipmentUpdate>* GetEquipmentUpdates();
|
||||
@ -468,6 +473,7 @@ public:
|
||||
|
||||
/* Guilds */
|
||||
void LoadGuilds();
|
||||
void LoadGuild(int32 guild_id);
|
||||
int32 LoadGuildMembers(Guild* guild);
|
||||
void LoadGuildEvents(Guild* guild);
|
||||
void LoadGuildRanks(Guild* guild);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,11 +32,14 @@
|
||||
#include "Player.h"
|
||||
#include "Quests.h"
|
||||
|
||||
#include "Web/PeerManager.h"
|
||||
|
||||
using namespace std;
|
||||
#define CLIENT_TIMEOUT 60000
|
||||
struct TransportDestination;
|
||||
struct ConversationOption;
|
||||
struct VoiceOverStruct;
|
||||
struct GroupOptions;
|
||||
|
||||
#define MAIL_SEND_RESULT_SUCCESS 0
|
||||
#define MAIL_SEND_RESULT_UNKNOWN_PLAYER 1
|
||||
@ -165,13 +168,14 @@ public:
|
||||
void QueuePacket(EQ2Packet* app, bool attemptedCombine=false);
|
||||
void SendLoginInfo();
|
||||
int8 GetMessageChannelColor(int8 channel_type);
|
||||
void HandleTellMessage(Client* from, const char* message, const char* to, int32 current_language_id);
|
||||
void HandleTellMessage(const char* fromName, const char* message, const char* to, int32 current_language_id);
|
||||
void SimpleMessage(int8 color, const char* message);
|
||||
void Message(int8 type, const char* message, ...);
|
||||
void SendSpellUpdate(Spell* spell, bool add_silently = false, bool add_to_hotbar = true);
|
||||
void Zone(ZoneServer* new_zone, bool set_coords = true, bool is_spell = false);
|
||||
void Zone(ZoneChangeDetails* new_zone, ZoneServer* opt_zone = nullptr, bool set_coords = true, bool is_spell = false);
|
||||
void Zone(const char* new_zone, bool set_coords = true, bool is_spell = false);
|
||||
void Zone(int32 instanceid, bool set_coords = true, bool byInstanceID=false, bool is_spell = false);
|
||||
void ApproveZone();
|
||||
void SendZoneInfo();
|
||||
void SendZoneSpawns();
|
||||
void HandleVerbRequest(EQApplicationPacket* app);
|
||||
@ -194,7 +198,7 @@ public:
|
||||
void ChangeTSLevel(int16 old_level, int16 new_level);
|
||||
bool Summon(const char* search_name);
|
||||
std::string IdentifyInstanceLockout(int32 zoneID, bool displayClient = true);
|
||||
ZoneServer* IdentifyInstance(int32 zoneID);
|
||||
bool IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID);
|
||||
bool TryZoneInstance(int32 zoneID, bool zone_coords_valid=false);
|
||||
bool GotoSpawn(const char* search_name, bool forceTarget=false);
|
||||
void DisplayDeadWindow();
|
||||
@ -588,6 +592,15 @@ public:
|
||||
int32 GetSpellVisualOverride(int32 spell_visual);
|
||||
|
||||
sint16 GetClientItemPacketOffset() { sint16 offset = -1; if(GetVersion() <= 373) { offset = -2; } return offset; }
|
||||
|
||||
int32 GetZoningID() { return zoning_id; }
|
||||
int32 GetZoningInstanceID() { return zoning_instance_id; }
|
||||
|
||||
void SetZoningDetails(ZoneChangeDetails* details) { zoning_details = ZoneChangeDetails(details); }
|
||||
|
||||
void HandleGroupAcceptResponse(int8 result);
|
||||
void SetGroupOptionsReference(GroupOptions* options);
|
||||
void SendReceiveOffer(Client* client_target, int8 type, std::string name, int8 unknown2);
|
||||
private:
|
||||
void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i);
|
||||
void SavePlayerImages();
|
||||
@ -657,7 +670,9 @@ private:
|
||||
|
||||
bool seencharsel;
|
||||
bool connected_to_zone;
|
||||
bool client_zoning;
|
||||
std::atomic<bool> client_zoning;
|
||||
std::atomic<bool> client_zoning_details_set;
|
||||
ZoneChangeDetails zoning_details;
|
||||
int32 zoning_id;
|
||||
int32 zoning_instance_id;
|
||||
ZoneServer* zoning_destination;
|
||||
@ -691,7 +706,7 @@ private:
|
||||
string* pending_last_name;
|
||||
IncomingPaperdollImage incoming_paperdoll;
|
||||
int32 transmuteID;
|
||||
ZoneServer* GetHouseZoneServer(int32 spawn_id, int64 house_id);
|
||||
bool GetHouseZoneServer(ZoneChangeDetails* zone_details, int32 spawn_id, int64 house_id);
|
||||
|
||||
std::atomic<bool> m_recipeListSent;
|
||||
bool initial_spawns_sent;
|
||||
|
@ -19,7 +19,7 @@ Lua_C_Flags = -DLUA_COMPAT_ALL -DLUA_USE_LINUX
|
||||
Lua_W_Flags = -Wall
|
||||
|
||||
C_Flags = -I/eq2emu/fmt/include -I/eq2emu/recastnavigation/Detour/Include -I/usr/include/mariadb -I/usr/local/include/boost -I/usr/include/glm -I/usr/include/lua5.4 -march=native -pipe -pthread -std=c++17
|
||||
LD_Flags = -L/usr/lib/x86_64-linux-gnu -lmariadb -lz -lpthread -L/eq2emu/recastnavigation/RecastDemo/Build/gmake2/lib/Debug -lDebugUtils -lDetour -lDetourCrowd -lDetourTileCache -lRecast -llua5.4-c++ -L/usr/local/lib -rdynamic -lm -Wl,-E -ldl -lreadline -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_iostreams -lboost_regex
|
||||
LD_Flags = -L/usr/lib/x86_64-linux-gnu -lmariadb -lz -lpthread -L/eq2emu/recastnavigation/RecastDemo/Build/gmake2/lib/Debug -lDebugUtils -lDetour -lDetourCrowd -lDetourTileCache -lRecast -llua5.4-c++ -L/usr/local/lib -rdynamic -lm -Wl,-E -ldl -lreadline -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_iostreams -lboost_regex -lboost_program_options
|
||||
|
||||
# World flags
|
||||
W_Flags = -Wall -Wno-reorder
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "../common/debug.h"
|
||||
#include "../common/Log.h"
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
#include <string.h>
|
||||
@ -59,6 +60,9 @@ using namespace std;
|
||||
#include "Transmute.h"
|
||||
#include "Zone/ChestTrap.h"
|
||||
|
||||
#include "Web/PeerManager.h"
|
||||
#include "Web/HTTPSClientPool.h"
|
||||
|
||||
//devn00b
|
||||
#ifdef DISCORD
|
||||
//linux only for the moment.
|
||||
@ -111,6 +115,8 @@ extern MasterSkillList master_skill_list;
|
||||
extern MasterItemList master_item_list;
|
||||
extern GuildList guild_list;
|
||||
extern Variables variables;
|
||||
extern PeerManager peer_manager;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
ConfigReader configReader;
|
||||
int32 MasterItemList::next_unique_id = 0;
|
||||
int last_signal = 0;
|
||||
@ -125,6 +131,7 @@ extern map<int16, int16> EQOpcodeVersions;
|
||||
ThreadReturnType ItemLoad (void* tmp);
|
||||
ThreadReturnType AchievmentLoad (void* tmp);
|
||||
ThreadReturnType SpellLoad (void* tmp);
|
||||
ThreadReturnType StartPeerPoll (void* tmp);
|
||||
//devn00b
|
||||
#ifdef DISCORD
|
||||
#ifndef WIN32
|
||||
@ -133,6 +140,7 @@ ThreadReturnType SpellLoad (void* tmp);
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
net.is_primary = true;
|
||||
#ifdef PROFILER
|
||||
PROFILE_FUNC();
|
||||
#endif
|
||||
@ -184,8 +192,8 @@ int main(int argc, char** argv) {
|
||||
LogWrite(WORLD__DEBUG, 0, "World", "Randomizing World...");
|
||||
srand(time(NULL));
|
||||
|
||||
net.ReadLoginINI();
|
||||
|
||||
net.ReadLoginINI(argc, argv);
|
||||
|
||||
// JA: Grouping all System (core) data loads together for timing purposes
|
||||
LogWrite(WORLD__INFO, 0, "World", "Loading System Data...");
|
||||
int32 t_now = Timer::GetUnixTimeStamp();
|
||||
@ -371,6 +379,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
world.world_loaded = true;
|
||||
world.world_uptime = getCurrentTimestamp();
|
||||
#ifdef WIN32
|
||||
_beginthread(StartPeerPoll, 0, NULL);
|
||||
#else
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, &StartPeerPoll, NULL);
|
||||
pthread_detach(thread);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
LogWrite(NET__ERROR, 0, "Net", "Failed to open port %i.", net.GetWorldPort());
|
||||
@ -384,8 +399,11 @@ int main(int argc, char** argv) {
|
||||
TimeoutTimer->Start();
|
||||
EQStream* eqs = 0;
|
||||
UpdateWindowTitle(0);
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Starting static zones...");
|
||||
database.LoadSpecialZones();
|
||||
|
||||
if(net.is_primary) {
|
||||
database.LoadSpecialZones();
|
||||
}
|
||||
|
||||
map<EQStream*, int32> connecting_clients;
|
||||
map<EQStream*, int32>::iterator cc_itr;
|
||||
|
||||
@ -469,7 +487,7 @@ int main(int argc, char** argv) {
|
||||
database.PingNewDB();
|
||||
database.PingAsyncDatabase();
|
||||
|
||||
if (net.LoginServerInfo && loginserver.Connected() == false && loginserver.CanReconnect()) {
|
||||
if (net.is_primary && net.LoginServerInfo && loginserver.Connected() == false && loginserver.CanReconnect()) {
|
||||
LogWrite(WORLD__DEBUG, 0, "Thread", "Starting autoinit loginserver thread...");
|
||||
#ifdef WIN32
|
||||
_beginthread(AutoInitLoginServer, 0, NULL);
|
||||
@ -495,6 +513,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
LogWrite(WORLD__DEBUG, 0, "World", "The world is ending!");
|
||||
|
||||
peer_https_pool.stopPolling();
|
||||
|
||||
LogWrite(WORLD__DEBUG, 0, "World", "Shutting down zones...");
|
||||
zone_list.ShutDownZones();
|
||||
|
||||
@ -612,6 +632,14 @@ ThreadReturnType AchievmentLoad (void* tmp)
|
||||
THREAD_RETURN(NULL);
|
||||
}
|
||||
|
||||
ThreadReturnType StartPeerPoll (void* tmp)
|
||||
{
|
||||
LogWrite(WORLD__WARNING, 0, "Thread", "Start Polling...");
|
||||
peer_https_pool.startPolling();
|
||||
THREAD_RETURN(NULL);
|
||||
}
|
||||
|
||||
|
||||
ThreadReturnType EQ2ConsoleListener(void* tmp)
|
||||
{
|
||||
char cmd[300];
|
||||
@ -663,7 +691,7 @@ void CatchSignal(int sig_num) {
|
||||
}
|
||||
}
|
||||
|
||||
bool NetConnection::ReadLoginINI() {
|
||||
bool NetConnection::ReadLoginINI(int argc, char** argv) {
|
||||
JsonParser parser(MAIN_CONFIG_FILE);
|
||||
if(!parser.IsLoaded()) {
|
||||
LogWrite(INIT__ERROR, 0, "Init", "Failed to find %s in server directory..", MAIN_CONFIG_FILE);
|
||||
@ -730,6 +758,43 @@ bool NetConnection::ReadLoginINI() {
|
||||
web_keypassword = parser.getValue("worldserver.webkeypassword");
|
||||
web_hardcodeuser = parser.getValue("worldserver.webhardcodeuser");
|
||||
web_hardcodepassword = parser.getValue("worldserver.webhardcodepassword");
|
||||
web_cmduser = parser.getValue("worldserver.webcmduser");
|
||||
web_cmdpassword = parser.getValue("worldserver.webcmdpassword");
|
||||
|
||||
std::string base_peeraddress = "worldserver.peeraddress";
|
||||
std::string base_peerport = "worldserver.peerport";
|
||||
|
||||
for (int i = -1; i <= 100; ++i) {
|
||||
std::string peeraddress = base_peeraddress;
|
||||
std::string peerport = base_peerport;
|
||||
if(i > -1){
|
||||
peeraddress = base_peeraddress + std::to_string(i);
|
||||
peerport = base_peerport + std::to_string(i);
|
||||
}
|
||||
|
||||
// Assuming parser.getValue can handle the concatenated strings.
|
||||
std::string web_peeraddress = parser.getValue(peeraddress);
|
||||
std::string web_peerport = parser.getValue(peerport);
|
||||
if(web_peeraddress.size() > 0 && web_peerport.size() > 0) {
|
||||
int16 port = 0;
|
||||
parser.convertStringToUnsignedShort(web_peerport, port);
|
||||
if(port > 0) {
|
||||
web_peers[web_peeraddress] = port;
|
||||
LogWrite(INIT__INFO, 0, "Init", "Adding peer %s:%u...", web_peeraddress.c_str(), port);
|
||||
}
|
||||
else {
|
||||
LogWrite(INIT__ERROR, 0, "Init", "Error peer %s:%u at position %i, skipped.", web_peeraddress.c_str(), port, i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string webpeerpriority_str = parser.getValue("worldserver.peerpriority");
|
||||
parser.convertStringToUnsignedShort(webpeerpriority_str, web_peerpriority);
|
||||
|
||||
peer_https_pool.init(web_certfile, web_keyfile);
|
||||
|
||||
std::string webloginport_str = parser.getValue("worldserver.webport");
|
||||
parser.convertStringToUnsignedShort(webloginport_str, web_worldport);
|
||||
@ -737,6 +802,54 @@ bool NetConnection::ReadLoginINI() {
|
||||
std::string defaultstatus_str = parser.getValue("worldserver.defaultstatus");
|
||||
parser.convertStringToUnsignedChar(defaultstatus_str, DEFAULTSTATUS);
|
||||
|
||||
|
||||
// Define namespace for ease of use
|
||||
namespace po = boost::program_options;
|
||||
|
||||
// Variables to store parsed options
|
||||
std::string worldAddress("");
|
||||
std::string internalWorldAddress("");
|
||||
std::string webWorldAddress("");
|
||||
uint16 worldPort = 0;
|
||||
uint16 webWorldPort = 0;
|
||||
uint16 peerPriority = 0;
|
||||
|
||||
// Setup options
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("worldaddress", po::value<std::string>(&worldAddress), "World address")
|
||||
("internalworldaddress", po::value<std::string>(&internalWorldAddress), "Internal world address")
|
||||
("worldport", po::value<uint16>(&worldPort)->default_value(0), "Web world port")
|
||||
("webworldaddress", po::value<std::string>(&webWorldAddress), "Web world address")
|
||||
("webworldport", po::value<uint16>(&webWorldPort)->default_value(0), "Web world port")
|
||||
("peerpriority", po::value<uint16>(&peerPriority)->default_value(0), "Peer priority");
|
||||
|
||||
// Parse the arguments
|
||||
po::variables_map vm;
|
||||
try {
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
po::notify(vm);
|
||||
}
|
||||
catch (const po::error &e) {
|
||||
std::cerr << "Error parsing options: " << e.what() << "\n";
|
||||
std::cout << desc << "\n";
|
||||
}
|
||||
if(peerPriority)
|
||||
web_peerpriority = peerPriority;
|
||||
if(webWorldPort)
|
||||
web_worldport = webWorldPort;
|
||||
if(worldPort)
|
||||
worldport = worldPort;
|
||||
|
||||
if(worldAddress.size() > 0)
|
||||
snprintf(worldaddress, sizeof(worldaddress), "%s", worldAddress.c_str());
|
||||
|
||||
if(internalWorldAddress.size() > 0)
|
||||
snprintf(internalworldaddress, sizeof(internalworldaddress), "%s", internalWorldAddress.c_str());
|
||||
|
||||
if(webWorldAddress.size() > 0)
|
||||
web_worldaddress = webWorldAddress;
|
||||
|
||||
LogWrite(INIT__DEBUG, 0, "Init", "%s read...", MAIN_CONFIG_FILE);
|
||||
LoginServerInfo=1;
|
||||
return true;
|
||||
@ -763,6 +876,11 @@ char* NetConnection::GetLoginInfo(int16* oPort) {
|
||||
return loginaddress[tmp[x]];
|
||||
}
|
||||
|
||||
void NetConnection::SetPrimary(bool isprimary) {
|
||||
net.is_primary = isprimary;
|
||||
database.LoadSpecialZones();
|
||||
}
|
||||
|
||||
void UpdateWindowTitle(char* iNewTitle) {
|
||||
|
||||
char tmp[500];
|
||||
|
@ -34,6 +34,9 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
|
||||
#include "../common/linked_list.h"
|
||||
#include "../common/types.h"
|
||||
|
||||
@ -64,12 +67,14 @@ public:
|
||||
LoginServerInfo = 0;//ReadLoginINI();
|
||||
UpdateStats = false;
|
||||
web_worldport = 0;
|
||||
web_peerpriority = 0;
|
||||
}
|
||||
~NetConnection() { }
|
||||
|
||||
bool ReadLoginINI();
|
||||
bool ReadLoginINI(int argc, char** argv);
|
||||
void WelcomeHeader();
|
||||
|
||||
void SetPrimary(bool isprimary = true);
|
||||
|
||||
bool LoginServerInfo;
|
||||
bool UpdateStats;
|
||||
char* GetLoginInfo(int16* oPort);
|
||||
@ -89,7 +94,12 @@ public:
|
||||
std::string GetWebKeyPassword() { return web_keypassword; }
|
||||
std::string GetWebHardcodeUser() { return web_hardcodeuser; }
|
||||
std::string GetWebHardcodePassword() { return web_hardcodepassword; }
|
||||
std::string GetCmdUser() { return web_cmduser; }
|
||||
std::string GetCmdPassword() { return web_cmdpassword; }
|
||||
std::map<std::string, int16> GetWebPeers() { std::map<std::string, int16> copied_map(web_peers); return copied_map; }
|
||||
int16 GetPeerPriority() { return web_peerpriority; }
|
||||
bool world_locked;
|
||||
std::atomic<bool> is_primary;
|
||||
private:
|
||||
int listening_socket;
|
||||
char loginaddress[4][255];
|
||||
@ -107,7 +117,11 @@ private:
|
||||
std::string web_keypassword;
|
||||
std::string web_hardcodeuser;
|
||||
std::string web_hardcodepassword;
|
||||
std::string web_cmduser;
|
||||
std::string web_cmdpassword;
|
||||
std::map<std::string, int16> web_peers;
|
||||
int16 web_worldport;
|
||||
int16 web_peerpriority;
|
||||
|
||||
};
|
||||
|
||||
|
@ -167,18 +167,26 @@ ZoneServer::ZoneServer(const char* name) {
|
||||
strcpy(zonesky_file,"");
|
||||
|
||||
reloading = true;
|
||||
spawnthread_active = false;
|
||||
movementMgr = nullptr;
|
||||
spellProcess = nullptr;
|
||||
tradeskillMgr = nullptr;
|
||||
watchdogTimestamp = Timer::GetCurrentTime2();
|
||||
|
||||
MPendingSpawnRemoval.SetName("ZoneServer::MPendingSpawnRemoval");
|
||||
|
||||
lifetime_client_count = 0;
|
||||
|
||||
is_initialized = false;
|
||||
}
|
||||
|
||||
typedef map <int32, bool> ChangedSpawnMapType;
|
||||
ZoneServer::~ZoneServer() {
|
||||
zoneShuttingDown = true; //ensure other threads shut down too
|
||||
//allow other threads to properly shut down
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Initiating zone shutdown of '%s'", zone_name);
|
||||
if(is_initialized) {
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Initiating zone shutdown of '%s'", zone_name);
|
||||
}
|
||||
int32 disp_count = 0;
|
||||
int32 next_disp_count = 100;
|
||||
while (spawnthread_active || initial_spawn_threads_active > 0){
|
||||
@ -246,7 +254,9 @@ ZoneServer::~ZoneServer() {
|
||||
grid_maps.clear();
|
||||
MGridMaps.unlock();
|
||||
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Completed zone shutdown of '%s'", zone_name);
|
||||
if(is_initialized) {
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Completed zone shutdown of '%s'", zone_name);
|
||||
}
|
||||
--numzones;
|
||||
UpdateWindowTitle(0);
|
||||
zone_list.Remove(this);
|
||||
@ -276,6 +286,8 @@ void ZoneServer::Init()
|
||||
{
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Loading new Zone '%s'", zone_name);
|
||||
zone_list.Add(this);
|
||||
|
||||
is_initialized = true;
|
||||
|
||||
spellProcess = new SpellProcess();
|
||||
tradeskillMgr = new TradeskillMgr();
|
||||
@ -337,6 +349,17 @@ void ZoneServer::Init()
|
||||
pathing = IPathfinder::Load(zoneName);
|
||||
movementMgr = new MobMovementManager();
|
||||
|
||||
if(GetInstanceID()) {
|
||||
PlayerHouse* ph = world.GetPlayerHouseByInstanceID(GetInstanceID());
|
||||
if(ph) {
|
||||
HouseZone* hz = world.GetHouseZone(ph->house_id);
|
||||
if(hz) {
|
||||
std::string desc = ph->player_name + "'s " + hz->name;
|
||||
SetZoneDescription((char*)desc.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MMasterSpawnLock.SetName("ZoneServer::MMasterSpawnLock");
|
||||
m_npc_faction_list.SetName("ZoneServer::npc_faction_list");
|
||||
m_enemy_faction_list.SetName("ZoneServer::enemy_faction_list");
|
||||
@ -1414,10 +1437,10 @@ void ZoneServer::LootProcess(Spawn* spawn) {
|
||||
looter = entry;
|
||||
}
|
||||
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
|
||||
world.GetGroupManager()->SendGroupMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %u on %s.", entry->GetName(), out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
||||
world.GetGroupManager()->SendGroupChatMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %u on %s.", entry->GetName(), out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
||||
}
|
||||
else {
|
||||
world.GetGroupManager()->SendGroupMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %s (%u) on %s.", entry->GetName(), itemNeed ? "NEED" : "GREED", out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
||||
world.GetGroupManager()->SendGroupChatMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %s (%u) on %s.", entry->GetName(), itemNeed ? "NEED" : "GREED", out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1493,6 +1516,10 @@ void ZoneServer::DeleteSpawns(bool delete_all) {
|
||||
continue;
|
||||
|
||||
spawn = itr->first;
|
||||
|
||||
lua_interface->SetLuaUserDataStale(spawn);
|
||||
spellProcess->RemoveCaster(spawn);
|
||||
|
||||
if(movementMgr != nullptr) {
|
||||
movementMgr->RemoveMob((Entity*)spawn);
|
||||
}
|
||||
@ -1508,8 +1535,6 @@ void ZoneServer::DeleteSpawns(bool delete_all) {
|
||||
spawn_delete_list.erase(erase_itr);
|
||||
|
||||
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
||||
lua_interface->SetLuaUserDataStale(spawn);
|
||||
|
||||
std::map<int32, Spawn*>::iterator sitr = spawn_list.find(spawn->GetID());
|
||||
if(sitr != spawn_list.end()) {
|
||||
spawn_list.erase(sitr);
|
||||
@ -1530,7 +1555,6 @@ void ZoneServer::DeleteSpawns(bool delete_all) {
|
||||
housing_spawn_map.erase(spawn->GetID());
|
||||
}
|
||||
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
||||
spellProcess->RemoveCaster(spawn);
|
||||
safe_delete(spawn);
|
||||
}
|
||||
else
|
||||
@ -1710,8 +1734,12 @@ bool ZoneServer::Process()
|
||||
}
|
||||
|
||||
// client loop
|
||||
if(charsheet_changes.Check())
|
||||
if(charsheet_changes.Check()) {
|
||||
SendCharSheetChanges();
|
||||
}
|
||||
else {
|
||||
SendRaidSheetChanges();
|
||||
}
|
||||
|
||||
// Client loop
|
||||
ClientProcess(startupDelayTimer.Enabled());
|
||||
@ -1998,15 +2026,13 @@ void ZoneServer::CheckRespawns(){
|
||||
for(int i=tmp_respawn_list.size()-1;i>=0;i--){
|
||||
if ( IsInstanceZone() )
|
||||
{
|
||||
if ( database.DeleteInstanceSpawnRemoved(GetInstanceID(),tmp_respawn_list[i]) )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
database.DeleteInstanceSpawnRemoved(GetInstanceID(),tmp_respawn_list[i]);
|
||||
}
|
||||
else {
|
||||
database.DeletePersistedRespawn(GetZoneID(),tmp_respawn_list[i]);
|
||||
}
|
||||
|
||||
ProcessSpawnLocation(tmp_respawn_list[i], true);
|
||||
ProcessSpawnLocation(tmp_respawn_list[i], nullptr, nullptr, nullptr, nullptr, nullptr, true);
|
||||
respawn_timers.erase(tmp_respawn_list[i]);
|
||||
}
|
||||
}
|
||||
@ -2291,6 +2317,23 @@ void ZoneServer::SendPlayerPositionChanges(Player* player){
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneServer::SendRaidSheetChanges(){
|
||||
vector<Client*>::iterator client_itr;
|
||||
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
||||
Client* client = (Client*)(*client_itr);
|
||||
if(client && client->IsConnected() && client->GetPlayer()->GetRaidSheetChanged()) {
|
||||
client->GetPlayer()->SetRaidSheetChanged(false);
|
||||
EQ2Packet* packet = client->GetPlayer()->GetRaidUpdatePacket(client->GetVersion());
|
||||
if(packet) {
|
||||
client->QueuePacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void ZoneServer::SendCharSheetChanges(){
|
||||
vector<Client*>::iterator client_itr;
|
||||
|
||||
@ -2301,9 +2344,11 @@ void ZoneServer::SendCharSheetChanges(){
|
||||
}
|
||||
|
||||
void ZoneServer::SendCharSheetChanges(Client* client){
|
||||
if(client && client->IsConnected() && client->GetPlayer()->GetCharSheetChanged()){
|
||||
client->GetPlayer()->SetCharSheetChanged(false);
|
||||
ClientPacketFunctions::SendCharacterSheet(client);
|
||||
if(client && client->IsConnected()){
|
||||
if(client->GetPlayer()->GetCharSheetChanged()) {
|
||||
client->GetPlayer()->SetCharSheetChanged(false);
|
||||
ClientPacketFunctions::SendCharacterSheet(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2377,7 +2422,7 @@ int32 ZoneServer::CalculateSpawnGroup(SpawnLocation* spawnlocation, bool respawn
|
||||
MSpawnLocationList.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = locations->begin(); itr != locations->end(); itr++) {
|
||||
if(spawn_location_list.count(itr->second) > 0){
|
||||
spawn = ProcessSpawnLocation(spawn_location_list[itr->second], respawn);
|
||||
spawn = ProcessSpawnLocation(spawn_location_list[itr->second], nullptr, nullptr, nullptr, nullptr, nullptr, respawn);
|
||||
if(!leader && spawn)
|
||||
leader = spawn;
|
||||
if(leader)
|
||||
@ -2400,7 +2445,7 @@ int32 ZoneServer::CalculateSpawnGroup(SpawnLocation* spawnlocation, bool respawn
|
||||
return group;
|
||||
}
|
||||
|
||||
void ZoneServer::ProcessSpawnLocation(int32 location_id, bool respawn)
|
||||
void ZoneServer::ProcessSpawnLocation(int32 location_id, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn)
|
||||
{
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
||||
|
||||
@ -2442,12 +2487,12 @@ void ZoneServer::ProcessSpawnLocation(int32 location_id, bool respawn)
|
||||
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
||||
|
||||
ProcessSpawnLocation(spawn_location_list[location_id], respawn);
|
||||
ProcessSpawnLocation(spawn_location_list[location_id], instNPCs, instGroundSpawns, instObjSpawns, instWidgetSpawns, instSignSpawns, respawn);
|
||||
}
|
||||
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, bool respawn)
|
||||
Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn)
|
||||
{
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
||||
|
||||
@ -2461,7 +2506,23 @@ Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, bool respa
|
||||
{
|
||||
if(spawnlocation->entities[i]->spawn_percentage == 0)
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
int32 spawnTime = 0;
|
||||
|
||||
if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
|
||||
if(spawnTime == 0) { // don't respawn
|
||||
return nullptr;
|
||||
}
|
||||
else if(spawnTime > 1) { // if not 1, respawn after time
|
||||
AddRespawn(spawnlocation->entities[i]->spawn_location_id, spawnTime);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (spawnlocation->conditional > 0) {
|
||||
if ((spawnlocation->conditional & SPAWN_CONDITIONAL_DAY) == SPAWN_CONDITIONAL_DAY && isDusk)
|
||||
continue;
|
||||
@ -2635,6 +2696,13 @@ void ZoneServer::ProcessSpawnLocations()
|
||||
instWidgetSpawns = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_WIDGET );
|
||||
instSignSpawns = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_SIGN );
|
||||
}
|
||||
else {
|
||||
instNPCs = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_NPC );
|
||||
instGroundSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_GROUNDSPAWN );
|
||||
instObjSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_OBJECT );
|
||||
instWidgetSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_WIDGET );
|
||||
instSignSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_SIGN );
|
||||
}
|
||||
|
||||
map<int32, bool> processed_spawn_locations;
|
||||
map<int32, SpawnLocation*>::iterator itr;
|
||||
@ -2683,7 +2751,7 @@ void ZoneServer::ProcessSpawnLocations()
|
||||
else
|
||||
{
|
||||
//LogWrite(SPAWN__DEBUG, 5, "Spawn", "ProcessSpawnLocation (%u)...", itr->second->placement_id);
|
||||
ProcessSpawnLocation(itr->second);
|
||||
ProcessSpawnLocation(itr->second,instNPCs,instGroundSpawns,instObjSpawns,instWidgetSpawns,instSignSpawns);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3288,13 +3356,14 @@ void ZoneServer::CheckTransporters(Client* client) {
|
||||
client->QueuePacket(packet);
|
||||
}
|
||||
else{
|
||||
ZoneServer* new_zone = zone_list.Get(loc->destination_zone_id);
|
||||
if(new_zone){
|
||||
ZoneChangeDetails zone_details;
|
||||
bool foundZone = zone_list.GetZone(&zone_details, loc->destination_zone_id);
|
||||
if(foundZone){
|
||||
client->GetPlayer()->SetX(loc->destination_x);
|
||||
client->GetPlayer()->SetY(loc->destination_y);
|
||||
client->GetPlayer()->SetZ(loc->destination_z);
|
||||
client->GetPlayer()->SetHeading(loc->destination_heading);
|
||||
client->Zone(new_zone, false);
|
||||
client->Zone(&zone_details, (ZoneServer*)zone_details.zonePtr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -3543,8 +3612,10 @@ void ZoneServer::RemoveClient(Client* client)
|
||||
map<int32, int32>::iterator itr;
|
||||
for (itr = client->GetPlayer()->SpawnedBots.begin(); itr != client->GetPlayer()->SpawnedBots.end(); itr++) {
|
||||
Spawn* spawn = GetSpawnByID(itr->second);
|
||||
if (spawn)
|
||||
if (spawn && spawn->IsBot()) {
|
||||
((Entity*)spawn)->SetOwner(nullptr);
|
||||
((Bot*)spawn)->Camp();
|
||||
}
|
||||
}
|
||||
|
||||
if(dismissPets) {
|
||||
@ -3598,7 +3669,7 @@ void ZoneServer::ClientProcess(bool ignore_shutdown_timer)
|
||||
{
|
||||
MIncomingClients.readlock(__FUNCTION__, __LINE__);
|
||||
bool shutdownDelayCheck = shutdownDelayTimer.Check();
|
||||
if((!AlwaysLoaded() && !shutdownTimer.Enabled()) || shutdownDelayCheck)
|
||||
if((!AlwaysLoaded() && !shutdownTimer.Enabled()) || (!AlwaysLoaded() && shutdownDelayCheck))
|
||||
{
|
||||
if(incoming_clients && !shutdownDelayTimer.Enabled()) {
|
||||
LogWrite(ZONE__INFO, 0, "Zone", "Incoming clients (%u) expected for %s, delaying shutdown timer...", incoming_clients, GetZoneName());
|
||||
@ -3620,6 +3691,9 @@ void ZoneServer::ClientProcess(bool ignore_shutdown_timer)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(AlwaysLoaded() && shutdownTimer.Enabled()) {
|
||||
shutdownTimer.Disable();
|
||||
}
|
||||
MIncomingClients.releasereadlock(__FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
@ -3751,6 +3825,36 @@ void ZoneServer::HandleChatMessage(Client* client, Spawn* from, const char* to,
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneServer::HandleChatMessage(Client* client, std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language) {
|
||||
if (!client->GetPlayer()->IsIgnored(fromName.c_str())) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_HearChat", client->GetVersion());
|
||||
if (packet) {
|
||||
packet->setMediumStringByName("from", fromName.c_str());
|
||||
|
||||
int8 clientchannel = client->GetMessageChannelColor(channel);
|
||||
packet->setDataByName("channel", client->GetMessageChannelColor(channel));
|
||||
packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
|
||||
packet->setDataByName("to_spawn_id", 0xFFFFFFFF);
|
||||
packet->setMediumStringByName("message", message);
|
||||
packet->setDataByName("language", language);
|
||||
|
||||
bool hasLanguage = client->GetPlayer()->HasLanguage(language);
|
||||
if (language > 0 && !hasLanguage)
|
||||
packet->setDataByName("understood", 0);
|
||||
else
|
||||
packet->setDataByName("understood", 1);
|
||||
|
||||
packet->setDataByName("show_bubble", 0);
|
||||
if (channel_name)
|
||||
packet->setMediumStringByName("channel_name", channel_name);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
//DumpPacket(outapp);
|
||||
client->QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneServer::HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language){
|
||||
vector<Client*>::iterator client_itr;
|
||||
Client* client = 0;
|
||||
@ -3764,6 +3868,19 @@ void ZoneServer::HandleChatMessage(Spawn* from, const char* to, int16 channel, c
|
||||
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void ZoneServer::HandleChatMessage(std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language){
|
||||
vector<Client*>::iterator client_itr;
|
||||
Client* client = 0;
|
||||
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
||||
client = *client_itr;
|
||||
if(client && client->IsConnected())
|
||||
HandleChatMessage(client, fromName, to, channel, message, distance, channel_name, language);
|
||||
}
|
||||
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void ZoneServer::HandleBroadcast(const char* message) {
|
||||
vector<Client*>::iterator client_itr;
|
||||
Client* client = 0;
|
||||
@ -4582,33 +4699,9 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool
|
||||
if(erase_from_spawn_list)
|
||||
AddPendingSpawnRemove(spawn->GetID());
|
||||
|
||||
if(respawn && !spawn->IsPlayer() && spawn->GetRespawnTime() > 0 && spawn->GetSpawnLocationID() > 0)
|
||||
{
|
||||
if(respawn && !spawn->IsPlayer() && spawn->GetSpawnLocationID() > 0) {
|
||||
LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn for '%s'.", spawn->GetName());
|
||||
|
||||
// handle instance spawn db info
|
||||
// we don't care if a NPC or a client kills the spawn, we could have events that cause NPCs to kill NPCs.
|
||||
if(spawn->GetZone()->GetInstanceID() > 0 && spawn->GetSpawnLocationID() > 0)
|
||||
{
|
||||
LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn in an Instance.");
|
||||
// use respawn time to either insert/update entry (likely insert in this situation)
|
||||
if ( spawn->IsNPC() )
|
||||
{
|
||||
database.CreateInstanceSpawnRemoved(spawn->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC,
|
||||
spawn->GetRespawnTime(),spawn->GetZone()->GetInstanceID());
|
||||
}
|
||||
else if ( spawn->IsObject ( ) )
|
||||
{
|
||||
database.CreateInstanceSpawnRemoved(spawn->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT,
|
||||
spawn->GetRespawnTime(),spawn->GetZone()->GetInstanceID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 spawnLocationID = spawn->GetSpawnLocationID();
|
||||
int32 spawnRespawnTime = spawn->GetRespawnTime();
|
||||
respawn_timers.Put(spawnLocationID, Timer::GetCurrentTime2() + spawnRespawnTime * 1000);
|
||||
}
|
||||
AddRespawn(spawn);
|
||||
}
|
||||
|
||||
RemoveSpawnFromGrid(spawn, spawn->GetLocation());
|
||||
@ -5145,6 +5238,12 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
|
||||
else if ( dead->IsObject ( ) )
|
||||
database.CreateInstanceSpawnRemoved(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT, dead->GetRespawnTime(),dead->GetZone()->GetInstanceID());
|
||||
}
|
||||
else if(!groupMemberAlive && dead->GetSpawnLocationID() > 0) {
|
||||
if(dead->IsNPC())
|
||||
database.CreatePersistedRespawn(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC,dead->GetRespawnTime(),GetZoneID());
|
||||
else if(dead->IsObject())
|
||||
database.CreatePersistedRespawn(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT,dead->GetRespawnTime(),GetZoneID());
|
||||
}
|
||||
|
||||
// Call the spawn scripts death() function
|
||||
CallSpawnScript(dead, SPAWN_SCRIPT_DEATH, killer);
|
||||
@ -8662,7 +8761,7 @@ void ZoneServer::ProcessSpawnConditional(int8 condition) {
|
||||
SpawnLocation* loc = itr2->second;
|
||||
if (loc && loc->conditional > 0 && ((loc->conditional & condition) == condition))
|
||||
if (GetSpawnByLocationID(loc->placement_id) == NULL)
|
||||
ProcessSpawnLocation(loc);
|
||||
ProcessSpawnLocation(loc, nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
@ -9127,3 +9226,13 @@ void ZoneServer::AddIgnoredWidget(int32 id) {
|
||||
ignored_widgets.insert(make_pair(id,true));
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneServer::AddRespawn(Spawn* spawn) {
|
||||
AddRespawn(spawn->GetSpawnLocationID(), spawn->GetRespawnTime());
|
||||
}
|
||||
|
||||
void ZoneServer::AddRespawn(int32 locationID, int32 respawnTime) {
|
||||
if(locationID > 0 && respawnTime > 0) {
|
||||
respawn_timers.Put(locationID, Timer::GetCurrentTime2() + respawnTime * 1000);
|
||||
}
|
||||
}
|
@ -298,6 +298,9 @@ public:
|
||||
void SimpleMessage(int8 type, const char* message, Spawn* from, float distance, bool send_to_sender = true);
|
||||
void HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance = 0, const char* channel_name = 0, bool show_bubble = true, int32 language = 0);
|
||||
void HandleChatMessage(Client* client, Spawn* from, const char* to, int16 channel, const char* message, float distance = 0, const char* channel_name = 0, bool show_bubble = true, int32 language = 0);
|
||||
void HandleChatMessage(Client* client, std::string fromName, const char* to, int16 channel, const char* message, float distance = 0, const char* channel_name = 0, int32 language = 0);
|
||||
void HandleChatMessage(std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language);
|
||||
|
||||
void HandleBroadcast(const char* message);
|
||||
void HandleAnnouncement(const char* message);
|
||||
|
||||
@ -517,7 +520,7 @@ public:
|
||||
void SetZoneDescription(char* desc) {
|
||||
if( strlen(desc) >= sizeof zone_description )
|
||||
return;
|
||||
strcpy(zone_description, desc);
|
||||
strncpy(zone_description, desc, 255);
|
||||
}
|
||||
|
||||
void SetUnderWorld(float under){ underworld = under; }
|
||||
@ -543,7 +546,7 @@ public:
|
||||
void SetZoneLockState(bool lock_state) { locked = lock_state; } // JA: /zone lock|unlock
|
||||
int32 GetInstanceID() { return instanceID; }
|
||||
bool IsInstanceZone() { return isInstance; }
|
||||
|
||||
void SetInstanceID(int32 newInstanceID) { instanceID = newInstanceID; }
|
||||
void SetShutdownTimer(int val){
|
||||
shutdownTimer.SetTimer(val*1000);
|
||||
}
|
||||
@ -725,8 +728,10 @@ public:
|
||||
int32 GetSpawnCountInGrid(int32 grid_id);
|
||||
void SendClientSpawnListInGrid(Client* client, int32 grid_id);
|
||||
|
||||
void AddIgnoredWidget(int32 id);
|
||||
void AddIgnoredWidget(int32 id);
|
||||
|
||||
void AddRespawn(Spawn* spawn);
|
||||
void AddRespawn(int32 locationID, int32 respawnTime);
|
||||
private:
|
||||
#ifndef WIN32
|
||||
pthread_t ZoneThread;
|
||||
@ -752,9 +757,10 @@ private:
|
||||
vector<int32>* GetAssociatedLocations(set<int32>* groups); // never used outside zone server
|
||||
set<int32>* GetAssociatedGroups(int32 group_id); // never used outside zone server
|
||||
list<int32>* GetSpawnGroupsByLocation(int32 location_id); // never used outside zone server
|
||||
void ProcessSpawnLocation(int32 location_id, bool respawn = false); // never used outside zone server
|
||||
Spawn* ProcessSpawnLocation(SpawnLocation* spawnlocation, bool respawn = false); // never used outside zone server
|
||||
void ProcessSpawnLocation(int32 location_id, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn = false); // never used outside zone server
|
||||
Spawn* ProcessSpawnLocation(SpawnLocation* spawnlocation, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn = false); // never used outside zone server
|
||||
Spawn* ProcessInstanceSpawnLocation(SpawnLocation* spawnlocation, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn = false); // never used outside zone server
|
||||
void SendRaidSheetChanges(); // never used outside zone server
|
||||
void SendCharSheetChanges(); // never used outside zone server
|
||||
void SendCharSheetChanges(Client* client); // never used outside zone server
|
||||
void SaveClients(); // never used outside zone server
|
||||
@ -925,6 +931,7 @@ private:
|
||||
volatile bool LoadingData;
|
||||
std::atomic<bool> reloading_spellprocess;
|
||||
std::atomic<bool> zoneShuttingDown;
|
||||
std::atomic<bool> is_initialized;
|
||||
bool cityzone;
|
||||
bool always_loaded;
|
||||
bool isInstance;
|
||||
|
@ -513,6 +513,15 @@ LOG_TYPE(REGION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED,
|
||||
LOG_TYPE(REGION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P")
|
||||
LOG_TYPE(REGION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T")
|
||||
|
||||
// Logging Peering code
|
||||
LOG_CATEGORY(PEERING)
|
||||
LOG_TYPE(PEERING, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I")
|
||||
LOG_TYPE(PEERING, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W")
|
||||
LOG_TYPE(PEERING, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E")
|
||||
LOG_TYPE(PEERING, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D")
|
||||
LOG_TYPE(PEERING, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P")
|
||||
LOG_TYPE(PEERING, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T")
|
||||
|
||||
#undef LOG_TYPE
|
||||
#undef LOG_CATEGORY
|
||||
#undef ENABLED
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <netinet/in.h>
|
||||
@ -324,27 +325,19 @@ int64 hextoi64(char* num) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
float MakeRandomFloat(float low, float high)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
thread_local bool seeded = false;
|
||||
#else
|
||||
static bool seeded = false;
|
||||
#endif
|
||||
|
||||
float MakeRandomFloat(float low, float high) {
|
||||
// Handle edge case where range is zero or inverted
|
||||
float diff = high - low;
|
||||
|
||||
if(!diff) return low;
|
||||
if(diff < 0)
|
||||
diff = 0 - diff;
|
||||
|
||||
if (low == high) return low;
|
||||
if (low > high) std::swap(low, high);
|
||||
|
||||
if(!seeded)
|
||||
{
|
||||
srand(time(0) * (time(0) % (int)diff));
|
||||
seeded = true;
|
||||
}
|
||||
|
||||
return (rand() / (float)RAND_MAX * diff + (low > high ? high : low));
|
||||
// Use a thread-local random generator for thread safety
|
||||
thread_local std::mt19937 generator(std::random_device{}()); // Seed once per thread
|
||||
std::uniform_real_distribution<float> distribution(low, high);
|
||||
|
||||
return distribution(generator);
|
||||
}
|
||||
|
||||
int32 GenerateEQ2Color(float* r, float* g, float* b){
|
||||
|
@ -38,11 +38,11 @@
|
||||
#endif
|
||||
|
||||
#if defined(LOGIN)
|
||||
#define CURRENT_VERSION "0.9.7-thetascorpii-DR2"
|
||||
#define CURRENT_VERSION "0.9.8-thetascorpii-DR1"
|
||||
#elif defined(WORLD)
|
||||
#define CURRENT_VERSION "0.9.7-thetascorpii-DR2"
|
||||
#define CURRENT_VERSION "0.9.8-thetascorpii-DR1"
|
||||
#else
|
||||
#define CURRENT_VERSION "0.9.7-thetascorpii-DR2"
|
||||
#define CURRENT_VERSION "0.9.8-thetascorpii-DR1"
|
||||
#endif
|
||||
|
||||
#define COMPILE_DATE __DATE__
|
||||
|
Loading…
x
Reference in New Issue
Block a user