Add common expedition base class
Use repository for zone and world expedition caching
World now stores members and leader as Member objects instead of ids
This improves readability of the caching methods and lets world cache
expedition dzs and members like zone. World also now caches expeditions
as unique_ptr which will be necessary for future dz callback lambdas
that capture 'this' so addresses don't change on cache vector resizes.
Move ExpeditionLockoutTimer to common
This simplifies expedition request conflict checks and uses repository
for the queries instead of processing the query result directly.
This is an experimental change which allows members to log in at the
safe return if they were offline when the expedition was deleted.
Prior to this they would log in at bind instead
Partially reverts commit 32c69d235d7fc8b816fa598b499be47d451ddba5
Removed expedition members are no longer hard deleted from db
This removes the is_pending column from character lockouts table
Synchronizing character lockout timers with the expedition's when zoning
into the dynamic zone simplifies adding missing lockouts to new members.
This also matches live behavior that replaces any character lockout
timers from another expedition with ones from the current expedition
This should eliminate race conditions caused by zones trying to set
a leader when members in different zones quit at the same time
Zone still detects when leader goes offline to trigger a change
since it's easier than having world process expedition member status
updates and perform expedition lookups
This exposes dynamic zone ids for any future changes and will make it
easier to preserve historic dz and expedition data. This also cleans up
some dynamic zone creation for expedition requests
When purging instances the expedition table is no longer updated
since dynamic zone ids are not re-used like instance ids are
Update #dz list commands to show dz id
Add GetDynamicZoneID and get_expedition_by_dz_id quest apis
This more accurately matches live for the new behavior introduced
in the September 16, 2020 patch. All members of a raid/group are
still checked for conflicts and the truncation is only allowed if
there are none.
It might make sense to add a rule for this since ignoring members
that would exceed the expedition max from the start makes it more
convenient to create expeditions. Members that wouldn't be added
anyway don't really need their conflicts checked.
Also supports reducing lockout duration
Add Expedition::AddLockoutDuration
Add Client::AddExpeditionLockoutDuration
Some expeditions require adding to existing lockout durations
during progression. These add the specified seconds to individual
member lockout timers instead of setting a static duration based on
internal expedition lockout like UpdateLockoutDuration.
Add SetLootEventByNPCTypeID and SetLootEventBySpawnID quest apis
These associate events with npcs or entities inside the dz to prevent
them from being looted by characters that didn't receive the event
lockout from the current expedition.
This fixes an exploit that allowed a player that already had a lockout
from another expedition being added to loot after the event is complete
Instead of allowing all previous members to bypass a replay timer
conflict, only allow if expedition uuid of the lockout matches
This fixes an exploit for expeditions that add delayed replay timers.
Members could be part of an expedition on creation and then quit to form
another expedition. They could then always be re-invited to the original
expedition even with a conflicting replay timer lockout.
Breaking change to the current API
has_replay_timer column removed from expedition_details table
This argument is unnecessary and just creates confusion. Expedition
replay timers use a hardcoded name precisely for this purpose and
those lockouts are already being checked on creation requests.
Delete pending lockouts of members on expedition creation
Delete pending lockouts when all members removed from expedition
This fixes an edge case where members could incorrectly be assigned
pending lockouts that were never cleared from the database (from a
server crash or other situation) after entering another dz.
Small cleanup of logic and unused variables
Rename LoadValidationData to LoadMembersForCreateRequest
Remove unnecessary early string building for members query
Remove unnecessary lockout expired check for leader messages
This optimizes caching all expeditions by loading dynamic zone data and
expedition members in bulk instead of for each expedition separately.
This reduces the number of queries from 1+2n to 3 total.
Expedition members are now joined in the initial query since empty
expeditions aren't cached anyway. Optional internal lockouts for all
cached expeditions are loaded in a single bulk query afterwards.
Dynamic Zone data is also loaded as a single bulk query afterwards to
simplify processing and keep dz database logic separated. It might be
worth investigating if joining dz data in the initial expeditions load
query is worth refactoring for.
Since world now tracks empty expeditions it can determine when to
shutdown dynamic zone instances when the rule is enabled rather than
letting zones do it.
Zones are no longer able to delete expeditions. World now tracks empty
expeditions in cache and only deletes them when it detects an
expedition's dynamic zone instance has no more clients inside.
This fixes an exploit where lockouts couldn't be applied to expeditions
after all members were removed because zones were deleting the expedition
immediately. Clients still inside the dz were able to complete events
before being kicked from the instance while not having an expedition.
Expeditions are no longer purged from database in the world purge
instance timer to avoid a possible race with this new system
Not all expeditions with a replay timer lockout add it to newly
added members automatically
This adds the Expedition::SetReplayLockoutOnMemberJoin(bool) method
to the quest api so it can be disabled
Checking the cache on member removal here isn't reliable due to race
with cross zone message
If a zone removes a member at the same time as another zone, neither zone
can know if the expedition will be empty via cache unless it processes the
world message from the other zone's member removal first.
Add DynamicZone sql table schema
Add DynamicZones logging category
Modify CreateExpedition to take DynamicZone and ExpeditionRequest objects
Implement DynamicZone compass, safereturn, and zone-in coordinates.
Implement live-like DynamicZone instance kick timer for removed members
Implement updating multiple client compasses (supports existing quest compass)
fix: Send client compass update after entering zones to clear existing compass
Implement Client::MovePCDynamicZone to invoke DynamicZoneSwitchListWnd
when entering a zone where client has multiple dynamic zones assigned
Implement OP_DzChooseZoneReply handling
Add Lua api methods for expedition's associated dynamic zone
Add #dz list gm command to list current DynamicZone instances from database
Add Expeditions logging category
Add handlers for all Dynamic Zone/Expedition related opcodes
Add FormatName string_util function to format character names
Add Zone::IsZone helper method
Add cross zone MessageString support with variable parameters
Add static Client method helpers for cross zone messaging
Add #dz gm command to debug expedition cache for current zone