Returning non-zero from EVENT_TASK_COMPLETE will prevent task rewards
and completion emote. This is necessary for a DoN mission which scripts
a mission failure but still gives the lockout.
* Combine task_activity item and npc fields
This will make tooling easier.
While denormalizing goallists may not be ideal, it decouples tasks from
rewards which share the table and removes a redundant column in favor
of a using the delimited string which better matches live packet data.
* [Tasks] Deprecate goallists table, migrate reward goal lists, simplify logic
* Update 2022_09_25_task_concat_matchlists.sql
* Update 2022_09_25_task_concat_matchlists.sql
* Tweaks
* Fix reward column name in conversion script
* Task reward stacking
* Update task_client_state.cpp
* Implement stack counts
* Fix reward item instance memory leak
* Validate reward item instance
* Fix item reward message
* Fix findtask
Co-authored-by: Akkadius <akkadius1@gmail.com>
On live if a task update would affect multiple elements, only the first
one gets updated. This was added as part of the changes in 7482cfc0.
Setting `UpdateOneElementPerTask` to false will restore the old behavior
that increments all active elements matching the task update criteria.
Some live npcs ignore the request cooldown timer (tutorialb)
A separate function had to be used for perl because the apis use an
array instead of array reference which won't allow a bool overload
This also replaces the fixed array and count args with a vector
This allows removal of the task id limit (MAXTASKS)
There's some suspect places task data isn't verified but left unchanged
If memory use becomes too high once more live data is added tasks can be
stored in shared memory instead
Erasing from the map inside the range loop invalidated the iterator used
internally by the loop. This caused ucs access violations under msvc
debug builds when a discord logging category was enabled.
The task goal system made implementing tasks a little confusing since
the goal could be ambiguous depending on type. This also didn't support
filtering on multiple goals (e.g. looting items from matching npc names
inside an area). Deliver types could specify an npc id in `delivertonpc`
but the database may have multiple npcs with the same name or a task
might want to match partial npc names.
This replaces goalids with explicit fields for npcs, items, proximity
areas, and touch switch ids. These changes make managing task data
easier without needing to update multiple tables and allows filtering
task updates by multiple criteria. To mitigate any performance impact
from merging task proximities, only clients with explore tasks in the
current zone are checked during client movement updates.
Items and npcs still support goallists but it would be possible to
denormalize entries into delimited strings to combine with the match
lists. This would also decouple task goals from reward lists.
The client task update functions were refactored to run through a single
filtering function which significantly reduces duplicated code from the
legacy task system. This will also make it easier to later implement
any unhandled types.
Since the new fields will handle filtering single entries and lists
based on having values set, `goalmethod` now only distinguishes quest
controlled from source controlled.
This is a breaking api change, `taskexploredarea` has been removed
since explore ids no longer exist.
Some live tasks make new elements available without requiring all
currently active ones to be completed first.
This adds the `req_activity_id` field to task activities which will mark
an element active if its required activity id is completed. If a valid
value is set then it's used instead of checking the current step.
The `step` field may still be set on rows with a valid `req_activity_id`
to specify its logical step and prevent later steps from becoming active
until completed. It's only ignored when deciding if the current element
is active.
The legacy task logic for unlocking activities was completely refactored
for this. A common method has been added so both zone and world can make
use of it to determine which elements are currently active. The previous
step system should remain unchanged.
The world logic for locking shared tasks when an element became active
did not account for "sequential" mode (all steps 0), unordered steps, or
gaps in step numbers. This also resolves that issue.
This was causing #task reloadall to desync state for shared tasks.
Characters were removed from the shared task but remained in the
character_tasks table. This CancelTask is cosmetic only to reload
the client window.
This wasn't safe since the erase would invalidate iterators used
internally by the range loops.
Shared tasks with no members are now also cleaned up
Make GetMembers return reference instead of copy (this is used a lot)
Add rule for shared task terminate time for easier debugging
This allows shared tasks to create dz instances automatically through
the `dz_template_id` field instead of using quest scripts. Quest apis
were also added to create expeditions from template ids.
This changes task touch elements to use the dz_switch_id as the goal id
instead of checking if a player zoned. It will remove the need to script
door clicks or modify task element zone field since task packet data can
just be imported.
This adds the `dz_switch_id` field to doors and dynamic_zones. It will
allow for compasses to be automatically added to dz entrances and will
support moving clients to the dz on use without needing to script it.
These can be imported for switches/doors from live packet dumps.
Also removes compass packet encoders (same struct in all clients)
Functionally nothing should change in this patch. This refactors a lot
of formatting and code duplication in requests and member removals.
The long SharedTaskMessage namespace and its constants were contributing
to a lot of formatting that was difficult to read. These were shortened
since they're not all shared task specific anyway (though they should
probably just be moved to string_ids.h with other eqstrs now)
Shared task requests were refactored a little to remove an unnecessary
function that filled potential members. The separate functions used for
/taskquit and /taskremove were also combined into a single function to
remove a member while preserving live message differences.
* Add shared task element locking
This adds the `lock_activity_id` field to the tasks table which will
automatically lock a shared task when that element becomes active.
A method was added to world analogous to zone's UnlockActivities to
determine when an activity is active with respect to task steps.
Also adds quest apis to manually lock or unlock a client's shared task
* Add comment
This was implemented in a very odd way. The intensity is just a float.
This is a breaking api change but low-impact (intensity will be
different).
To convert old values to new values with same intensity:
0: 0.03125
1: 0.05625
2: 0.1
3: 0.175
4: 0.3
5: 0.5
6: 0.9
7: 1.6
8: 2.8
9: 4.8
10: 8.0
Live sends the task updated message for every solo quest and task
increment (not just goal completed).
Also changed color to what live uses now. There are some other color
changes with shared task messages but they correspond with colors
changed in client generated messages but probably not worth chasing
down and causing inconsistency.
* Add task reward points field
This replaces the separate DoN crystal reward fields with points and
point_type fields. This will make it easier to import data from
packet/client captures and possibly better support any future clients
or tasks that don't reward through the newer reward window.
* Fix manifest column check
Returning non-zero from EVENT_CLICK_DOOR will prevent the default
handler.
This should have been implemented when expeditions were put in to
allow scripts to prevent zoning out of vxed and tipt without flags.
Some upcoming changes may need this to allow scripts to prevent
automatic dz entry.
When a packet was over max_raw_size and zlib failed to compress the
first packet chunk then the final output would be 513 bytes which
exceeded m_max_packet_size of 512.
This occured because the first packet chunk used sublen without
adjusting for the new_length + 1 compression flag added in Compress().
When the packet failed to compress then it was already at its max.
After the first packet, chunk sizes are calculated using max_raw_size
which already accounted for the compress flag. (From #979)
This should fix#2325
If a message was longer than 50 characters with "00000" somewhere in the
message (such as messages with hex numbers) then the saylink injection
method was sending a blank message to the chat window.
This refactors the saylink injection method using a crude state machine
to build the output. It should function the same:
- Inner-most brackets generate saylinks when nested
- Saylinks are not generated in brackets that already have a saylink
- Existing saylinks are preserved
- Existing saylinks that contain text with brackets do not attempt to
generate saylinks
- Saylinks are not generated if brackets contains leading or trailing
spaces (e.g. [ spaces ])
If a member is offline (or possibly during a race while zoning?) when
the shared task is completed they will not receive the reward. On live
the character receives their reward (with an updated replay timer) if
they enter back into game while the shared task is still active. They
keep the original replay timer if the shared task is no longer active
and do not receive a reward.
This makes it so clients are issued rewards (and a task completed
event is dispatch) if the client's task state was out of sync with a
completed shared task. To prevent characters being rewarded more than
once in case of bad sync checks, a 'was_rewarded' field has been added
to the character_tasks table and updated when rewards are assigned.
This fixes a couple bugs so the character_activities table is correctly
updated with shared task states to better detect when out of sync:
- The character_activities table is now flagged to update after syncing
shared task states. This table was not being updated if a client was
offline or inaccessible for a shared task element update.
- The character_activities table is now updated when a task element is
completed. This was only being updated for activity increments and on
completing the entire task. SaveClientState is now called at the end
of ClientTaskState::IncrementDoneCount to cover all cases.
This also has a cosmetic change to show replay timers before rewards
like live, though this will not work for shared tasks until refactoring
world code
Shared task replay timers used to be based on the task start time
(reducing the final lockout timer by elapsed task time).
Live changed this in the 2022 April 20 patch:
"Replay timers will now apply their full duration upon completion of
their associated task/quest."
Solo tasks and quests already did this
This also fixes a small bug where request timers were being
deleted when replacing replay timers for shared tasks
EventItem was leaving the package table on the stack in successful
(non-encounter) calls and only popping the return. All events were
also leaking the full stack on lua_pcall errors. Lua mod callbacks
were popping error returns but leaking the table.
* Implement SetFace opcode
This implements the opcode that's used to update zone clients when a
client updates their face
* Use SetFace in #feature command
Add check for valid number
This adds the #feature eyes command which isn't in the illusion struct
CastRestrictedSpell is already defined in common/spdat
MSVC Debug builds caught this as an ODR violation, though Release
builds still allowed it (maybe because the methods were identical)
* Add player login reply struct
* Use player login reply struct for failed logins
* Use base message struct for login requests
* Refactor server list reply serialization
Use BaseMessage and BaseReplyMessage structs for server list
and add flags for server type and status
* Use reply message struct for login handshake
Remove client version checks, the packets are the same for titanium and rof2
* Use base headers for join server requests
* Log correct server list ip
* Add compressed flag to base message header
Document encrypt type flag more
This makes the float and integer fields in the doors table not nullable.
The only column this should affect is the buffer column which wasn't
being loaded in the old doors loading query. The other columns weren't
validated but they should still be made not nullable to avoid issues.
This will fix a crash in potimeb which is the only zone that had NULL
values in the buffer column with the current peq database. This column
can be removed in a future followup since it isn't being used anyway.
The include order here was causing a compile error when building with
perl 5.12 due to a bad interaction with the older fmt submodule version
being used
Add hash overload to perl CreateExpedition api
This adds an api to perl similar to the Lua api that accepts a reference
to a hash table with expedition creation info
Usage example:
my $expedition_info = {
expedition => { name => "Perl expedition", min_players => 2, max_players => 6 },
instance => { zone => "crushbone", version => 0, duration => 3600 },
compass => { zone => "gfaydark", x => 238, y => 987, z => -24.90 },
safereturn => { zone => "gfaydark", x => 245.84, y => 987.93, z => -27.6, h => 484.0 },
zonein => { x => 479.44, y => -500.18, z => 5.75, h => 421.8 }
};
$client->CreateExpedition($expedition_info);
Syntax for passing directly from a hash:
my %expedition_info = (...);
$client->CreateExpedition(\%expedition_info);
Add CreateTaskDynamicZone api to perl
Usage example:
sub EVENT_TASKACCEPTED {
if ($task_id == 4795) {
my %dz_hash = (
"instance", { zone=>"thundercrest", version => 11 },
"compass", { zone=>"broodlands", x=>1241.88, y=>511.147, z=>23.4192 },
"safereturn", { zone=>"broodlands", x=>1242.0, y=>526.0, z=>27.0, h=>0.0 }
);
$client->CreateTaskDynamicZone($task_id, \%dz_hash)
}
}
Shared tasks start replay timers based on accept time but solo tasks
should start from completion time. Solo tasks on live that have a
non-unlimited duration may require further investigation
This fixes an edge case where a player could be made leader when added
to an expedition that only had a single member previously.
If a leader in a two-member expedition quit (forcing a leader change)
and the new leader went offline while throttled, a leader change flag
would be set until a non-leader was available. The first added member
would then be made the new leader.
This could also potentially occur on world startup due to the initial
throttle timer state but member statuses aren't processed there yet
This moves members from expeditions so other systems can use them
Replace expedition_members table with dynamic_zone_members
Move 'EnableInDynamicZoneStatus' rule to DynamicZone namespace
Modify #dz list to show dz members (not instance players) and type name
Move various queries to repository methods
This starts some changes that move storage of things from expeditions to
dynamic zone class so other systems can possibly use them. This will also
make it easier to move window packet creation methods to DynamicZone.
For now these will remain on the expeditions table in the database. This
can be re-evaluated once other components are moved and seeing how other
systems may want to handle their player requirements.
World now caches and tracks member statuses so it can send them to zones
that request them on startup. Prior to this the cle would be searched in
world for every zone startup caching request, now it's only searched once
when a new expedition is created.
Bulk loading statuses removed since it would only be needed on world
startup now and likely have no clients in the client list anyway.
This also lets world choose non-linkdead members on expedition leader
changes and better detect when a leader change needs to occur
* Rename dz member id field
The name of the struct is enough to make this implicit
* Rename dz member enum and struct
Rename ExpeditionMember to DynamicZoneMember
Rename ExpeditionMemberStatus to DynamicZoneMemberStatus
* Rename dz window packet structs
This makes it more clear the window may be used by any dynamic zone
system not just expeditions (live missions fill the window when
player doesn't have an active expedition).
* Rename dz window packet fields
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.
This creates an abstract class in common so zone and world can share
most of the implementation. World now has access to the same dz data and
api as zone.
Rename CharacterChange to AddRemoveCharacter for clarity
Rename GetRemainingDuration to GetDurationRemaining for consistency
Move dynamic zone queries to custom repository methods
Move early empty shutdown and process rate rules to DynamicZone scope
This decouples the expired status check from expeditions into an
internal dz method that can be called by its owning system
Use internal dz messages to process duration and location changes
Add world DynamicZone class (later this will inherit from a base)
Add FindDynamicZoneByID to get dz from zone and world system caches
Move ExpeditionLockoutTimer to common
This simplifies expedition request conflict checks and uses repository
for the queries instead of processing the query result directly.
This fixes unnecessary leader processing on mass dzquits
Also marks leader dirty for empty expeditions in case something goes
wrong (shouldn't be possible after 62e480fe)
* Rename dynamic zone source files
* Store description and leader name on dz
Removes the DynamicZoneInfo struct used for switch list window. This
data can be stored on DynamicZone and kept updated by its owning system
* Separate create compass packet method
Cleanup MarkSingleCompassLoc
The NPC pointer held by Spawn2 wasn't reset if the npc was depopped
without a respawn timer by #depop commands or depop(false) quest apis.
If the NPC was part of a spawn condition then the condition would try
to dereference that pointer (which gets deleted) on condition change.
Fixes regression from b08dc02a (PR #1051)
The normal door list sent on zone entry caused unopenable double doors
on clients if an api respawned them while the client was zoning. This
waits until the client finishes zoning and has received the initial
door list before sending any despawn/respawn packets.
Moves corpses to graveyard when an expired instance shuts down.
Zones without a graveyard move them to non-instance version instead.
Fixes player corpses being left inside instances that expire
before graveyards process or in instances without a graveyard
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 32c69d235d
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